tegaki 0.17.1 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/FONTS-LICENSE.md +6 -0
  3. package/README.md +1 -0
  4. package/dist/core/index.d.mts +3 -3
  5. package/dist/core/index.mjs +1 -1
  6. package/dist/{core-Cbi21OMg.mjs → core-B87FE4Wo.mjs} +147 -12
  7. package/dist/core-B87FE4Wo.mjs.map +1 -0
  8. package/dist/fonts/amiri/bundle.mjs.map +1 -1
  9. package/dist/fonts/caveat/bundle.mjs.map +1 -1
  10. package/dist/fonts/italianno/bundle.mjs.map +1 -1
  11. package/dist/fonts/klee-one/bundle.mjs.map +1 -1
  12. package/dist/fonts/nanum-pen-script/bundle.d.mts +7144 -0
  13. package/dist/fonts/nanum-pen-script/bundle.mjs +161041 -0
  14. package/dist/fonts/nanum-pen-script/bundle.mjs.map +1 -0
  15. package/dist/fonts/nanum-pen-script/nanum-pen-script-38efadb5.ttf +0 -0
  16. package/dist/fonts/nanum-pen-script/nanum-pen-script.ttf +0 -0
  17. package/dist/fonts/parisienne/bundle.mjs.map +1 -1
  18. package/dist/fonts/suez-one/bundle.mjs.map +1 -1
  19. package/dist/fonts/tangerine/bundle.mjs.map +1 -1
  20. package/dist/fonts/tillana/bundle.mjs.map +1 -1
  21. package/dist/{index-BcyoxWKO.d.mts → index-Bbbx_Ck-.d.mts} +4 -5
  22. package/dist/{index-BRsSO7q7.d.mts → index-H08fD9_Z.d.mts} +49 -4
  23. package/dist/index.d.mts +4 -4
  24. package/dist/index.mjs +2 -2
  25. package/dist/react/index.d.mts +4 -4
  26. package/dist/react/index.mjs +2 -2
  27. package/dist/{react-Aw2TTpfu.mjs → react-cKaviQiL.mjs} +3 -2
  28. package/dist/{react-Aw2TTpfu.mjs.map → react-cKaviQiL.mjs.map} +1 -1
  29. package/dist/shaper-harfbuzz/index.d.mts +1 -1
  30. package/dist/shaper-harfbuzz/index.mjs +26 -35
  31. package/dist/shaper-harfbuzz/index.mjs.map +1 -1
  32. package/dist/{shaper-registry-DVS5R37Q.d.mts → shaper-registry-DmlJGnE3.d.mts} +2 -2
  33. package/dist/wc/index.d.mts +3 -3
  34. package/dist/wc/index.mjs +1 -1
  35. package/dist/wc/index.mjs.map +1 -1
  36. package/fonts/nanum-pen-script/bundle.ts +20 -0
  37. package/fonts/nanum-pen-script/glyphData.json +1 -0
  38. package/fonts/nanum-pen-script/nanum-pen-script-38efadb5.ttf +0 -0
  39. package/fonts/nanum-pen-script/nanum-pen-script.ttf +0 -0
  40. package/package.json +26 -18
  41. package/src/astro/TegakiRenderer.astro +31 -5
  42. package/src/core/bundle-registry.ts +3 -0
  43. package/src/core/engine.ts +1 -0
  44. package/src/core/index.ts +7 -1
  45. package/src/env.d.ts +2 -0
  46. package/src/lib/drawGlyph.ts +12 -2
  47. package/src/lib/font.ts +1 -1
  48. package/src/lib/timeline.test.ts +173 -0
  49. package/src/lib/timeline.ts +179 -10
  50. package/src/nuxt/module.ts +0 -3
  51. package/src/shaper-harfbuzz/index.ts +24 -42
  52. package/src/types.ts +1 -1
  53. package/src/vue/TegakiRenderer.vue +2 -2
  54. package/src/vue/index.ts +1 -0
  55. package/dist/core-Cbi21OMg.mjs.map +0 -1
  56. package/dist/solid/index.d.mts +0 -20
  57. package/dist/solid/index.mjs +0 -91
  58. package/dist/solid/index.mjs.map +0 -1
  59. package/src/lib/harfbuzzjs.d.ts +0 -71
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core-B87FE4Wo.mjs","names":[],"sources":["../src/lib/effects.ts","../src/lib/catmullRom.ts","../src/lib/strokeCache.ts","../src/lib/utils.ts","../src/lib/drawGlyph.ts","../src/lib/features.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/shaper-registry.ts","../src/core/engine.ts"],"sourcesContent":["import type { TegakiEffectConfigs, TegakiEffectName } from '../types.ts';\nimport type { LayoutBBox, TextLayout } from './textLayout.ts';\n\nexport interface ResolvedEffect<K extends TegakiEffectName = TegakiEffectName> {\n effect: K;\n order: number;\n config: TegakiEffectConfigs[K];\n}\n\n/**\n * Context passed to effect render hooks. Covers everything a hook might need\n * to prepare or finalize state that spans the whole text layout (as opposed\n * to the per-glyph state owned by `drawGlyph`).\n *\n * The `ctx` has already been translated so its origin is the top-left of the\n * content box (i.e. `(0, 0)` is the top-left of `bbox`). Hooks that mutate ctx\n * state (transforms, strokeStyle, compositing, filters) are expected to\n * save/restore as needed so they don't leak into adjacent effects or the\n * glyph loop unintentionally.\n */\nexport interface RenderStageContext {\n ctx: CanvasRenderingContext2D;\n layout: TextLayout;\n fontSize: number;\n lineHeight: number;\n unitsPerEm: number;\n ascender: number;\n descender: number;\n /** Text bounding box in the current ctx coordinate space (CSS pixels). */\n bbox: LayoutBBox;\n /** The base color string resolved from `getComputedStyle().color`. */\n baseColor: string;\n /** Engine seed, offset by whatever the caller chose (currently the engine root seed). */\n seed: number;\n /**\n * Stroke paint override for the whole glyph loop. A `beforeRender` hook sets\n * this to e.g. a `CanvasGradient` when the effect wants every stroke to share\n * a single canvas-space paint. The engine reads it after all hooks have run\n * and threads it through to `drawGlyph`. `shadowColor` (used by `glow`) still\n * reads from `baseColor` since Canvas shadows don't accept gradients.\n */\n strokeStyle?: string | CanvasGradient | CanvasPattern;\n}\n\n/**\n * Metadata describing how an effect participates in rendering. The per-glyph\n * branch of an effect still lives inside `drawGlyph`; these hooks exist for\n * effects whose behavior spans the full layout (e.g. a gradient whose color\n * stops map to canvas-space position rather than per-stroke progress).\n *\n * `beforeRender` hooks run in forward order (same order as `_resolvedEffects`\n * after sorting by `order`). `afterRender` hooks run in reverse order so\n * save/restore-style pairs nest correctly.\n */\nexport interface EffectDefinition {\n beforeRender?(stage: RenderStageContext, config: any): void;\n afterRender?(stage: RenderStageContext, config: any): void;\n}\n\nconst defaultEffects: Record<string, any> = { pressureWidth: true };\n\nconst knownEffects: Record<string, EffectDefinition> = {\n glow: {},\n wobble: {},\n pressureWidth: {},\n taper: {},\n strokeGradient: {},\n globalGradient: {\n beforeRender(stage, config: { colors?: string[]; angle?: number }) {\n const colors = config.colors;\n if (!Array.isArray(colors) || colors.length === 0) return;\n const { ctx, bbox } = stage;\n\n // Project the bbox onto the direction vector to find gradient endpoints\n // that cover the full box at any angle. y grows downward on canvas, so\n // `angle=0` (dx=1, dy=0) is left→right and `angle=90` (dx=0, dy=1) is\n // top→bottom — positive angles rotate clockwise.\n const rad = ((config.angle ?? 0) * Math.PI) / 180;\n const dx = Math.cos(rad);\n const dy = Math.sin(rad);\n const cx = bbox.x + bbox.width / 2;\n const cy = bbox.y + bbox.height / 2;\n const halfW = bbox.width / 2;\n const halfH = bbox.height / 2;\n const proj = Math.abs(dx * halfW) + Math.abs(dy * halfH);\n\n const grad = ctx.createLinearGradient(cx - dx * proj, cy - dy * proj, cx + dx * proj, cy + dy * proj);\n if (colors.length === 1) {\n grad.addColorStop(0, colors[0]!);\n grad.addColorStop(1, colors[0]!);\n } else {\n for (let i = 0; i < colors.length; i++) {\n grad.addColorStop(i / (colors.length - 1), colors[i]!);\n }\n }\n stage.strokeStyle = grad;\n },\n },\n};\n\n/**\n * Normalizes an effects record into a sorted array of resolved effects.\n * Known keys infer the effect name; custom keys read it from the `effect` field.\n * Boolean `true` becomes an empty config. `false`/absent entries are skipped.\n */\nexport function resolveEffects(effects: Record<string, any> | undefined): ResolvedEffect[] {\n const merged = { ...defaultEffects, ...effects };\n\n const result: ResolvedEffect[] = [];\n\n for (const [key, value] of Object.entries(merged)) {\n if (value === false || value == null) continue;\n\n let effectName: TegakiEffectName;\n let config: Record<string, any>;\n let order: number;\n\n if (value === true) {\n effectName = (Object.hasOwn(knownEffects, key) ? key : undefined) as TegakiEffectName;\n if (!effectName) continue;\n config = {};\n order = 0;\n } else {\n if (value.enabled === false) continue;\n effectName = value.effect ?? (Object.hasOwn(knownEffects, key) ? key : undefined);\n if (!effectName) continue;\n const { effect: _, order: o, enabled: __, ...rest } = value;\n config = rest;\n order = o ?? 0;\n }\n\n result.push({ effect: effectName, order, config });\n }\n\n result.sort((a, b) => a.order - b.order);\n return result;\n}\n\n/** Check if a specific effect is active. */\nexport function findEffect<K extends TegakiEffectName>(effects: ResolvedEffect[], name: K): ResolvedEffect<K> | undefined {\n return effects.find((e) => e.effect === name) as ResolvedEffect<K> | undefined;\n}\n\n/** Get all instances of a specific effect (for duplicates). */\nexport function findEffects<K extends TegakiEffectName>(effects: ResolvedEffect[], name: K): ResolvedEffect<K>[] {\n return effects.filter((e) => e.effect === name) as ResolvedEffect<K>[];\n}\n\n/** Look up the render-hook metadata for an effect name. Unknown names return undefined. */\nexport function getEffectDefinition(name: string): EffectDefinition | undefined {\n return Object.hasOwn(knownEffects, name) ? knownEffects[name] : undefined;\n}\n\n/** True when any resolved effect defines a before/after render hook. */\nexport function hasRenderHooks(effects: ResolvedEffect[]): boolean {\n for (const e of effects) {\n const def = getEffectDefinition(e.effect);\n if (def?.beforeRender || def?.afterRender) return true;\n }\n return false;\n}\n","/**\n * Centripetal Catmull-Rom spline sampling.\n *\n * A point is `[x, y, w]` — the same shape the stroke data uses — but the\n * third channel is carried through the interpolation opaquely, so this\n * module is self-contained and doesn't know about strokes or glyph data.\n */\n\n/** A 3-channel point `[x, y, w]`. Typed as a plain array to match the loose shape used by the glyph data format. */\nexport type Point3 = readonly number[];\n\nexport interface SmoothSample {\n x: number;\n y: number;\n /** Third channel (stroke width in the caller's use) interpolated on the same parameterization as x/y. */\n width: number;\n}\n\n/**\n * Sample `count` points along a centripetal Catmull-Rom segment from `p1` to\n * `p2`, with neighbor control points `p0` and `p3`. Samples are emitted at\n * `u = k/count` for `k = 1..count`, so the last sample equals `p2`.\n *\n * Centripetal parameterization (α = 0.5) avoids the cusps and self-loops that\n * uniform/chordal Catmull-Rom can produce on sharp corners — relevant for the\n * baked RDP-simplified polylines the renderer consumes.\n *\n * For endpoint segments where a neighbor is missing, pass a phantom point\n * built with {@link reflect}. Zero-length chords are clamped to a tiny epsilon\n * so the knot parameterization stays non-degenerate.\n */\nexport function sampleCatmullRom(p0: Point3, p1: Point3, p2: Point3, p3: Point3, count: number): SmoothSample[] {\n const d01 = Math.max(dist(p0, p1), 1e-6);\n const d12 = Math.max(dist(p1, p2), 1e-6);\n const d23 = Math.max(dist(p2, p3), 1e-6);\n const t0 = 0;\n const t1 = t0 + Math.sqrt(d01);\n const t2 = t1 + Math.sqrt(d12);\n const t3 = t2 + Math.sqrt(d23);\n\n const out: SmoothSample[] = new Array(count);\n for (let k = 1; k <= count; k++) {\n const u = k / count;\n const t = t1 + u * (t2 - t1);\n out[k - 1] = evalBarryGoldman(p0, p1, p2, p3, t0, t1, t2, t3, t);\n }\n return out;\n}\n\n/**\n * Reflect `p` across `anchor` to produce a phantom neighbor for endpoint\n * segments. The result lies on the extension of (p, anchor) past `anchor` at\n * the same distance — equivalent to a zero-curvature extrapolation, which\n * gives a natural straight start/end tangent.\n */\nexport function reflect(anchor: Point3, p: Point3): Point3 {\n return [2 * anchor[0]! - p[0]!, 2 * anchor[1]! - p[1]!, anchor[2]!];\n}\n\nfunction dist(a: Point3, b: Point3): number {\n const dx = b[0]! - a[0]!;\n const dy = b[1]! - a[1]!;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\n// Barry-Goldman pyramid evaluation of a Catmull-Rom segment between p1 and p2\n// at knot value t ∈ [t1, t2]. Applies the same interpolation to x, y, and the\n// third channel so they stay smooth together.\nfunction evalBarryGoldman(\n p0: Point3,\n p1: Point3,\n p2: Point3,\n p3: Point3,\n t0: number,\n t1: number,\n t2: number,\n t3: number,\n t: number,\n): SmoothSample {\n const a1x = lerp(p0[0]!, p1[0]!, t0, t1, t);\n const a1y = lerp(p0[1]!, p1[1]!, t0, t1, t);\n const a1w = lerp(p0[2]!, p1[2]!, t0, t1, t);\n const a2x = lerp(p1[0]!, p2[0]!, t1, t2, t);\n const a2y = lerp(p1[1]!, p2[1]!, t1, t2, t);\n const a2w = lerp(p1[2]!, p2[2]!, t1, t2, t);\n const a3x = lerp(p2[0]!, p3[0]!, t2, t3, t);\n const a3y = lerp(p2[1]!, p3[1]!, t2, t3, t);\n const a3w = lerp(p2[2]!, p3[2]!, t2, t3, t);\n const b1x = lerp(a1x, a2x, t0, t2, t);\n const b1y = lerp(a1y, a2y, t0, t2, t);\n const b1w = lerp(a1w, a2w, t0, t2, t);\n const b2x = lerp(a2x, a3x, t1, t3, t);\n const b2y = lerp(a2y, a3y, t1, t3, t);\n const b2w = lerp(a2w, a3w, t1, t3, t);\n return {\n x: lerp(b1x, b2x, t1, t2, t),\n y: lerp(b1y, b2y, t1, t2, t),\n width: lerp(b1w, b2w, t1, t2, t),\n };\n}\n\nfunction lerp(a: number, b: number, ta: number, tb: number, t: number): number {\n const span = tb - ta;\n if (span === 0) return a;\n return a + (b - a) * ((t - ta) / span);\n}\n","import type { TegakiGlyphData } from '../types.ts';\nimport { reflect, sampleCatmullRom } from './catmullRom.ts';\n\ntype Stroke = TegakiGlyphData['s'][number];\n\n/**\n * A single vertex in a subdivided stroke polyline. Consecutive vertices form\n * a drawable sub-segment. Coordinates are in font units and pre-wobble: the\n * renderer applies wobble, scale, and translation at draw time so that one\n * cached subdivision can be reused across every instance of the same glyph.\n */\nexport interface SubVertex {\n /** X in font units, pre-wobble. */\n x: number;\n /** Y in font units, pre-wobble. */\n y: number;\n /** Stroke width at this vertex, in font units. */\n width: number;\n /** Accumulated length from the start of the stroke, in font units. */\n cumLen: number;\n /** Fractional original-point index (used to keep wobble phase continuous). */\n idx: number;\n}\n\nexport interface SubdividedStroke {\n /** Ordered vertices; vertices[i] → vertices[i+1] is a drawable sub-segment. */\n vertices: SubVertex[];\n /** Total polyline length in font units. */\n totalLen: number;\n /** Mean width across the original points in font units. */\n avgWidth: number;\n}\n\n/**\n * Subdivide a stroke so that no sub-segment exceeds `maxSegLen` font units.\n * Pass `Infinity` (or any non-finite value) to skip subdivision and return\n * the raw polyline.\n *\n * When `smoothing` is true, intermediate vertices are placed on a centripetal\n * Catmull-Rom spline through the original points (see `catmullRom.ts`) —\n * hiding the polyline facets that show up at large render sizes. The original\n * points remain on the curve, so endpoints and `cumLen`/`idx` semantics are\n * preserved. Has no effect when `maxSegLen` is non-finite (no subdivision).\n *\n * Output depends only on `(stroke.p, maxSegLen, smoothing)` — not on position,\n * seed, progress, or effect config — so it can be cached and shared across\n * every instance of the same glyph at the same font size.\n */\nexport function subdivideStroke(stroke: Stroke, maxSegLen: number, smoothing = false): SubdividedStroke {\n const pts = stroke.p;\n const n = pts.length;\n if (n === 0) return { vertices: [], totalLen: 0, avgWidth: 0 };\n\n const first = pts[0]!;\n const vertices: SubVertex[] = [{ x: first[0]!, y: first[1]!, width: first[2]!, cumLen: 0, idx: 0 }];\n\n let cumLen = 0;\n for (let j = 1; j < n; j++) {\n const prev = pts[j - 1]!;\n const cur = pts[j]!;\n const dx = cur[0]! - prev[0]!;\n const dy = cur[1]! - prev[1]!;\n const chordLen = Math.sqrt(dx * dx + dy * dy);\n const count = chordLen > 0 && Number.isFinite(maxSegLen) && maxSegLen > 0 ? Math.max(1, Math.ceil(chordLen / maxSegLen)) : 1;\n\n if (smoothing && count > 1) {\n const p0 = j >= 2 ? pts[j - 2]! : reflect(prev, cur);\n const p3 = j + 1 < n ? pts[j + 1]! : reflect(cur, prev);\n const samples = sampleCatmullRom(p0, prev, cur, p3, count);\n let px = prev[0]!;\n let py = prev[1]!;\n let segAccum = 0;\n for (let k = 0; k < count; k++) {\n const s = samples[k]!;\n const ex = s.x - px;\n const ey = s.y - py;\n segAccum += Math.sqrt(ex * ex + ey * ey);\n vertices.push({ x: s.x, y: s.y, width: s.width, cumLen: cumLen + segAccum, idx: j - 1 + (k + 1) / count });\n px = s.x;\n py = s.y;\n }\n cumLen += segAccum;\n } else {\n const dw = cur[2]! - prev[2]!;\n for (let k = 1; k <= count; k++) {\n const t = k / count;\n vertices.push({\n x: prev[0]! + dx * t,\n y: prev[1]! + dy * t,\n width: prev[2]! + dw * t,\n cumLen: cumLen + chordLen * t,\n idx: j - 1 + t,\n });\n }\n cumLen += chordLen;\n }\n }\n\n let widthSum = 0;\n for (const p of pts) widthSum += p[2]!;\n\n return { vertices, totalLen: cumLen, avgWidth: widthSum / n };\n}\n","import type { CSSLength, TegakiBundle, TegakiGlyphData } 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\n/**\n * Look up `glyphData` for a grapheme cluster, with a fallback to its leading\n * codepoint. Devanagari clusters like `\"हि\"` or `\"न्दी\"` have no entry in the\n * single-codepoint-keyed `glyphData`; without the fallback every shaped glyph\n * inside such a cluster that the variant map skipped (i.e. nominal forms like\n * the bare `ह`) would resolve to `undefined`, get tagged `hasGlyph: false`,\n * and collapse onto the 0.2s `unknownDuration` slot — drawn at end-of-slot\n * via DOM `fillText`. The fallback resolves the cluster's first codepoint\n * (e.g. `ह` from `\"हि\"`), so the nominal glyph picks up its real stroke data\n * and animates instead of popping in.\n */\nexport function lookupGlyphData(font: TegakiBundle, char: string): TegakiGlyphData | undefined {\n const direct = font.glyphData[char];\n if (direct || char.length <= 1) return direct;\n const cp = char.codePointAt(0);\n if (cp === undefined) return undefined;\n return font.glyphData[String.fromCodePoint(cp)];\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 *\n * `strokeDelays` is a sparse per-stroke override of the bundled `d` field. When\n * `strokeDelays[i]` is a number, it replaces `glyph.s[i].d` as the stroke's\n * delay relative to `localTime = 0`. Used by the timeline scheduler to defer\n * priority-tagged strokes (disconnected marks / i-dots / Arabic nuqṭa) to\n * after every body stroke in the word has drawn.\n *\n * `strokeTimeScale` multiplies both bundled `d` and `a` so a glyph's strokes\n * fit a stretched/compressed time slot (used by stagger mode with a static\n * `duration`). Defaults to `1` (no scaling). `strokeDelays` are already\n * scheduler-relative seconds and are not affected by this scale.\n */\nexport function drawGlyph(\n ctx: CanvasRenderingContext2D,\n glyph: TegakiGlyphData,\n pos: GlyphPosition,\n localTime: number,\n lineCap: LineCap,\n color: string,\n effects: ResolvedEffect[] = [],\n seed = 0,\n getSubdivided?: (stroke: Stroke) => SubdividedStroke,\n strokeEasing: ((t: number) => number) | undefined = defaultStrokeEasing,\n strokeScale = 1,\n strokeStyleOverride?: string | CanvasGradient | CanvasPattern,\n strokeDelays?: (number | undefined)[],\n strokeTimeScale = 1,\n) {\n // Default stroke paint. When a layout-spanning effect (e.g. `globalGradient`)\n // provides a CanvasGradient/Pattern via `strokeStyleOverride`, use it as the\n // default paint for main strokes and dots. `color` (always a string) is still\n // the source of truth for `shadowColor` — Canvas shadows don't accept\n // gradients. A per-stroke `strokeGradient` still overrides this per segment.\n const defaultStrokePaint: string | CanvasGradient | CanvasPattern = strokeStyleOverride ?? color;\n const scale = pos.fontSize / pos.unitsPerEm;\n const ox = pos.x;\n const oy = pos.y;\n\n const glowEffects = findEffects(effects, 'glow');\n const wobbleEffect = findEffect(effects, 'wobble');\n const pressureEffect = findEffect(effects, 'pressureWidth');\n const taperEffect = findEffect(effects, 'taper');\n const strokeGradientEffect = findEffect(effects, 'strokeGradient');\n\n // Pressure params (0 = uniform avg width, 1 = fully per-point width)\n const pressureAmount = pressureEffect ? Math.max(0, Math.min(pressureEffect.config.strength ?? 1, 1)) : 0;\n\n // Wobble params\n const wobbleAmplitude = wobbleEffect ? (wobbleEffect.config.amplitude ?? 1.5) : 0;\n const wobbleFrequency = wobbleEffect ? (wobbleEffect.config.frequency ?? 8) : 0;\n const wobbleMode = wobbleEffect?.config.mode ?? 'sine';\n const hasWobble = !!wobbleEffect;\n\n // Taper params\n const taperStart = taperEffect ? Math.max(0, Math.min(taperEffect.config.startLength ?? 0.15, 1)) : 0;\n const taperEnd = taperEffect ? Math.max(0, Math.min(taperEffect.config.endLength ?? 0.15, 1)) : 0;\n\n // Gradient params\n const gradientColors = strokeGradientEffect?.config.colors;\n const isRainbow = gradientColors === 'rainbow';\n const gradientColorStops = Array.isArray(gradientColors) ? gradientColors : undefined;\n const gradientSaturation = strokeGradientEffect?.config.saturation ?? 80;\n const gradientLightness = strokeGradientEffect?.config.lightness ?? 55;\n const hasStrokeGradient = !!strokeGradientEffect;\n\n // Effects that vary per-segment require splitting the polyline into\n // individual stroke() calls. Gradient also varies per-segment but via\n // strokeStyle, not lineWidth.\n const needsPerSegment = pressureAmount > 0 || !!taperEffect;\n\n // Fallback subdivider for callers that don't thread the engine's cache\n // (tests, standalone use). Engine always provides a cached version.\n const subdivide = getSubdivided ?? ((s: Stroke) => subdivideStroke(s, Infinity));\n\n // Wobble offsets (in font units). Evaluated once per rendered sub-vertex;\n // fractional `idx` keeps the wobble phase continuous across sub-segments.\n // dx depends on y; dy depends on x — the asymmetry keeps the perpendicular\n // wobble component out of phase with the along-stroke one.\n const wobbleDx = (_x: number, y: number, idx: number): number => {\n if (!hasWobble) return 0;\n if (wobbleMode === 'noise') return wobbleAmplitude * (noise1d(y * 0.1 + idx * 0.7, seed) * 2 - 1);\n return wobbleAmplitude * Math.sin(wobbleFrequency * (y * 0.01 + idx * 0.7) + seed);\n };\n const wobbleDy = (x: number, _y: number, idx: number): number => {\n if (!hasWobble) return 0;\n if (wobbleMode === 'noise') return wobbleAmplitude * (noise1d(x * 0.1 + idx * 0.5, seed * 1.3 + 1000) * 2 - 1);\n return wobbleAmplitude * Math.cos(wobbleFrequency * (x * 0.01 + idx * 0.5) + seed * 1.3);\n };\n\n // Helper: convert font-unit point to pixel\n const px = (x: number) => ox + x * scale;\n const py = (y: number) => oy + (y + pos.ascender) * scale;\n\n // Helper: get color for a given stroke progress\n const colorAt = (progress: number): string => {\n if (isRainbow) return rainbowColor(progress, gradientSaturation, gradientLightness, seed);\n if (gradientColorStops) return gradientColor(progress, gradientColorStops, seed);\n return color;\n };\n\n // Helper: taper multiplier (0-1) for a given stroke progress\n const taperMultiplier = (progress: number): number => {\n let m = 1;\n if (taperStart > 0 && progress < taperStart) m = Math.min(m, progress / taperStart);\n if (taperEnd > 0 && progress > 1 - taperEnd) m = Math.min(m, (1 - progress) / taperEnd);\n return m;\n };\n\n for (let si = 0; si < glyph.s.length; si++) {\n const stroke = glyph.s[si]!;\n // Stagger-mode static duration scales bundled `d` and `a`; scheduler-set\n // `strokeDelays` (dot deferral) are already in entry-relative seconds and\n // bypass the scale.\n const delay = strokeDelays?.[si] ?? stroke.d * strokeTimeScale;\n if (localTime < delay) continue;\n const elapsed = localTime - delay;\n const animDuration = stroke.a * strokeTimeScale;\n const linearProgress = animDuration > 0 ? Math.min(elapsed / animDuration, 1) : 1;\n const progress = strokeEasing ? strokeEasing(linearProgress) : linearProgress;\n\n const rawPts = stroke.p;\n if (rawPts.length === 0) continue;\n\n // Degenerate polylines (all points coincident) render as dots. Older\n // bundles can emit `[[x,y,w],[x,y,w]]` for Arabic nuqta-sized blobs where\n // the pipeline's orient step collapsed two near-identical skeleton pixels\n // into the same point; without this check they'd be dropped by the\n // `totalLen <= 0` guard below.\n const isDegenerate = rawPts.length > 1 && rawPts.every((p) => p[0] === rawPts[0]![0] && p[1] === rawPts[0]![1]);\n\n // --- Single-point dot (bypass cache; there is nothing to subdivide) ---\n if (rawPts.length === 1 || isDegenerate) {\n if (progress <= 0) continue;\n const p = rawPts[0]!;\n const dotX = px(p[0]! + wobbleDx(p[0]!, p[1]!, 0));\n const dotY = py(p[1]! + wobbleDy(p[0]!, p[1]!, 0));\n const baseLineWidth = Math.max(p[2]!, 0.5) * scale * strokeScale;\n const perPointDot = Math.max(p[2]!, 0.5) * scale * strokeScale;\n let dotWidth = baseLineWidth + (perPointDot - baseLineWidth) * pressureAmount;\n dotWidth *= taperMultiplier(0.5);\n\n // Glow passes for dots\n for (const glow of glowEffects) {\n ctx.save();\n ctx.shadowBlur = resolveCSSLength(glow.config.radius ?? 8, pos.fontSize);\n ctx.shadowColor = glow.config.color ?? color;\n ctx.shadowOffsetX = (glow.config.offsetX ?? 0) * scale;\n ctx.shadowOffsetY = (glow.config.offsetY ?? 0) * scale;\n ctx.fillStyle = glow.config.color ?? color;\n ctx.beginPath();\n if (lineCap === 'round') {\n ctx.arc(dotX, dotY, dotWidth / 2, 0, Math.PI * 2);\n } else {\n ctx.rect(dotX - dotWidth / 2, dotY - dotWidth / 2, dotWidth, dotWidth);\n }\n ctx.fill();\n ctx.restore();\n }\n\n // Main dot. strokeGradient needs a per-point color (rainbow hue or array\n // stop 0); otherwise let the default paint apply — a CanvasGradient from\n // globalGradient samples by dot position automatically.\n ctx.fillStyle = hasStrokeGradient ? colorAt(0) : defaultStrokePaint;\n ctx.beginPath();\n if (lineCap === 'round') {\n ctx.arc(dotX, dotY, dotWidth / 2, 0, Math.PI * 2);\n ctx.fill();\n } else {\n ctx.fillRect(dotX - dotWidth / 2, dotY - dotWidth / 2, dotWidth, dotWidth);\n }\n continue;\n }\n\n // --- Multi-point stroke: consume cached subdivision ---\n const cached = subdivide(stroke);\n const { vertices, totalLen, avgWidth } = cached;\n if (vertices.length < 2 || totalLen <= 0) continue;\n\n const drawLen = totalLen * progress;\n if (drawLen <= 0) continue;\n\n const baseLineWidth = Math.max(avgWidth, 0.5) * scale * strokeScale;\n\n // Binary search for the last fully-included vertex — i.e. the largest i\n // with vertices[i].cumLen <= drawLen.\n let lo = 0;\n let hi = vertices.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (vertices[mid]!.cumLen <= drawLen) lo = mid;\n else hi = mid - 1;\n }\n const lastIdx = lo;\n\n // Interpolate the tail of the last, partially-drawn sub-segment.\n let tailX = 0;\n let tailY = 0;\n let tailWidth = 0;\n let tailIdx = 0;\n let tailCumLen = 0;\n let hasTail = false;\n if (lastIdx + 1 < vertices.length && drawLen > vertices[lastIdx]!.cumLen) {\n const a = vertices[lastIdx]!;\n const b = vertices[lastIdx + 1]!;\n const segLen = b.cumLen - a.cumLen;\n const t = segLen > 0 ? (drawLen - a.cumLen) / segLen : 0;\n tailX = a.x + (b.x - a.x) * t;\n tailY = a.y + (b.y - a.y) * t;\n tailWidth = a.width + (b.width - a.width) * t;\n tailIdx = a.idx + (b.idx - a.idx) * t;\n tailCumLen = drawLen;\n hasTail = true;\n }\n\n // Pre-transform every visible vertex (raw + wobble + scale + translate)\n // exactly once — glow and main passes both iterate this array, and the\n // per-segment case needs stable endpoints across its N stroke() calls.\n const tcount = lastIdx + 1 + (hasTail ? 1 : 0);\n const txs: number[] = new Array(tcount);\n const tys: number[] = new Array(tcount);\n for (let i = 0; i <= lastIdx; i++) {\n const v = vertices[i]!;\n txs[i] = px(v.x + wobbleDx(v.x, v.y, v.idx));\n tys[i] = py(v.y + wobbleDy(v.x, v.y, v.idx));\n }\n if (hasTail) {\n txs[tcount - 1] = px(tailX + wobbleDx(tailX, tailY, tailIdx));\n tys[tcount - 1] = py(tailY + wobbleDy(tailX, tailY, tailIdx));\n }\n\n ctx.lineCap = lineCap;\n ctx.lineJoin = 'round';\n\n // Trace the full visible polyline as one Path2D primitive. Used for both\n // glow (where it's critical — shadowBlur cost is per stroke() call, so\n // coalescing into one call matters) and the no-per-segment-effect main\n // draw.\n const tracePolyline = () => {\n ctx.beginPath();\n ctx.moveTo(txs[0]!, tys[0]!);\n for (let i = 1; i < tcount; i++) ctx.lineTo(txs[i]!, tys[i]!);\n };\n\n // --- Glow passes (one stroke() call per glow over the full polyline) ---\n for (const glow of glowEffects) {\n ctx.save();\n ctx.shadowBlur = resolveCSSLength(glow.config.radius ?? 8, pos.fontSize);\n ctx.shadowColor = glow.config.color ?? color;\n ctx.shadowOffsetX = (glow.config.offsetX ?? 0) * scale;\n ctx.shadowOffsetY = (glow.config.offsetY ?? 0) * scale;\n ctx.strokeStyle = glow.config.color ?? color;\n ctx.lineWidth = baseLineWidth;\n tracePolyline();\n ctx.stroke();\n ctx.restore();\n }\n\n // --- Main stroke ---\n if (!needsPerSegment && !hasStrokeGradient) {\n // Fast path: single stroke() over the whole truncated polyline.\n ctx.strokeStyle = defaultStrokePaint;\n ctx.lineWidth = baseLineWidth;\n tracePolyline();\n ctx.stroke();\n } else {\n // Per-segment path: each sub-segment is its own mini-stroke so\n // lineWidth / strokeStyle can vary. Adjacent round-capped endpoints\n // overlap to read as a continuous line.\n const invTotalLen = 1 / totalLen;\n for (let i = 1; i < tcount; i++) {\n const aCum = i - 1 <= lastIdx ? vertices[i - 1]!.cumLen : tailCumLen;\n const bCum = i <= lastIdx ? vertices[i]!.cumLen : tailCumLen;\n const aWidth = i - 1 <= lastIdx ? vertices[i - 1]!.width : tailWidth;\n const bWidth = i <= lastIdx ? vertices[i]!.width : tailWidth;\n const midProgress = (aCum + bCum) * 0.5 * invTotalLen;\n\n let lw = baseLineWidth;\n if (needsPerSegment) {\n const perPoint = (aWidth + bWidth) * 0.5 * scale * strokeScale;\n const w = Math.max(baseLineWidth + (perPoint - baseLineWidth) * pressureAmount, 0.5 * scale * strokeScale);\n lw = w * taperMultiplier(midProgress);\n }\n ctx.lineWidth = lw;\n ctx.strokeStyle = hasStrokeGradient ? colorAt(midProgress) : defaultStrokePaint;\n ctx.beginPath();\n ctx.moveTo(txs[i - 1]!, tys[i - 1]!);\n ctx.lineTo(txs[i]!, tys[i]!);\n ctx.stroke();\n }\n }\n }\n}\n","/**\n * Features that harfbuzz's (and the browser's) complex-text shapers apply\n * context-sensitively based on the script. Passing them in an explicit\n * \"enable\" list tells the shaper to apply them unconditionally to the whole\n * text range, which breaks the positional assignment — e.g. every Arabic\n * glyph collapses to the final form.\n *\n * Trust the shaper's script defaults for these and never emit them as\n * explicit enables. (Explicit *disables* are fine — `-fina` suppressing the\n * automatic fina is exactly the user's intent.)\n */\nconst SHAPER_MANAGED_FEATURES = new Set(['init', 'medi', 'fina', 'isol', 'rlig']);\n\n/**\n * Build a CSS `font-feature-settings` value from bundle features. Same\n * filter as the HB path: shaper-managed tags are omitted so browsers keep\n * their contextual positional assignment.\n *\n * Fonts with no declared features fall back to disabling `liga` + `calt`,\n * which matches the legacy \"1:1 char-to-glyph\" assumption the renderer\n * makes when it can't shape.\n */\nexport function toCssFeatureSettings(enabled: readonly string[]): string {\n if (enabled.length === 0) return \"'calt' 0, 'liga' 0\";\n const explicit = enabled.filter((tag) => !SHAPER_MANAGED_FEATURES.has(tag));\n if (explicit.length === 0) return 'normal';\n return explicit.map((f) => `'${f}' 1`).join(', ');\n}\n","import type { TegakiBundle } from '../types.ts';\nimport { toCssFeatureSettings } from './features.ts';\n\nconst fontFaceCache = new Map<string, Promise<void>>();\nconst resolvedUrls = new Set<string>();\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, bundle.features, bundle.extraFontUrls);\n}\n\nexport function ensureFont(family: string, url: string, features?: readonly string[], extraFontUrls?: string[]): Promise<void> | null {\n if (typeof document === 'undefined') return Promise.resolve();\n // Register every subset URL under the same family name. Browsers union\n // glyph coverage across same-family faces via cmap, so Arabic text falls\n // through to an Arabic subset even though the primary face is Latin-only.\n const urls = [url, ...(extraFontUrls ?? [])];\n if (urls.every((u) => resolvedUrls.has(u))) return null;\n // Align DOM shaping with the bundle. Shaper-managed features (Arabic\n // init/medi/fina/isol, rlig) are *omitted* from font-feature-settings\n // because explicitly enabling them suppresses the browser's contextual\n // positional assignment (every glyph would end up the same variant).\n // Legacy bundles without any declared features fall back to disabling\n // liga/calt so 1:1 char-to-glyph fallback holds.\n const featureSettings = toCssFeatureSettings(features ?? []);\n const pending = urls.map((u) => {\n let cached = fontFaceCache.get(u);\n if (!cached) {\n cached = new FontFace(family, `url(${u})`, { featureSettings }).load().then((loaded) => {\n document.fonts.add(loaded);\n resolvedUrls.add(u);\n });\n fontFaceCache.set(u, cached);\n }\n return cached;\n });\n return Promise.all(pending).then(() => {});\n}\n","import type { TegakiBundle } from '../types.ts';\nimport type { BundleShaper, ShapedGlyph } from './shaper.ts';\nimport type { Timeline } from './timeline.ts';\nimport { graphemes } from './utils.ts';\n\n// Strong-RTL codepoints: Hebrew, Arabic, Syriac, Thaana, NKo, Samaritan,\n// Mandaic, plus Arabic Presentation Forms A/B. Sufficient to decide per-line\n// shaping direction for `applyShaperPositions`.\nconst RTL_CHAR_RE = /[\\u0590-\\u08FF\\uFB1D-\\uFDFF\\uFE70-\\uFEFF]/;\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 * Visual-left edge of each line in em (parallel to `lines`). Populated by\n * `applyShaperPositions` and consumed by the engine when a timeline entry\n * carries per-glyph offsets \\u2014 engine adds `lineLefts[lineIdx]` to\n * `entry.xOffsetEm` to get the absolute x position. Undefined when the\n * shaper path isn't taken; consumers must fall back to `charOffsets`.\n */\n lineLefts?: number[];\n}\n\n/**\n * Axis-aligned bounding box of the laid-out text in the ctx coordinate space\n * used by the engine's glyph loop (i.e. after `padH`/`padV` translation).\n * `width` is the max line advance; `height` is `lines.length * lineHeight`.\n */\nexport interface LayoutBBox {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Compute the text bounding box from a measured layout. Inputs are in CSS\n * pixels. Assumes the layout's char offsets are em-relative to the left edge\n * of each line (as produced by `computeTextLayout`).\n */\nexport function computeLayoutBbox(layout: TextLayout, fontSize: number, lineHeight: number): LayoutBBox {\n let maxRight = 0;\n for (const lineIndices of layout.lines) {\n for (const charIdx of lineIndices) {\n const offset = layout.charOffsets[charIdx] ?? 0;\n const width = layout.charWidths[charIdx] ?? 0;\n const right = (offset + width) * fontSize;\n if (right > maxRight) maxRight = right;\n }\n }\n return {\n x: 0,\n y: 0,\n width: maxRight,\n height: layout.lines.length * lineHeight,\n };\n}\n\n/**\n * Measure text layout using the Range API on an existing DOM element.\n * The element must already be in the document with correct text content,\n * font, line-height, white-space, and width styles applied.\n */\nexport function computeTextLayout(el: HTMLElement, fontSize: number): TextLayout;\n/**\n * Measure text layout by creating a temporary off-screen DOM element.\n */\nexport function computeTextLayout(text: string, fontSize: number, fontFamily: string, lineHeight: number, maxWidth: number): TextLayout;\nexport function computeTextLayout(\n elOrText: HTMLElement | string,\n fontSize: number,\n fontFamily?: string,\n lineHeight?: number,\n maxWidth?: number,\n): TextLayout {\n if (typeof elOrText === 'string') {\n return measureWithTempElement(elOrText, fontFamily!, fontSize, lineHeight!, maxWidth!);\n }\n return measureElement(elOrText, fontSize);\n}\n\nfunction measureElement(el: HTMLElement, fontSize: number): TextLayout {\n const textNode = el.firstChild;\n if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {\n return { lines: [], charOffsets: [], charWidths: [] };\n }\n\n const text = textNode.textContent ?? '';\n const chars = graphemes(text);\n if (!chars.length) return { lines: [], charOffsets: [], charWidths: [] };\n\n // Use element's left edge as reference so offsets are direction-agnostic.\n // For LTR the first char is near the left edge; for RTL it's near the right —\n // either way, subtracting elLeft produces correct visual x-positions.\n const elRect = el.getBoundingClientRect();\n const elLeft = elRect.left;\n // Ancestor CSS transforms (e.g. Remotion Studio's preview-fit scale) make\n // getClientRects() return pre-scale pixel values while getComputedStyle()\n // returns unscaled fontSize. Divide measured widths by the scale so the em\n // conversion matches fontSize. offsetWidth is layout-box width (unscaled).\n const scale = el.offsetWidth > 0 ? elRect.width / el.offsetWidth : 1;\n const range = document.createRange();\n\n const charOffsets: number[] = [];\n const charWidths: number[] = [];\n const lines: number[][] = [];\n let currentLine: number[] = [];\n let prevTop = -Infinity;\n let utf16Offset = 0;\n\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i]!;\n\n if (char === '\\n') {\n charOffsets.push(0);\n charWidths.push(0);\n currentLine.push(i);\n lines.push(currentLine);\n currentLine = [];\n prevTop = -Infinity;\n utf16Offset += char.length;\n continue;\n }\n\n range.setStart(textNode, utf16Offset);\n range.setEnd(textNode, utf16Offset + char.length);\n const rects = range.getClientRects();\n utf16Offset += char.length;\n\n if (rects.length === 0) {\n charOffsets.push(0);\n charWidths.push(0);\n currentLine.push(i);\n continue;\n }\n\n const rect = rects[rects.length - 1]!;\n\n // A significant vertical shift signals a new line. Both rect.top and\n // prevTop are in scaled pixels, so compare against a scaled threshold.\n if (currentLine.length > 0 && rect.top - prevTop > fontSize * 0.25 * scale) {\n lines.push(currentLine);\n currentLine = [];\n }\n\n if (currentLine.length === 0) {\n prevTop = rect.top;\n }\n\n charOffsets.push((rect.left - elLeft) / scale / fontSize);\n charWidths.push(rect.width / scale / fontSize);\n currentLine.push(i);\n }\n if (currentLine.length > 0) lines.push(currentLine);\n\n return { lines, charOffsets, charWidths };\n}\n\n/**\n * Replace `layout.charOffsets` and `charWidths` with values computed from the\n * shaper's advances, while preserving the DOM's line-break decisions. When a\n * `timeline` is provided, each entry's `xOffsetEm` and `yOffsetEm` are also\n * filled in from the shaper's per-glyph pen-walk (mutated in place) and\n * `layout.lineLefts` is populated — together they let the engine draw each\n * glyph at its GPOS-positioned origin (cursive-attachment lift, mark\n * attachment) instead of sharing one position per cluster.\n *\n * The DOM's Range API returns imprecise per-grapheme rects inside a complex-\n * shaped cluster (Arabic joining, Indic conjuncts, ligatures with kern/mark\n * GPOS), so strokes positioned from `rect.left` drift relative to the actual\n * glyph origins the shaper produced. Using the shaper's own `ax` walk keeps\n * the stroke positions aligned with the glyph ids the shaper chose.\n *\n * Line anchor (the leftmost visual pixel of each line) is measured from the\n * DOM using a full-line Range — per-grapheme rects inside shaped clusters are\n * not reliable enough to anchor against.\n */\nexport function applyShaperPositions(\n layout: TextLayout,\n el: HTMLElement,\n text: string,\n fontSize: number,\n font: TegakiBundle,\n shaper: BundleShaper,\n timeline?: Timeline,\n): TextLayout {\n const chars = graphemes(text);\n if (!chars.length) return layout;\n\n const textNode = el.firstChild;\n if (!textNode || textNode.nodeType !== Node.TEXT_NODE) return layout;\n const elRect = el.getBoundingClientRect();\n const elLeft = elRect.left;\n const scale = el.offsetWidth > 0 ? elRect.width / el.offsetWidth : 1;\n const range = document.createRange();\n\n // utf16 start offset of each grapheme.\n const graphemeStartU: number[] = [];\n {\n let u = 0;\n for (let i = 0; i < chars.length; i++) {\n graphemeStartU.push(u);\n u += chars[i]!.length;\n }\n }\n const utf16ToGrapheme = new Int32Array(text.length + 1).fill(-1);\n for (let i = 0; i < chars.length; i++) utf16ToGrapheme[graphemeStartU[i]!] = i;\n utf16ToGrapheme[text.length] = chars.length;\n\n const charOffsets = layout.charOffsets.slice();\n const charWidths = layout.charWidths.slice();\n const lineLefts: number[] = new Array(layout.lines.length).fill(0);\n const emPerUnit = 1 / font.unitsPerEm;\n\n // Index timeline entries by `graphemeIndex:glyphId` so each shaped glyph\n // can find its corresponding entry and fill in xOffsetEm/yOffsetEm.\n // Multiple entries can share the same key if the same glyph repeats inside\n // a cluster (rare); FIFO-pop preserves emission order across the duplicate.\n const entryQueue = new Map<string, number[]>();\n if (timeline) {\n for (let ei = 0; ei < timeline.entries.length; ei++) {\n const e = timeline.entries[ei]!;\n if (e.glyphId === undefined) continue;\n const key = `${e.graphemeIndex}:${e.glyphId}`;\n const list = entryQueue.get(key);\n if (list) list.push(ei);\n else entryQueue.set(key, [ei]);\n }\n }\n\n for (let li = 0; li < layout.lines.length; li++) {\n const lineIndices = layout.lines[li]!;\n const realIndices = lineIndices.filter((idx) => chars[idx] !== '\\n');\n if (realIndices.length === 0) continue;\n\n const lineStartU = graphemeStartU[realIndices[0]!]!;\n const lastReal = realIndices[realIndices.length - 1]!;\n const lineEndU = graphemeStartU[lastReal]! + chars[lastReal]!.length;\n\n // Measure the whole line's visual-left edge via a single Range. The\n // line's own aggregate rect is reliable even when per-grapheme rects\n // inside a shaped cluster are not.\n range.setStart(textNode, lineStartU);\n range.setEnd(textNode, lineEndU);\n const lineRects = range.getClientRects();\n if (lineRects.length === 0) continue;\n let lineLeftPx = Infinity;\n for (const r of lineRects) if (r.left < lineLeftPx) lineLeftPx = r.left;\n const lineLeftEm = (lineLeftPx - elLeft) / scale / fontSize;\n lineLefts[li] = lineLeftEm;\n\n // Harfbuzz emits glyphs in a per-run visual order: within each bidi/word\n // run (contiguous stretch where cluster indices walk monotonically) the\n // glyphs are already in visual left-to-right order, but the *runs* are\n // emitted in logical order. For RTL lines that means each word is\n // shaped correctly internally yet the words themselves appear in the\n // wrong order — pen-walking the raw buffer would put the logical-first\n // word at the visual left edge. Detect run boundaries (cl jumps up for\n // RTL, where within a word cl walks downward) and reverse the list of\n // runs so pen-walking forward produces the true visual-LTR layout.\n const lineText = text.slice(lineStartU, lineEndU);\n const lineRTL = RTL_CHAR_RE.test(lineText);\n const shaped = shaper.shape(lineText);\n if (shaped.length === 0) continue;\n const visualGlyphs = lineRTL ? reverseRTLRuns(shaped) : shaped;\n\n // Walk glyphs in visual order. Glyph draw position is pen + (dx, dy),\n // and the pen advances by (ax, ay) after each glyph. HarfBuzz uses a\n // y-up coordinate system; we negate dy/ay to match our y-down draw\n // axis. Record each cluster's visual left + summed advance for layout\n // (consumed by the engine's fallback path, the layout bbox, and any\n // consumer that indexes by grapheme), and fill in per-entry x/y for\n // the engine's primary draw path so marks land on their base correctly.\n const clusterLeft = new Map<number, number>();\n const clusterAdvance = new Map<number, number>();\n let penEm = 0;\n let penYEm = 0;\n for (const g of visualGlyphs) {\n const axEm = g.ax * emPerUnit;\n const ayEm = g.ay * emPerUnit;\n const dxEm = g.dx * emPerUnit;\n const dyEm = g.dy * emPerUnit;\n const glyphXEm = penEm + dxEm;\n const glyphYEm = penYEm - dyEm;\n if (!clusterLeft.has(g.cl)) clusterLeft.set(g.cl, glyphXEm);\n clusterAdvance.set(g.cl, (clusterAdvance.get(g.cl) ?? 0) + axEm);\n\n if (timeline) {\n const gIdx = utf16ToGrapheme[lineStartU + g.cl];\n if (gIdx !== undefined && gIdx >= 0) {\n const queue = entryQueue.get(`${gIdx}:${g.g}`);\n const ei = queue?.shift();\n if (ei !== undefined) {\n const entry = timeline.entries[ei]!;\n entry.xOffsetEm = glyphXEm;\n entry.yOffsetEm = glyphYEm;\n }\n }\n }\n\n penEm += axEm;\n penYEm -= ayEm;\n }\n\n // Assign offsets to the grapheme at each cluster start.\n const assigned = new Set<number>();\n for (const [cl, leftEm] of clusterLeft) {\n const gIdx = utf16ToGrapheme[lineStartU + cl];\n if (gIdx === undefined || gIdx < 0) continue;\n charOffsets[gIdx] = lineLeftEm + leftEm;\n charWidths[gIdx] = clusterAdvance.get(cl) ?? 0;\n assigned.add(gIdx);\n }\n\n // Mid-cluster graphemes (ligature interior) share their host cluster's\n // left edge and have zero advance — timeline.ts skips them for drawing,\n // but keep the offset sane for any consumer that indexes by grapheme.\n const sortedCls = [...clusterLeft.keys()].sort((a, b) => a - b);\n for (const idx of realIndices) {\n if (assigned.has(idx)) continue;\n const u = graphemeStartU[idx]! - lineStartU;\n let hostCl = -1;\n for (const cl of sortedCls) {\n if (cl <= u) hostCl = cl;\n else break;\n }\n if (hostCl < 0) continue;\n charOffsets[idx] = lineLeftEm + (clusterLeft.get(hostCl) ?? 0);\n charWidths[idx] = 0;\n }\n }\n\n return { lines: layout.lines, charOffsets, charWidths, lineLefts };\n}\n\n/**\n * Reorder a harfbuzz-shaped RTL line so that pen-walking forward produces\n * visual left-to-right positions. HB emits each word's glyphs in within-word\n * visual order (cl descending for RTL) but keeps the words themselves in\n * logical order — so we split on cl-jumps and reverse the list of runs while\n * preserving each run's internal order.\n */\nfunction reverseRTLRuns(shaped: ShapedGlyph[]): ShapedGlyph[] {\n const runs: ShapedGlyph[][] = [];\n let cur: ShapedGlyph[] = [];\n for (const g of shaped) {\n if (cur.length && g.cl > cur[cur.length - 1]!.cl) {\n runs.push(cur);\n cur = [];\n }\n cur.push(g);\n }\n if (cur.length) runs.push(cur);\n const out: ShapedGlyph[] = [];\n for (let i = runs.length - 1; i >= 0; i--) out.push(...runs[i]!);\n return out;\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, TegakiGlyphData } from '../types.ts';\nimport type { BundleShaper } from './shaper.ts';\nimport { graphemes, lookupGlyphData } 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 * When `true` (default), disconnected marks tagged by the generator — i-dots,\n * Arabic nuqṭa, diacritics — are deferred so every body stroke in a word\n * draws before any dot in that word. When `false`, strokes animate in their\n * bundled order with no deferral.\n *\n * Ignored when `stagger` is set — stagger mode treats every stroke uniformly.\n */\n deferDots?: boolean;\n /**\n * Optional staggered scheduling: each glyph starts a fixed advance after the\n * previous glyph started, regardless of whether the previous glyph has\n * finished drawing. Replaces the default \"previous-end + glyphGap\" cadence.\n *\n * Word and line breaks still pause for `wordGap` / `lineGap`. Dot deferral\n * is bypassed (each stroke draws in its bundled order). See\n * {@link TimelineStaggerConfig} for `advance` and `duration` semantics.\n */\n stagger?: TimelineStaggerConfig;\n}\n\nexport interface TimelineStaggerConfig {\n /**\n * Delay between the start of consecutive glyphs.\n * - Number: seconds (e.g. `0.3`).\n * - String ending in `%`: percentage of the previous glyph's **effective**\n * duration — i.e. the `duration` field below when set, otherwise the\n * bundled `glyph.t`. So `\"100%\"` always means \"start once the previous\n * glyph finishes\", and `\"50%\"` always means \"start halfway through\",\n * regardless of whether `duration` is overridden.\n */\n advance: number | `${number}%`;\n /**\n * Per-glyph draw duration.\n * - `'auto'` (default): each glyph plays for its bundled duration; strokes\n * keep their bundled `d`/`a` timing.\n * - Number: every glyph is scaled to take exactly this many seconds. All\n * strokes inside the glyph are time-scaled (`d * scale`, `a * scale` where\n * `scale = duration / bundledDuration`).\n */\n duration?: number | 'auto';\n}\n\nconst DEFAULTS = {\n glyphGap: 0.1,\n wordGap: 0.15,\n lineGap: 0.3,\n unknownDuration: 0.2,\n deferDots: true,\n};\n\nexport interface TimelineEntry {\n /** First grapheme of the cluster this entry represents. Used for fallback glyph lookup. */\n char: string;\n /** Grapheme index of `char` in the full text — matches `layout.charOffsets`. */\n graphemeIndex: number;\n /**\n * Shaped glyph key (when a shaper produced this entry). Bare numeric string\n * for primary-subset glyphs (e.g. `\"42\"`), or `\"<subsetIndex>:<gid>\"` for\n * glyphs from an `extraFontUrls` subset — same format used as the key into\n * `bundle.glyphDataById`.\n */\n glyphId?: string;\n offset: number;\n duration: number;\n hasGlyph: boolean;\n /**\n * Sparse per-stroke override of the bundled stroke's `d` (delay) field. When\n * `strokeDelays[i]` is a number, the renderer treats that value as stroke\n * `i`'s delay (relative to `offset`) instead of the delay stored in the\n * glyph. Populated by word-level dot deferral: priority-tagged strokes get\n * shifted to after every body stroke in the word has drawn.\n */\n strokeDelays?: (number | undefined)[];\n /**\n * X offset of this glyph relative to its visual line's left edge, in em.\n * Populated by `applyShaperPositions` from the shaper's pen-walk: this is\n * `pen.x + dx` where `dx` is the GPOS x-offset for the glyph. The engine\n * combines it with `layout.lineLefts[lineIdx]` to get the final draw x.\n *\n * Why per-entry rather than per-grapheme: in clusters with mark glyphs\n * (e.g. Arabic dot below ي), the base and the mark have different `dx` via\n * mark-attachment GPOS. Storing per-entry preserves both. Falls back to\n * `layout.charOffsets[graphemeIndex]` when undefined.\n */\n xOffsetEm?: number;\n /**\n * Y offset of this glyph relative to the line baseline, in em (positive =\n * down, mirroring CSS axis). Populated by `applyShaperPositions` as\n * `-dy / unitsPerEm` (HB's `dy` is y-up, ours is y-down). Encodes Arabic\n * cursive-attachment GPOS — Aref Ruqaa's wavy Ruq'ah baseline lifts each\n * connected letter by ~250 font units — and mark-attachment vertical\n * offsets. The engine adds `yOffsetEm * fontSize` to `glyphY` so the glyph\n * draws at the correct cursive-lifted height.\n */\n yOffsetEm?: number;\n /**\n * Multiplier applied to bundled stroke `d` (delay) and `a` (animation\n * duration) when drawing this glyph. Populated by stagger mode with a static\n * `duration` to scale the glyph's strokes to fit the new slot. Undefined or\n * `1` means use the bundled timing as-is.\n */\n strokeTimeScale?: number;\n}\n\nexport interface Timeline {\n entries: TimelineEntry[];\n totalDuration: number;\n}\n\nexport function computeTimeline(text: string, font: TegakiBundle, config?: TimelineConfig, shaper?: BundleShaper | null): Timeline {\n if (shaper && font.glyphDataById) {\n return computeShapedTimeline(text, font, config, shaper);\n }\n return computeGraphemeTimeline(text, font, config);\n}\n\n// ---------------------------------------------------------------------------\n// Stagger scheduler — alternative cadence where each glyph starts a fixed\n// advance after the previous glyph *started*, regardless of when the previous\n// glyph finishes. Used by both the grapheme and shaped paths when\n// `config.stagger` is set.\n// ---------------------------------------------------------------------------\n\n/** Parse a stagger advance value into seconds, given the previous glyph's bundled duration. */\nfunction resolveAdvance(advance: number | `${number}%`, prevBundled: number): number {\n if (typeof advance === 'number') return Math.max(0, advance);\n // String form: trailing '%' marks a percentage of the previous glyph's bundled `t`.\n const m = /^(-?\\d+(?:\\.\\d+)?)\\s*%$/.exec(advance);\n if (m) {\n const pct = Number(m[1]) / 100;\n return Math.max(0, pct * prevBundled);\n }\n // Fall back to numeric parsing (string number without %): treat as seconds.\n const n = Number(advance);\n return Number.isFinite(n) ? Math.max(0, n) : 0;\n}\n\nclass StaggerScheduler {\n readonly entries: TimelineEntry[] = [];\n private offset = 0;\n /**\n * Effective duration of the most recent glyph (= `staticDuration` when set,\n * else its bundled `glyph.t`). Basis for percent advances — so\n * `advance: '100%'` always means \"start once the previous glyph finishes\",\n * independent of whether the duration was overridden.\n */\n private prevEffective = 0;\n private hasPrev = false;\n /** Accumulated word/line gap pending until the next glyph (or finalize). */\n private pendingGap = 0;\n\n constructor(\n private readonly wordGap: number,\n private readonly lineGap: number,\n private readonly advance: number | `${number}%`,\n private readonly staticDuration: number | undefined,\n ) {}\n\n addGlyph(fields: EntryFields, bundledDuration: number): void {\n if (this.hasPrev) {\n this.offset += resolveAdvance(this.advance, this.prevEffective) + this.pendingGap;\n this.pendingGap = 0;\n }\n const duration = this.staticDuration ?? bundledDuration;\n // Avoid div-by-zero: zero-duration glyphs collapse to scale=1 (no scaling\n // matters when the glyph has no strokes anyway).\n const strokeTimeScale = bundledDuration > 0 ? duration / bundledDuration : 1;\n this.entries.push({\n ...fields,\n offset: this.offset,\n duration,\n ...(strokeTimeScale !== 1 ? { strokeTimeScale } : {}),\n });\n this.prevEffective = duration;\n this.hasPrev = true;\n }\n\n /** Emit a zero-duration marker (e.g. whitespace) at the current offset without advancing the cursor. */\n addMarker(fields: EntryFields): void {\n this.entries.push({ ...fields, offset: this.offset, duration: 0 });\n }\n\n separator(sep: Separator): void {\n this.pendingGap += sep === 'line' ? this.lineGap : this.wordGap;\n }\n\n finalize(): Timeline {\n // Total duration is the latest finish across *all* entries, not just the\n // last one — with auto duration and varying bundled lengths, an earlier\n // long glyph can outlast subsequent short ones (especially with heavy\n // overlap). Using the max guarantees every glyph has fully rendered.\n let total = 0;\n for (const e of this.entries) {\n const end = e.offset + e.duration;\n if (end > total) total = end;\n }\n return { entries: this.entries, totalDuration: total };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Scheduler — shared body/dot layout for both shaped and grapheme paths.\n// ---------------------------------------------------------------------------\n\ntype EntryFields = Omit<TimelineEntry, 'offset' | 'duration' | 'strokeDelays'>;\n\ninterface Pending {\n fields: EntryFields;\n /** Span of the body-stroke phase (or the full `t` when no strokes are dot-tagged). */\n bodyDuration: number;\n /** Span of the dot-stroke phase (`dotMaxEnd − dotMinD`). Zero when no dots. */\n dotDuration: number;\n /** Minimum `d` across dot strokes — used to re-anchor dots against the word's dot phase. */\n dotMinD: number;\n /** Indices of dot-tagged strokes inside `glyph.s`. */\n dotIndices: number[];\n /** Bundled `d` values of the dot strokes at those indices (parallel to `dotIndices`). */\n dotDelays: number[];\n}\n\ntype Separator = 'word' | 'line';\n\n/** Decompose a glyph into a body phase and an optional dot phase. */\nfunction partitionGlyph(glyph: TegakiGlyphData, fallbackTotal: number, deferDots: boolean) {\n const strokes = glyph.s;\n let bodyDuration = 0;\n let dotMinD = Infinity;\n let dotMaxEnd = 0;\n const dotIndices: number[] = [];\n const dotDelays: number[] = [];\n for (let i = 0; i < strokes.length; i++) {\n const s = strokes[i]!;\n const end = s.d + s.a;\n if (deferDots && (s.r ?? 0) < 0) {\n dotIndices.push(i);\n dotDelays.push(s.d);\n if (s.d < dotMinD) dotMinD = s.d;\n if (end > dotMaxEnd) dotMaxEnd = end;\n } else if (end > bodyDuration) {\n bodyDuration = end;\n }\n }\n if (dotIndices.length === 0) {\n return { bodyDuration: glyph.t ?? fallbackTotal, dotDuration: 0, dotMinD: 0, dotIndices, dotDelays };\n }\n // All-dot glyphs (e.g. a standalone nuqṭa that classification flagged) fall\n // back to plain body rendering so we never emit an empty body phase.\n if (bodyDuration === 0) {\n return { bodyDuration: glyph.t ?? fallbackTotal, dotDuration: 0, dotMinD: 0, dotIndices: [], dotDelays: [] };\n }\n return { bodyDuration, dotDuration: dotMaxEnd - dotMinD, dotMinD, dotIndices, dotDelays };\n}\n\nclass Scheduler {\n readonly entries: TimelineEntry[] = [];\n private readonly group: Pending[] = [];\n private offset = 0;\n /** Gap appended by the last `separate` call; stripped on `finalize` when no further content followed. */\n private lastGap = 0;\n\n constructor(\n private readonly glyphGap: number,\n private readonly wordGap: number,\n private readonly lineGap: number,\n ) {}\n\n add(p: Pending): void {\n // A pending group invalidates any trailing gap — its content sits past\n // the gap, so we're no longer at a strippable trailing position.\n this.lastGap = 0;\n this.group.push(p);\n }\n\n /**\n * Close the current word group and advance by a separator gap. Optionally\n * emits a zero-width marker entry (e.g. a whitespace grapheme / cluster)\n * between the group end and the gap.\n */\n separate(sep: Separator, marker?: { fields: EntryFields; duration: number }): void {\n this.flushGroup();\n if (marker) {\n this.entries.push({ ...marker.fields, offset: this.offset, duration: marker.duration });\n this.offset += marker.duration;\n }\n const gap = sep === 'line' ? this.lineGap : this.wordGap;\n this.offset += gap;\n this.lastGap = gap;\n }\n\n finalize(): Timeline {\n this.flushGroup();\n // If the last activity was a `separate`, `lastGap` still holds its gap —\n // strip it so the timeline ends exactly at the last pixel drawn. If\n // instead a group was flushed (resetting `lastGap` to 0), nothing is\n // stripped.\n return { entries: this.entries, totalDuration: Math.max(0, this.offset - this.lastGap) };\n }\n\n private flushGroup(): void {\n if (this.group.length === 0) return;\n const group = this.group;\n\n // --- Phase 1: bodies laid out sequentially, separated by `glyphGap`. ---\n const bodyStarts: number[] = new Array(group.length);\n let cursor = this.offset;\n for (let i = 0; i < group.length; i++) {\n bodyStarts[i] = cursor;\n cursor += group[i]!.bodyDuration;\n if (i < group.length - 1) cursor += this.glyphGap;\n }\n const bodyEnd = cursor;\n\n // --- Phase 2: deferred dots, anchored after every body in the group. ---\n // Glyphs without dots contribute nothing to phase 2 and stay finished at\n // their body end; glyphs with dots get their entry duration stretched to\n // cover the dot phase.\n const hasAnyDots = group.some((p) => p.dotIndices.length > 0);\n const dotStarts: (number | undefined)[] = new Array(group.length);\n let groupEnd = bodyEnd;\n if (hasAnyDots) {\n cursor = bodyEnd + this.glyphGap;\n for (let i = 0; i < group.length; i++) {\n const p = group[i]!;\n if (p.dotIndices.length === 0) continue;\n dotStarts[i] = cursor;\n cursor += p.dotDuration + this.glyphGap;\n }\n // The trailing glyphGap past the last dot phase is intra-group spacing\n // that should not count; strip it.\n groupEnd = cursor - this.glyphGap;\n }\n\n // --- Emit each glyph's finalized entry. ---\n for (let i = 0; i < group.length; i++) {\n const p = group[i]!;\n const bodyStart = bodyStarts[i]!;\n const dotStart = dotStarts[i];\n\n let strokeDelays: (number | undefined)[] | undefined;\n let endTime = bodyStart + p.bodyDuration;\n if (dotStart !== undefined && p.dotIndices.length > 0) {\n // Rebuild a sparse per-stroke delay array. `dotIndices[k]` is the\n // absolute stroke index inside the glyph; its new effective delay\n // (relative to the entry's offset, i.e. the body start) re-anchors\n // it to the group-level dot phase while preserving the glyph's\n // intra-dot spacing.\n strokeDelays = [];\n for (let k = 0; k < p.dotIndices.length; k++) {\n const strokeIdx = p.dotIndices[k]!;\n strokeDelays[strokeIdx] = dotStart - bodyStart + (p.dotDelays[k]! - p.dotMinD);\n }\n endTime = dotStart + p.dotDuration;\n }\n\n this.entries.push({\n ...p.fields,\n offset: bodyStart,\n duration: endTime - bodyStart,\n ...(strokeDelays ? { strokeDelays } : {}),\n });\n }\n\n this.group.length = 0;\n this.offset = groupEnd;\n this.lastGap = 0;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Grapheme-path timeline (no shaper, iterate graphemes directly).\n// ---------------------------------------------------------------------------\n\nfunction computeGraphemeTimeline(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 const deferDots = config?.deferDots ?? DEFAULTS.deferDots;\n\n const chars = graphemes(text);\n\n if (config?.stagger) {\n const staticDur = config.stagger.duration === 'auto' || config.stagger.duration === undefined ? undefined : config.stagger.duration;\n const sched = new StaggerScheduler(wordGap, lineGap, config.stagger.advance, staticDur);\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i]!;\n const isLineBreak = char === '\\n';\n const isWhitespace = !isLineBreak && /^\\s+$/.test(char);\n if (isLineBreak) {\n sched.separator('line');\n continue;\n }\n if (isWhitespace) {\n sched.addMarker({ char, graphemeIndex: i, hasGlyph: false });\n sched.separator('word');\n continue;\n }\n const glyph = lookupGlyphData(font, char);\n if (glyph) {\n sched.addGlyph({ char, graphemeIndex: i, hasGlyph: true }, glyph.t ?? unknownDuration);\n } else {\n sched.addGlyph({ char, graphemeIndex: i, hasGlyph: false }, unknownDuration);\n }\n }\n return sched.finalize();\n }\n\n const sched = new Scheduler(glyphGap, wordGap, lineGap);\n\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i]!;\n const isLineBreak = char === '\\n';\n const isWhitespace = !isLineBreak && /^\\s+$/.test(char);\n\n if (isLineBreak) {\n sched.separate('line');\n continue;\n }\n if (isWhitespace) {\n sched.separate('word', { fields: { char, graphemeIndex: i, hasGlyph: false }, duration: 0 });\n continue;\n }\n\n const glyph = lookupGlyphData(font, char);\n if (glyph) {\n const part = partitionGlyph(glyph, unknownDuration, deferDots);\n sched.add({\n fields: { char, graphemeIndex: i, hasGlyph: true },\n bodyDuration: part.bodyDuration,\n dotDuration: part.dotDuration,\n dotMinD: part.dotMinD,\n dotIndices: part.dotIndices,\n dotDelays: part.dotDelays,\n });\n } else {\n sched.add({\n fields: { char, graphemeIndex: i, hasGlyph: false },\n bodyDuration: unknownDuration,\n dotDuration: 0,\n dotMinD: 0,\n dotIndices: [],\n dotDelays: [],\n });\n }\n }\n\n return sched.finalize();\n}\n\n// ---------------------------------------------------------------------------\n// Shaped-path timeline (harfbuzz clusters).\n// ---------------------------------------------------------------------------\n\nfunction computeShapedTimeline(text: string, font: TegakiBundle, config: TimelineConfig | undefined, shaper: BundleShaper): 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 const deferDots = config?.deferDots ?? DEFAULTS.deferDots;\n\n // UTF-16 offset → grapheme index map. Shaper clusters come back in UTF-16\n // units; the renderer's layout indexes by grapheme. Map once per text.\n const chars = graphemes(text);\n const utf16ToGrapheme = new Int32Array(text.length + 1).fill(-1);\n {\n let u = 0;\n for (let i = 0; i < chars.length; i++) {\n utf16ToGrapheme[u] = i;\n u += chars[i]!.length;\n }\n utf16ToGrapheme[text.length] = chars.length;\n }\n\n const staggerSched = config?.stagger\n ? new StaggerScheduler(\n wordGap,\n lineGap,\n config.stagger.advance,\n config.stagger.duration === 'auto' || config.stagger.duration === undefined ? undefined : config.stagger.duration,\n )\n : null;\n const sched = staggerSched ? null : new Scheduler(glyphGap, wordGap, lineGap);\n\n // Shape each newline-delimited line separately so shaping never crosses a\n // break. This matches the DOM layout, which also breaks at `\\n`.\n let lineStart = 0;\n for (let i = 0; i <= text.length; i++) {\n const atEnd = i === text.length;\n if (!atEnd && text[i] !== '\\n') continue;\n\n const lineText = text.slice(lineStart, i);\n if (lineText.length > 0) {\n const shaped = shaper.shape(lineText);\n // Harfbuzz emits glyphs in visual order (left-to-right on screen) regardless\n // of script direction. Sort by cluster offset so animation follows the\n // logical / reading order — matching how each script is actually\n // handwritten (right-to-left for Arabic/Hebrew, left-to-right for Latin).\n // Stable sort preserves intra-cluster glyph order (marks, ligature splits).\n const order = shaped.map((_, idx) => idx);\n order.sort((a, b) => shaped[a]!.cl - shaped[b]!.cl || a - b);\n for (let k = 0; k < order.length; k++) {\n const g = order[k]!;\n const glyph = shaped[g]!;\n const clusterStart = lineStart + glyph.cl;\n // Cluster extents run to the next cluster start in **logical** order\n // (i.e. the next entry in the sorted `order` array). Using the visual-\n // order neighbour works for LTR (cl ascending anyway) but yields\n // negative-length slices for RTL where cl descends.\n const nextOrder = k + 1 < order.length ? order[k + 1]! : -1;\n const clusterEnd = nextOrder >= 0 ? lineStart + shaped[nextOrder]!.cl : i;\n const graphemeIdx = utf16ToGrapheme[clusterStart] ?? -1;\n if (graphemeIdx < 0) continue; // cluster starts mid-grapheme — skip\n const clusterText = text.slice(clusterStart, clusterEnd);\n const firstChar = chars[graphemeIdx]!;\n const isWhitespace = /^\\s+$/.test(clusterText);\n const data = font.glyphDataById?.[glyph.g] ?? lookupGlyphData(font, firstChar);\n const hasGlyph = !!data;\n\n if (isWhitespace) {\n if (staggerSched) {\n staggerSched.addMarker({ char: firstChar, graphemeIndex: graphemeIdx, glyphId: glyph.g, hasGlyph });\n staggerSched.separator('word');\n } else {\n sched!.separate('word', {\n fields: { char: firstChar, graphemeIndex: graphemeIdx, glyphId: glyph.g, hasGlyph },\n duration: 0,\n });\n }\n continue;\n }\n\n if (staggerSched) {\n const bundled = hasGlyph && data ? (data.t ?? unknownDuration) : unknownDuration;\n staggerSched.addGlyph({ char: firstChar, graphemeIndex: graphemeIdx, glyphId: glyph.g, hasGlyph: !!(hasGlyph && data) }, bundled);\n } else if (hasGlyph && data) {\n const part = partitionGlyph(data, unknownDuration, deferDots);\n sched!.add({\n fields: { char: firstChar, graphemeIndex: graphemeIdx, glyphId: glyph.g, hasGlyph: true },\n bodyDuration: part.bodyDuration,\n dotDuration: part.dotDuration,\n dotMinD: part.dotMinD,\n dotIndices: part.dotIndices,\n dotDelays: part.dotDelays,\n });\n } else {\n sched!.add({\n fields: { char: firstChar, graphemeIndex: graphemeIdx, glyphId: glyph.g, hasGlyph: false },\n bodyDuration: unknownDuration,\n dotDuration: 0,\n dotMinD: 0,\n dotIndices: [],\n dotDelays: [],\n });\n }\n }\n }\n\n if (!atEnd) {\n if (staggerSched) staggerSched.separator('line');\n else sched!.separate('line');\n lineStart = i + 1;\n }\n }\n\n return staggerSched ? staggerSched.finalize() : sched!.finalize();\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 * Draw-priority rank. `0` (default) is the glyph body; negative numbers are\n * rendered later within word-level scheduling so disconnected marks — i-dots,\n * Arabic nuqṭa, diacritics — follow after every body stroke in the word.\n * Only `0` and `-1` are used today; the range is open for future priority\n * tiers.\n */\n priority?: 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),\n * `a` (animation duration), and optional `r` (priority — see `Stroke.priority`;\n * omitted when `0`)\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 r?: number;\n }[];\n}\n\ntype BaseEffectConfig = { enabled?: boolean };\n\n/** A length value: plain number is pixels, string `\"${number}em\"` is relative to font size. */\nexport type CSSLength = number | `${number}em`;\n\nexport type TegakiEffectConfigs = {\n glow: BaseEffectConfig & { radius?: CSSLength; color?: string; offsetX?: number; offsetY?: number };\n wobble: BaseEffectConfig & { amplitude?: number; frequency?: number; mode?: 'sine' | 'noise' };\n pressureWidth: BaseEffectConfig & { strength?: number };\n taper: BaseEffectConfig & { startLength?: number; endLength?: number };\n strokeGradient: BaseEffectConfig & { colors?: string[] | 'rainbow'; saturation?: number; lightness?: number };\n /**\n * Linear gradient spanning the whole text bounding box (CSS `background-clip: text` style).\n * Unlike `strokeGradient`, the color stops map to canvas-space position — the leftmost pixel of\n * the first glyph is `colors[0]` and the rightmost pixel of the last glyph is `colors[N]`,\n * regardless of stroke boundaries.\n *\n * - `colors`: ordered color stops (2+ stops).\n * - `angle`: direction in degrees. `0` = left→right (default). `90` = top→bottom.\n * Positive values rotate clockwise (y-down screen convention).\n */\n globalGradient: BaseEffectConfig & { colors?: string[]; angle?: number };\n};\n\nexport type TegakiEffectName = keyof TegakiEffectConfigs;\n\n/** Effects that can only appear once (cannot be used with custom keys). */\nexport type TegakiSingletonEffectName = 'pressureWidth' | 'wobble' | 'taper' | 'strokeGradient' | 'globalGradient';\n\n/** Effects that can be duplicated with custom keys. */\nexport type TegakiMultiEffectName = Exclude<TegakiEffectName, TegakiSingletonEffectName>;\n\ntype TegakiCustomEffect = {\n [K in TegakiMultiEffectName]: TegakiEffectConfigs[K] & { effect: K; order?: number };\n}[TegakiMultiEffectName];\n\n/** Validates an effects object: known keys infer `effect`, unknown keys require it (singleton effects excluded). */\nexport type TegakiEffects<T> = {\n [K in keyof T]: K extends TegakiEffectName\n ? (TegakiEffectConfigs[K] & { effect?: K; order?: number }) | boolean\n : TegakiCustomEffect | boolean;\n};\n\nexport interface TegakiBundle {\n /** Bundle format version. Used by the engine to warn about incompatible bundles. */\n version?: number;\n family: string;\n /**\n * Original font family name, used as a CSS fallback for characters not in\n * the generated glyph set. Present when the bundle was generated from a\n * subset of the font (the default). When absent, `family` is the original\n * name (full-font bundle).\n */\n fullFamily?: string;\n lineCap: LineCap;\n fontUrl: string;\n /**\n * Additional subset font URLs (e.g. Arabic/Hebrew/CJK subsets served alongside\n * Latin). Registered under the same `family` as `fontUrl` for DOM layout and\n * fed into the shaper so cross-script text gets shaped against the subset\n * that actually contains its glyphs — required for positional forms in\n * Arabic, conjuncts in Indic, etc.\n */\n extraFontUrls?: 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 /** Default glyphs keyed by character. Used as a fallback when a shaped glyph id is absent. */\n glyphData: Record<string, TegakiGlyphData>;\n /**\n * Glyphs keyed by shaper output. Populated when the bundle was generated\n * with ligature/contextual-alternate support. Keys are the shaper's\n * opentype glyph id as a string (e.g. `\"42\"`) for glyphs from the primary\n * font, or `\"<subsetIndex>:<gid>\"` (e.g. `\"1:42\"`) for glyphs from an\n * `extraFontUrls` subset — subset index matches the position in\n * `extraFontUrls` + 1 (0 is reserved for the primary). Includes every glyph\n * the shaper emits — nominal, ligature, contextual variant — so a shaped\n * glyph never needs to fall back through `glyphData[char]`. That fallback\n * is brittle for complex-script clusters where `entry.char` is a\n * multi-codepoint grapheme (Devanagari `\"हि\"`, `\"स्ते\"`) and `glyphData`\n * is keyed per single codepoint. Misses still fall back to `glyphData[char]`\n * for the no-shaper path.\n */\n glyphDataById?: Record<string, TegakiGlyphData>;\n /**\n * OpenType feature tags the bundle was generated with (e.g. `['liga', 'calt']`).\n * The renderer enables these during shaping. Absent when no variant glyphs\n * were generated.\n */\n features?: readonly string[];\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 if (bundle.fullFamily && bundle.fullFamily !== bundle.family) {\n bundles.set(bundle.fullFamily, bundle);\n }\n}\n\n/** Look up a registered bundle by family name. */\nexport function getBundle(family: string): TegakiBundle | undefined {\n return bundles.get(family);\n}\n\nexport function resolveBundle(font: TegakiBundle | string | undefined): TegakiBundle | undefined {\n if (typeof font === 'string') {\n const bundle = getBundle(font);\n if (!bundle) throw new Error(`TegakiEngine: no bundle registered for \"${font}\". Call TegakiEngine.registerBundle() first.`);\n return bundle;\n }\n if (font) checkBundleVersion(font);\n return font;\n}\n","import { BUNDLE_VERSION, type LineCap, type TegakiBundle, type TegakiGlyphData } from '../types.ts';\n\n/**\n * Creates a {@link TegakiBundle} from its constituent parts.\n *\n * Useful when loading font data from a CDN or other source where the\n * pre-built bundle modules aren't available:\n *\n * ```js\n * const glyphData = await fetch('.../glyphData.json').then(r => r.json());\n * const bundle = createBundle({\n * family: 'Caveat',\n * fontUrl: '.../caveat.ttf',\n * glyphData,\n * });\n * ```\n */\nexport function createBundle({\n family,\n fullFamily,\n fontUrl,\n fullFontUrl,\n glyphData,\n lineCap = 'round',\n unitsPerEm = 1000,\n ascender = 800,\n descender = -200,\n}: {\n family: string;\n fullFamily?: string;\n fontUrl: string;\n fullFontUrl?: string;\n glyphData: Record<string, TegakiGlyphData>;\n lineCap?: LineCap;\n unitsPerEm?: number;\n ascender?: number;\n descender?: number;\n}): TegakiBundle {\n const rules = [`@font-face { font-family: '${family}'; src: url(${fontUrl}); }`];\n if (fullFamily && fullFontUrl) {\n rules.push(`@font-face { font-family: '${fullFamily}'; src: url(${fullFontUrl}); }`);\n }\n return {\n version: BUNDLE_VERSION,\n family,\n fullFamily,\n lineCap,\n fontUrl,\n fullFontUrl,\n fontFaceCSS: rules.join(' '),\n unitsPerEm,\n ascender,\n descender,\n glyphData,\n };\n}\n","export const CSS_TIME = '--tegaki-time';\nexport const CSS_PROGRESS = '--tegaki-progress';\nexport const CSS_DURATION = '--tegaki-duration';\n\nexport const PADDING_H_EM = 0.2;\nexport const MIN_LINE_HEIGHT_EM = 1.8;\nexport const MIN_PADDING_V_EM = 0.2;\n\n// Register custom properties so they are animatable (typed as <number>).\n// Deferred to first use to avoid running at import time during SSR.\nlet cssPropertiesRegistered = false;\nexport function registerCssProperties() {\n if (cssPropertiesRegistered) return;\n cssPropertiesRegistered = true;\n if (typeof CSS !== 'undefined' && 'registerProperty' in CSS) {\n for (const prop of [CSS_TIME, CSS_PROGRESS, CSS_DURATION]) {\n try {\n CSS.registerProperty({ name: prop, syntax: '<number>', inherits: true, initialValue: '0' });\n } catch {\n // Already registered — ignore.\n }\n }\n }\n}\n","import { findEffect, findEffects, type ResolvedEffect } from './effects.ts';\nimport { resolveCSSLength } from './utils.ts';\n\n/**\n * Draw a fallback glyph (plain text) with applicable effects (glow, strokeGradient, wobble).\n */\nexport function drawFallbackGlyph(\n ctx: CanvasRenderingContext2D,\n char: string,\n x: number,\n baseline: number,\n fontSize: number,\n fontFamily: string,\n color: string,\n effects: ResolvedEffect[] = [],\n seed = 0,\n) {\n const glowEffects = findEffects(effects, 'glow');\n const wobbleEffect = findEffect(effects, 'wobble');\n const strokeGradientEffect = findEffect(effects, 'strokeGradient');\n\n // Wobble offsets\n let dx = 0;\n let dy = 0;\n if (wobbleEffect) {\n const amplitude = (wobbleEffect.config.amplitude ?? 1.5) * (fontSize / 100);\n const frequency = wobbleEffect.config.frequency ?? 8;\n dx = amplitude * Math.sin(frequency * (baseline * 0.01) + seed);\n dy = amplitude * Math.cos(frequency * (x * 0.01) + seed * 1.3);\n }\n\n const drawX = x + dx;\n const drawY = baseline + dy;\n\n // Gradient / rainbow color\n let fillColor = color;\n if (strokeGradientEffect) {\n const colors = strokeGradientEffect.config.colors;\n if (colors === 'rainbow') {\n const saturation = strokeGradientEffect.config.saturation ?? 80;\n const lightness = strokeGradientEffect.config.lightness ?? 55;\n const hue = (seed * 137.5) % 360;\n fillColor = `hsl(${hue}, ${saturation}%, ${lightness}%)`;\n } else if (Array.isArray(colors) && colors.length > 0) {\n fillColor = colors[Math.floor(seed) % colors.length]!;\n }\n }\n\n ctx.save();\n ctx.font = `${fontSize}px ${fontFamily}`;\n ctx.textBaseline = 'alphabetic';\n\n // Glow passes\n for (const glow of glowEffects) {\n ctx.save();\n ctx.shadowBlur = resolveCSSLength(glow.config.radius ?? 8, fontSize);\n ctx.shadowColor = glow.config.color ?? color;\n ctx.shadowOffsetX = glow.config.offsetX ?? 0;\n ctx.shadowOffsetY = glow.config.offsetY ?? 0;\n ctx.fillStyle = glow.config.color ?? color;\n ctx.fillText(char, drawX, drawY);\n ctx.restore();\n }\n\n // Main text\n ctx.fillStyle = fillColor;\n ctx.fillText(char, drawX, drawY);\n\n ctx.restore();\n}\n","import { CSS_DURATION, CSS_PROGRESS, CSS_TIME } from '../lib/css-properties.ts';\nimport { computeTimeline } from '../lib/timeline.ts';\nimport { cssFontFamily } from '../lib/utils.ts';\nimport { resolveBundle } from './bundle-registry.ts';\nimport type { CreateElementFn, TegakiEngineOptions } from './types.ts';\n\nexport const PAD_V_CSS = 'max(0.2em, 0.9em - 0.5lh)';\n\nexport function buildRootProps(options: TegakiEngineOptions): Record<string, any> {\n const text = options.text ?? '';\n const font = resolveBundle(options.font);\n const fontFamily = font ? cssFontFamily(font) : undefined;\n\n const duration = text && font ? computeTimeline(text, font, options.timing).totalDuration : 0;\n const timeObj = typeof options.time === 'object' ? options.time : null;\n const rawTime =\n typeof options.time === 'number'\n ? options.time\n : timeObj?.mode === 'controlled'\n ? timeObj.unit === 'progress'\n ? timeObj.value * duration\n : timeObj.value\n : timeObj?.mode === 'uncontrolled'\n ? (timeObj.initialTime ?? 0)\n : 0;\n const easing = timeObj?.mode === 'uncontrolled' ? timeObj.easing : undefined;\n const time = easing && duration > 0 ? easing(rawTime / duration) * duration : rawTime;\n const progress = duration > 0 ? time / duration : 0;\n\n return {\n 'data-tegaki': 'root',\n dir: options?.direction ?? 'auto',\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 textRendering: 'geometricPrecision',\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 type { BundleShaper } from '../lib/shaper.ts';\nimport type { TegakiBundle } from '../types.ts';\n\n/**\n * Factory invoked once per bundle to produce (or decline) a shaper.\n *\n * Return `null` when the bundle doesn't need shaping or the environment can't\n * run it — the renderer falls back to its char-keyed glyph path. Return a\n * `Promise<BundleShaper>` when shaper init is async (wasm load, font fetch).\n */\nexport type ShaperFactory = (bundle: TegakiBundle) => Promise<BundleShaper> | null;\n\nlet factory: ShaperFactory | null = null;\nconst shaperCache = new Map<string, Promise<BundleShaper>>();\n\n/**\n * Register a shaper factory. Shaping is opt-in — without a registered factory,\n * the renderer iterates raw graphemes and uses the bundle's char-keyed\n * `glyphData` map. Use `tegaki/shaper-harfbuzz` for fonts that need complex\n * shaping (ligatures, contextual forms, Arabic/Indic scripts).\n *\n * Re-registering replaces the previous factory and invalidates the shaper\n * cache. Pass `null` to unregister.\n */\nexport function registerShaper(f: ShaperFactory | null): void {\n factory = f;\n shaperCache.clear();\n}\n\n/**\n * Build (or reuse) a shaper for a bundle. Returns `null` when no factory is\n * registered or the factory declined this bundle.\n */\nexport function getShaperForBundle(bundle: TegakiBundle): Promise<BundleShaper> | null {\n if (!factory) return null;\n const key = bundle.fontUrl;\n let entry = shaperCache.get(key);\n if (!entry) {\n const result = factory(bundle);\n if (!result) return null;\n shaperCache.set(key, result);\n entry = result;\n }\n return entry;\n}\n","import {\n CSS_DURATION,\n CSS_PROGRESS,\n CSS_TIME,\n MIN_LINE_HEIGHT_EM,\n MIN_PADDING_V_EM,\n PADDING_H_EM,\n registerCssProperties,\n} from '../lib/css-properties.ts';\nimport { drawFallbackGlyph } from '../lib/drawFallbackGlyph.ts';\nimport { drawGlyph } from '../lib/drawGlyph.ts';\nimport {\n findEffect,\n getEffectDefinition,\n hasRenderHooks,\n type RenderStageContext,\n type ResolvedEffect,\n resolveEffects,\n} from '../lib/effects.ts';\nimport { ensureFont } from '../lib/font.ts';\nimport type { BundleShaper } from '../lib/shaper.ts';\nimport { type SubdividedStroke, subdivideStroke } from '../lib/strokeCache.ts';\nimport type { TextLayout } from '../lib/textLayout.ts';\nimport { applyShaperPositions, computeLayoutBbox, computeTextLayout } from '../lib/textLayout.ts';\nimport type { Timeline, TimelineConfig, TimelineEntry } from '../lib/timeline.ts';\nimport { computeTimeline } from '../lib/timeline.ts';\nimport { cssFontFamily, graphemes, lookupGlyphData } from '../lib/utils.ts';\nimport type { TegakiBundle, TegakiGlyphData } from '../types.ts';\nimport { getBundle, registerBundle, resolveBundle } from './bundle-registry.ts';\nimport { buildChildren, buildRootProps, domCreateElement } from './render-elements.ts';\nimport { getShaperForBundle, registerShaper } from './shaper-registry.ts';\nimport type { CreateElementFn, TegakiEngineOptions, TegakiQuality, TimeControlMode, TimeControlProp } from './types.ts';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a percentage string like `\"50%\"` into a 0–1 fraction. Returns `null`\n * for non-percentage strings or unparseable input. Whitespace around the\n * value is tolerated; the numeric part is parsed with `Number(...)`, so any\n * finite numeric form (including negatives and decimals) is accepted.\n */\nfunction parsePercentage(s: string): number | null {\n const trimmed = s.trim();\n if (!trimmed.endsWith('%')) return null;\n const num = Number(trimmed.slice(0, -1));\n return Number.isFinite(num) ? num / 100 : null;\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 (typeof prop === 'string') {\n if (prop === 'css') return { mode: 'css' };\n const pct = parsePercentage(prop);\n if (pct != null) return { mode: 'controlled', value: pct, unit: 'progress' };\n return { mode: 'uncontrolled' };\n }\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 = registerBundle;\n\n /** Look up a registered bundle by family name. */\n static getBundle = getBundle;\n\n // --- Shaper registry (delegates to shaper-registry module) ---\n\n /**\n * Register a shaper factory. Shaping is opt-in — without a registered\n * factory, the renderer iterates raw graphemes and uses the bundle's\n * char-keyed `glyphData` map. Pass the `harfbuzzShaper` export from\n * `tegaki/shaper-harfbuzz` for fonts that need complex shaping.\n *\n * Re-registering replaces the previous factory and invalidates the cache.\n * Pass `null` to unregister.\n */\n static registerShaper = registerShaper;\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 _onChangeTimeline: ((timeline: Timeline) => 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 private _shaper: BundleShaper | null = null;\n private _shaperReady = true;\n private _shaperEnabled = true;\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 container.dir = options?.direction ?? 'auto';\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 /**\n * The engine's current timeline — the same object that drives rendering.\n * Reflects the resolved shaper once the (async) shaper promise has\n * settled; use the `onChangeTimeline` option to be notified of recomputations.\n * Treat the returned object as read-only.\n */\n get timeline(): Timeline {\n return this._timeline;\n }\n\n /**\n * Compute a timeline for arbitrary text against this engine's currently-\n * loaded font, timing config, and resolved shaper. Useful for measuring\n * the duration of hypothetical text without changing what's rendered\n * (e.g. layout planning, fade-in scheduling).\n *\n * Returns an empty timeline when no font is loaded. The result reflects\n * shaper state at call time — call after `onChangeTimeline` has fired\n * once to be sure the shaper has resolved.\n */\n computeTimeline(text: string): Timeline {\n if (!this._font) return { entries: [], totalDuration: 0 };\n return computeTimeline(text, this._font, this._timing, this._shaper);\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 /**\n * Seek the (uncontrolled) timeline to an absolute time. Accepts seconds\n * (number) or a percentage string like `\"50%\"`, which is interpreted as\n * a fraction of the timeline's total duration.\n */\n seek(time: number | `${number}%`): void {\n if (this._timeControl.mode !== 'uncontrolled') return;\n let resolved: number;\n if (typeof time === 'string') {\n const pct = parsePercentage(time);\n if (pct == null) return;\n resolved = pct * this._timeline.totalDuration;\n } else {\n resolved = time;\n }\n this._internalTime = Math.max(0, Math.min(resolved, 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 // NFC normalize so input form (NFC vs NFD) doesn't change which bundle\n // key resolves: bundles are built with NFC keys, and HarfBuzz / the\n // browser overlay normalize internally, so without this the canvas\n // shapes correctly but `glyphData[char]` lookups (timeline, advance\n // widths, DOM-fillText fallback) miss for NFD input — e.g. `\"é\"` typed\n // as `e` + U+0301 would resolve to bare `e` via the leading-codepoint\n // fallback even though the bundle has the right precomposed glyph.\n const nextText = (options.text ?? '').replace(/\\r\\n?/g, '\\n').normalize('NFC');\n if (nextText !== this._text) {\n this._text = nextText;\n dirtyTimeline = true;\n dirtyLayout = true;\n }\n }\n\n if ('shaper' in options) {\n const next = options.shaper !== false;\n if (next !== this._shaperEnabled) {\n this._shaperEnabled = next;\n this._loadShaper();\n this._updateOverlayStyle();\n dirtyTimeline = true;\n dirtyLayout = true;\n dirtyPlayback = true;\n dirtyRender = 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 if ('onChangeTimeline' in options) {\n this._onChangeTimeline = options.onChangeTimeline;\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 // When the shaper is off, the renderer iterates raw graphemes and looks\n // each char up in the bundle's char-keyed `glyphData` — i.e. nominal\n // glyphs only. The overlay (which provides layout measurement and the\n // visible text outline) must match: disable every variant-producing\n // GSUB feature so the browser doesn't form ligatures, contextual\n // alternates, or Arabic positional forms the renderer can't draw.\n this._overlayEl.style.fontFeatureSettings = this._shaperEnabled\n ? ''\n : \"'liga' 0, 'calt' 0, 'clig' 0, 'rlig' 0, 'dlig' 0, 'init' 0, 'medi' 0, 'fina' 0, 'isol' 0\";\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) {\n this._loadShaper();\n return;\n }\n\n const pending = ensureFont(font.family, font.fontUrl, font.features, font.extraFontUrls);\n if (pending === null) {\n this._fontReady = true;\n } else {\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 this._loadShaper();\n }\n\n /**\n * Resolve the shaper for the current font. Called when the font changes or\n * when the `shaper` option is toggled. Drops any in-flight shaper for the\n * previous font; the `_font === currentFont` guard inside the promise\n * handler discards stale resolutions.\n */\n private _loadShaper(): void {\n this._shaper = null;\n this._shaperReady = true;\n if (!this._shaperEnabled || !this._font) return;\n\n const shaperPromise = getShaperForBundle(this._font);\n if (!shaperPromise) return;\n\n this._shaperReady = false;\n const currentFont = this._font;\n shaperPromise.then((shaper) => {\n if (this._font === currentFont && this._shaperEnabled && !this._destroyed) {\n this._shaper = shaper;\n this._shaperReady = true;\n this._recomputeTimeline();\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, this._shaper);\n } else {\n this._timeline = { entries: [] as TimelineEntry[], totalDuration: 0 };\n }\n this._onChangeTimeline?.(this._timeline);\n }\n\n private _recomputeLayout(): void {\n if (this._fontReady && this._font?.family && this._fontSize && this._containerWidth && this._text) {\n const shaperId = this._shaper ? '1' : '0';\n const key = `${this._text}\\0${this._font.family}\\0${this._fontSize}\\0${this._lineHeight}\\0${this._containerWidth}\\0${this._direction ?? ''}\\0${shaperId}`;\n if (key === this._layoutKey) return;\n this._layoutKey = key;\n let layout = computeTextLayout(this._overlayEl, this._fontSize);\n if (this._shaper && this._font) {\n // Replace DOM-measured per-grapheme offsets with shaper-accumulated\n // advances so stroke positions match the glyph ids the shaper chose.\n // Also fills in per-entry GPOS x/y offsets on the (already-computed)\n // timeline — essential for Arabic cursive attachment and mark\n // positioning, where each glyph in a cluster needs its own origin.\n // The DOM is still the source of truth for line breaks.\n layout = applyShaperPositions(layout, this._overlayEl, this._text, this._fontSize, this._font, this._shaper, this._timeline);\n }\n this._layout = layout;\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 =\n tc.mode === 'uncontrolled' && this._playing && !!this._font && this._fontReady && this._shaperReady && !this._prefersReducedMotion;\n\n if (shouldRun) {\n this._startLoop();\n } else {\n this._stopLoop();\n }\n }\n\n private _startLoop(): void {\n if (this._rafId) return;\n this._lastTs = null;\n this._smoothedBoost = 0;\n this._rafId = requestAnimationFrame(this._tick);\n }\n\n private _stopLoop(): void {\n if (this._rafId) {\n cancelAnimationFrame(this._rafId);\n this._rafId = 0;\n }\n }\n\n private _tick = (ts: number): void => {\n if (this._destroyed) return;\n\n if (this._lastTs === null) this._lastTs = ts;\n const dtSec = (ts - this._lastTs) / 1000;\n this._lastTs = ts;\n\n const tc = this._timeControl;\n if (tc.mode !== 'uncontrolled') return;\n\n const loop = tc.loop ?? false;\n const totalDur = this._timeline.totalDuration;\n const durationOverride = tc.duration;\n const useDuration = durationOverride !== undefined && durationOverride > 0;\n\n if (totalDur === 0 || (!loop && this._internalTime >= totalDur)) {\n this._internalTime = totalDur;\n this._rafId = requestAnimationFrame(this._tick);\n return;\n }\n\n // --- Initial delay ---\n if (this._delayRemaining > 0) {\n this._delayRemaining = Math.max(0, this._delayRemaining - dtSec);\n this._rafId = requestAnimationFrame(this._tick);\n return;\n }\n\n // --- Loop gap (waiting between iterations) ---\n if (this._loopGapRemaining > 0) {\n this._loopGapRemaining = Math.max(0, this._loopGapRemaining - dtSec);\n if (this._loopGapRemaining <= 0) {\n this._internalTime = 0;\n this._prevCompleted = false;\n this._smoothedBoost = 0;\n }\n this._notifyTimeChange();\n this._render();\n this._updateCssProperties();\n this._rafId = requestAnimationFrame(this._tick);\n return;\n }\n\n // Compute effective speed. `duration` stretches the natural timeline to fit\n // a fixed wall-clock slot; otherwise use `speed` + optional `catchUp`.\n let effectiveSpeed: number;\n if (useDuration) {\n effectiveSpeed = totalDur / durationOverride;\n } else {\n const speed = tc.speed ?? 1;\n const catchUp = tc.catchUp ?? 0;\n effectiveSpeed = speed;\n if (catchUp > 0) {\n const remaining = Math.max(0, totalDur - this._internalTime);\n const excess = Math.max(0, remaining - 2);\n const targetBoost = catchUp * excess;\n const attackRate = 4;\n const releaseRate = loop ? 30 : 2;\n const rate = targetBoost > this._smoothedBoost ? attackRate : releaseRate;\n this._smoothedBoost += (targetBoost - this._smoothedBoost) * (1 - Math.exp(-rate * dtSec));\n effectiveSpeed = speed + this._smoothedBoost;\n }\n }\n\n let next = this._internalTime + dtSec * effectiveSpeed;\n if (next >= totalDur) {\n if (loop) {\n const loopGap = tc.loopGap ?? 0;\n if (loopGap > 0) {\n // Hold at the end and start the loop gap countdown\n next = totalDur;\n this._loopGapRemaining = loopGap;\n } else if (this._internalTime < totalDur) {\n // Render one frame at totalDur so every entry (including the\n // last fallback character) satisfies its reveal condition\n // before the animation wraps back to the start.\n next = totalDur;\n } else {\n next %= totalDur;\n }\n } else {\n next = totalDur;\n }\n this._smoothedBoost = 0;\n }\n this._internalTime = next;\n\n this._notifyTimeChange();\n this._checkCompletion();\n this._render();\n this._updateCssProperties();\n\n this._rafId = requestAnimationFrame(this._tick);\n };\n\n private _notifyTimeChange(): void {\n const tc = this._timeControl;\n if (tc.mode === 'uncontrolled' && tc.onTimeChange) {\n // Emit eased time so it matches what's drawn and what CSS variables expose.\n tc.onTimeChange(this.currentTime);\n }\n }\n\n private _checkCompletion(): void {\n const complete = this.isComplete;\n if (complete && !this._prevCompleted) {\n this._prevCompleted = true;\n this._onComplete?.();\n } else if (!complete) {\n this._prevCompleted = false;\n }\n }\n\n // =========================================================================\n // Internal: Canvas rendering\n // =========================================================================\n\n private _render(): void {\n const canvas = this._canvasEl;\n const font = this._font;\n const layout = this._layout;\n const fontSize = this._fontSize;\n\n const dpr = window.devicePixelRatio || 1;\n // Supersampling: draw into a backing canvas larger than the displayed CSS\n // size, then let the browser downsample. Improves antialiasing at a\n // quadratic cost in pixels filled.\n const pixelRatio = Math.max(this._quality?.pixelRatio ?? 1, 0);\n const effectiveDpr = dpr * pixelRatio;\n const w = canvas.offsetWidth;\n const h = canvas.offsetHeight;\n\n const needsResize = canvas.width !== Math.round(w * effectiveDpr) || canvas.height !== Math.round(h * effectiveDpr);\n if (needsResize) {\n canvas.width = Math.round(w * effectiveDpr);\n canvas.height = Math.round(h * effectiveDpr);\n }\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n ctx.setTransform(effectiveDpr, 0, 0, effectiveDpr, 0, 0);\n ctx.clearRect(0, 0, w, h);\n\n // Nothing to draw (e.g. empty text) — but the clear above still needs to\n // run so stale pixels from the previous render don't linger.\n if (!font?.glyphData || !layout || !fontSize) return;\n\n const padH = PADDING_H_EM * fontSize;\n const lineHeight = this._lineHeight;\n const padV = Math.max(MIN_PADDING_V_EM * fontSize, (MIN_LINE_HEIGHT_EM * fontSize - lineHeight) / 2);\n ctx.translate(padH, padV);\n\n const color = this._currentColor || 'black';\n const emHeight = (font.ascender - font.descender) / font.unitsPerEm;\n const emHeightPx = emHeight * fontSize;\n const halfLeading = (lineHeight - emHeightPx) / 2;\n const characters = graphemes(this._text);\n const currentTime = this.currentTime;\n\n // --- Subdivision cache setup ---\n // `maxSegLenFU` is the subdivision threshold in font units. It collapses\n // every input that matters (segmentSize in CSS px, fontSize, unitsPerEm,\n // whether any effect needs subdivision) into a single value, so the cache\n // key is just (font family, maxSegLenFU). When anything that affects\n // subdivision changes, the key changes and the WeakMap is swapped out.\n const effectsNeedSubdivision =\n !!findEffect(this._resolvedEffects, 'wobble') ||\n !!findEffect(this._resolvedEffects, 'strokeGradient') ||\n !!findEffect(this._resolvedEffects, 'taper') ||\n (() => {\n const p = findEffect(this._resolvedEffects, 'pressureWidth');\n return !!p && Math.max(0, Math.min(p.config.strength ?? 1, 1)) > 0;\n })();\n const smoothing = this._quality?.smoothing === true;\n const userSegmentSize = this._quality?.segmentSize;\n const resolvedSegmentSize = userSegmentSize ?? (effectsNeedSubdivision || smoothing ? 2 : undefined);\n const scale = fontSize / font.unitsPerEm;\n const maxSegLenFU = resolvedSegmentSize != null ? resolvedSegmentSize / scale : Infinity;\n const cacheKey = `${font.family}|${maxSegLenFU}|${smoothing ? 's' : 'l'}`;\n if (cacheKey !== this._strokeCacheKey) {\n this._strokeCache = new WeakMap();\n this._strokeCacheKey = cacheKey;\n }\n const strokeCache = this._strokeCache;\n const getSubdivided = (stroke: TegakiGlyphData['s'][number]): SubdividedStroke => {\n let sub = strokeCache.get(stroke);\n if (!sub) {\n sub = subdivideStroke(stroke, maxSegLenFU, smoothing);\n strokeCache.set(stroke, sub);\n }\n return sub;\n };\n\n const clipText = this._quality?.clipText;\n const strokeScale = typeof clipText === 'number' ? clipText : 1;\n\n // --- Render-stage hooks (pre) ---\n // Effects that span the whole layout (vs. per-stroke) can hook the\n // render pipeline here. The stage context is only computed when at\n // least one resolved effect declares a hook, so the common case pays\n // nothing.\n const runHooks = hasRenderHooks(this._resolvedEffects);\n const stage: RenderStageContext | null = runHooks\n ? {\n ctx,\n layout,\n fontSize,\n lineHeight,\n unitsPerEm: font.unitsPerEm,\n ascender: font.ascender,\n descender: font.descender,\n bbox: computeLayoutBbox(layout, fontSize, lineHeight),\n baseColor: color,\n seed: this._seed,\n }\n : null;\n\n if (stage) {\n for (const effect of this._resolvedEffects) {\n getEffectDefinition(effect.effect)?.beforeRender?.(stage, effect.config);\n }\n }\n\n // Map grapheme index -> line index so timeline entries (which reference\n // graphemes) can be placed without re-walking the lines array per entry.\n const graphemeToLine = new Int32Array(characters.length).fill(-1);\n for (let li = 0; li < layout.lines.length; li++) {\n const lineIndices = layout.lines[li]!;\n for (const charIdx of lineIndices) graphemeToLine[charIdx] = li;\n }\n\n for (let ei = 0; ei < this._timeline.entries.length; ei++) {\n const entry = this._timeline.entries[ei]!;\n if (entry.char === '\\n') continue;\n const charIdx = entry.graphemeIndex;\n const lineIdx = graphemeToLine[charIdx] ?? -1;\n if (lineIdx < 0) continue;\n const y = lineIdx * lineHeight;\n // Prefer per-entry GPOS-positioned offsets when the shaper populated\n // them (Arabic cursive attachment, mark positioning, contextual kerning).\n // The legacy per-grapheme `charOffsets` path is the fallback for the\n // unshaped char-keyed render or any shape glyph that lacks a matching\n // entry. `lineLefts[lineIdx]` anchors the entry-relative xOffsetEm to\n // the visual line's left edge measured from the DOM.\n const lineLeftEm = layout.lineLefts?.[lineIdx];\n const x =\n entry.xOffsetEm !== undefined && lineLeftEm !== undefined\n ? (lineLeftEm + entry.xOffsetEm) * fontSize\n : (layout.charOffsets[charIdx] ?? 0) * fontSize;\n const glyph = (entry.glyphId !== undefined ? font.glyphDataById?.[entry.glyphId] : undefined) ?? lookupGlyphData(font, entry.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 // Apply HB's GPOS y-offset (negated dy, in em y-down). Encodes Arabic\n // cursive lift and mark vertical placement; zero for scripts that\n // don't use vertical GPOS, so behaviour for Latin/Caveat is unchanged.\n const glyphY = y + halfLeading + (entry.yOffsetEm ?? 0) * fontSize;\n drawGlyph(\n ctx,\n glyph,\n {\n x,\n y: glyphY,\n fontSize,\n unitsPerEm: font.unitsPerEm,\n ascender: font.ascender,\n descender: font.descender,\n },\n localTime,\n font.lineCap,\n color,\n this._resolvedEffects,\n this._seed + charIdx,\n getSubdivided,\n this._timing?.strokeEasing,\n strokeScale,\n stage?.strokeStyle,\n entry.strokeDelays,\n entry.strokeTimeScale,\n );\n } else if (!entry.hasGlyph && currentTime >= entry.offset + entry.duration) {\n const baseline = y + halfLeading + (font.ascender / font.unitsPerEm) * fontSize;\n drawFallbackGlyph(ctx, entry.char, x, baseline, fontSize, cssFontFamily(font), color, this._resolvedEffects, this._seed + charIdx);\n }\n }\n\n // --- Render-stage hooks (post) ---\n // Reverse order so save/restore-style pairs nest correctly with their\n // `beforeRender` counterparts. Runs before the clipText mask so any\n // post-processing still gets constrained to the text shape.\n if (stage) {\n for (let i = this._resolvedEffects.length - 1; i >= 0; i--) {\n const effect = this._resolvedEffects[i]!;\n getEffectDefinition(effect.effect)?.afterRender?.(stage, effect.config);\n }\n }\n\n // --- Clip strokes to the filled text shape ---\n // All text characters are rendered onto a cached offscreen canvas so the\n // mask can be applied as a single destination-in drawImage call. Doing\n // fillText per-character with destination-in would erase previously-clipped\n // strokes.\n if (clipText) {\n if (!this._maskCanvas) this._maskCanvas = document.createElement('canvas');\n const maskCanvas = this._maskCanvas;\n if (maskCanvas.width !== canvas.width || maskCanvas.height !== canvas.height) {\n maskCanvas.width = canvas.width;\n maskCanvas.height = canvas.height;\n }\n const maskCtx = maskCanvas.getContext('2d')!;\n maskCtx.setTransform(effectiveDpr, 0, 0, effectiveDpr, 0, 0);\n maskCtx.clearRect(0, 0, w, h);\n maskCtx.translate(padH, padV);\n maskCtx.font = `${fontSize}px ${cssFontFamily(font)}`;\n maskCtx.textBaseline = 'alphabetic';\n // Fill each line as a single string so the browser's shaper sees the\n // full run — per-character fillText would drop ligatures, kerning, and\n // script-specific contextual forms (Arabic init/medi/fina, Indic\n // conjuncts, etc.), producing a mask that doesn't match the shaped\n // stroke positions.\n let clipY = 0;\n for (const lineIndices of layout.lines) {\n let lineText = '';\n for (const charIdx of lineIndices) {\n const char = characters[charIdx]!;\n if (char === '\\n') continue;\n lineText += char;\n }\n if (lineText) {\n const baseline = clipY + halfLeading + (font.ascender / font.unitsPerEm) * fontSize;\n maskCtx.fillText(lineText, 0, 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":";AA2DA,MAAM,iBAAsC,EAAE,eAAe,KAAK;AAElE,MAAM,eAAiD;CACrD,MAAM,CAAC;CACP,QAAQ,CAAC;CACT,eAAe,CAAC;CAChB,OAAO,CAAC;CACR,gBAAgB,CAAC;CACjB,gBAAgB,EACd,aAAa,OAAO,QAA+C;EACjE,MAAM,SAAS,OAAO;EACtB,IAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,GAAG;EACnD,MAAM,EAAE,KAAK,SAAS;EAMtB,MAAM,OAAQ,OAAO,SAAS,KAAK,KAAK,KAAM;EAC9C,MAAM,KAAK,KAAK,IAAI,GAAG;EACvB,MAAM,KAAK,KAAK,IAAI,GAAG;EACvB,MAAM,KAAK,KAAK,IAAI,KAAK,QAAQ;EACjC,MAAM,KAAK,KAAK,IAAI,KAAK,SAAS;EAClC,MAAM,QAAQ,KAAK,QAAQ;EAC3B,MAAM,QAAQ,KAAK,SAAS;EAC5B,MAAM,OAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK;EAEvD,MAAM,OAAO,IAAI,qBAAqB,KAAK,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,KAAK,IAAI;EACpG,IAAI,OAAO,WAAW,GAAG;GACvB,KAAK,aAAa,GAAG,OAAO,EAAG;GAC/B,KAAK,aAAa,GAAG,OAAO,EAAG;EACjC,OACE,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KACjC,KAAK,aAAa,KAAK,OAAO,SAAS,IAAI,OAAO,EAAG;EAGzD,MAAM,cAAc;CACtB,EACF;AACF;;;;;;AAOA,SAAgB,eAAe,SAA4D;CACzF,MAAM,SAAS;EAAE,GAAG;EAAgB,GAAG;CAAQ;CAE/C,MAAM,SAA2B,CAAC;CAElC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAAG;EACjD,IAAI,UAAU,SAAS,SAAS,MAAM;EAEtC,IAAI;EACJ,IAAI;EACJ,IAAI;EAEJ,IAAI,UAAU,MAAM;GAClB,aAAc,OAAO,OAAO,cAAc,GAAG,IAAI,MAAM,KAAA;GACvD,IAAI,CAAC,YAAY;GACjB,SAAS,CAAC;GACV,QAAQ;EACV,OAAO;GACL,IAAI,MAAM,YAAY,OAAO;GAC7B,aAAa,MAAM,WAAW,OAAO,OAAO,cAAc,GAAG,IAAI,MAAM,KAAA;GACvE,IAAI,CAAC,YAAY;GACjB,MAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,IAAI,GAAG,SAAS;GACtD,SAAS;GACT,QAAQ,KAAK;EACf;EAEA,OAAO,KAAK;GAAE,QAAQ;GAAY;GAAO;EAAO,CAAC;CACnD;CAEA,OAAO,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;CACvC,OAAO;AACT;;AAGA,SAAgB,WAAuC,SAA2B,MAAwC;CACxH,OAAO,QAAQ,MAAM,MAAM,EAAE,WAAW,IAAI;AAC9C;;AAGA,SAAgB,YAAwC,SAA2B,MAA8B;CAC/G,OAAO,QAAQ,QAAQ,MAAM,EAAE,WAAW,IAAI;AAChD;;AAGA,SAAgB,oBAAoB,MAA4C;CAC9E,OAAO,OAAO,OAAO,cAAc,IAAI,IAAI,aAAa,QAAQ,KAAA;AAClE;;AAGA,SAAgB,eAAe,SAAoC;CACjE,KAAK,MAAM,KAAK,SAAS;EACvB,MAAM,MAAM,oBAAoB,EAAE,MAAM;EACxC,IAAI,KAAK,gBAAgB,KAAK,aAAa,OAAO;CACpD;CACA,OAAO;AACT;;;;;;;;;;;;;;;;ACjIA,SAAgB,iBAAiB,IAAY,IAAY,IAAY,IAAY,OAA+B;CAC9G,MAAM,MAAM,KAAK,IAAI,KAAK,IAAI,EAAE,GAAG,IAAI;CACvC,MAAM,MAAM,KAAK,IAAI,KAAK,IAAI,EAAE,GAAG,IAAI;CACvC,MAAM,MAAM,KAAK,IAAI,KAAK,IAAI,EAAE,GAAG,IAAI;CACvC,MAAM,KAAK;CACX,MAAM,KAAK,KAAK,KAAK,KAAK,GAAG;CAC7B,MAAM,KAAK,KAAK,KAAK,KAAK,GAAG;CAC7B,MAAM,KAAK,KAAK,KAAK,KAAK,GAAG;CAE7B,MAAM,MAAsB,IAAI,MAAM,KAAK;CAC3C,KAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;EAE/B,MAAM,IAAI,KADA,IAAI,SACM,KAAK;EACzB,IAAI,IAAI,KAAK,iBAAiB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;CACjE;CACA,OAAO;AACT;;;;;;;AAQA,SAAgB,QAAQ,QAAgB,GAAmB;CACzD,OAAO;EAAC,IAAI,OAAO,KAAM,EAAE;EAAK,IAAI,OAAO,KAAM,EAAE;EAAK,OAAO;CAAG;AACpE;AAEA,SAAS,KAAK,GAAW,GAAmB;CAC1C,MAAM,KAAK,EAAE,KAAM,EAAE;CACrB,MAAM,KAAK,EAAE,KAAM,EAAE;CACrB,OAAO,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;AACpC;AAKA,SAAS,iBACP,IACA,IACA,IACA,IACA,IACA,IACA,IACA,IACA,GACc;CACd,MAAM,MAAM,KAAK,GAAG,IAAK,GAAG,IAAK,IAAI,IAAI,CAAC;CAC1C,MAAM,MAAM,KAAK,GAAG,IAAK,GAAG,IAAK,IAAI,IAAI,CAAC;CAC1C,MAAM,MAAM,KAAK,GAAG,IAAK,GAAG,IAAK,IAAI,IAAI,CAAC;CAC1C,MAAM,MAAM,KAAK,GAAG,IAAK,GAAG,IAAK,IAAI,IAAI,CAAC;CAC1C,MAAM,MAAM,KAAK,GAAG,IAAK,GAAG,IAAK,IAAI,IAAI,CAAC;CAC1C,MAAM,MAAM,KAAK,GAAG,IAAK,GAAG,IAAK,IAAI,IAAI,CAAC;CAC1C,MAAM,MAAM,KAAK,GAAG,IAAK,GAAG,IAAK,IAAI,IAAI,CAAC;CAC1C,MAAM,MAAM,KAAK,GAAG,IAAK,GAAG,IAAK,IAAI,IAAI,CAAC;CAC1C,MAAM,MAAM,KAAK,GAAG,IAAK,GAAG,IAAK,IAAI,IAAI,CAAC;CAC1C,MAAM,MAAM,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC;CACpC,MAAM,MAAM,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC;CACpC,MAAM,MAAM,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC;CACpC,MAAM,MAAM,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC;CACpC,MAAM,MAAM,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC;CACpC,MAAM,MAAM,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC;CACpC,OAAO;EACL,GAAG,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC;EAC3B,GAAG,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC;EAC3B,OAAO,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC;CACjC;AACF;AAEA,SAAS,KAAK,GAAW,GAAW,IAAY,IAAY,GAAmB;CAC7E,MAAM,OAAO,KAAK;CAClB,IAAI,SAAS,GAAG,OAAO;CACvB,OAAO,KAAK,IAAI,OAAO,IAAI,MAAM;AACnC;;;;;;;;;;;;;;;;;;ACzDA,SAAgB,gBAAgB,QAAgB,WAAmB,YAAY,OAAyB;CACtG,MAAM,MAAM,OAAO;CACnB,MAAM,IAAI,IAAI;CACd,IAAI,MAAM,GAAG,OAAO;EAAE,UAAU,CAAC;EAAG,UAAU;EAAG,UAAU;CAAE;CAE7D,MAAM,QAAQ,IAAI;CAClB,MAAM,WAAwB,CAAC;EAAE,GAAG,MAAM;EAAK,GAAG,MAAM;EAAK,OAAO,MAAM;EAAK,QAAQ;EAAG,KAAK;CAAE,CAAC;CAElG,IAAI,SAAS;CACb,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;EAC1B,MAAM,OAAO,IAAI,IAAI;EACrB,MAAM,MAAM,IAAI;EAChB,MAAM,KAAK,IAAI,KAAM,KAAK;EAC1B,MAAM,KAAK,IAAI,KAAM,KAAK;EAC1B,MAAM,WAAW,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;EAC5C,MAAM,QAAQ,WAAW,KAAK,OAAO,SAAS,SAAS,KAAK,YAAY,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK,WAAW,SAAS,CAAC,IAAI;EAE3H,IAAI,aAAa,QAAQ,GAAG;GAG1B,MAAM,UAAU,iBAFL,KAAK,IAAI,IAAI,IAAI,KAAM,QAAQ,MAAM,GAAG,GAEd,MAAM,KADhC,IAAI,IAAI,IAAI,IAAI,IAAI,KAAM,QAAQ,KAAK,IAAI,GACF,KAAK;GACzD,IAAI,KAAK,KAAK;GACd,IAAI,KAAK,KAAK;GACd,IAAI,WAAW;GACf,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;IAC9B,MAAM,IAAI,QAAQ;IAClB,MAAM,KAAK,EAAE,IAAI;IACjB,MAAM,KAAK,EAAE,IAAI;IACjB,YAAY,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;IACvC,SAAS,KAAK;KAAE,GAAG,EAAE;KAAG,GAAG,EAAE;KAAG,OAAO,EAAE;KAAO,QAAQ,SAAS;KAAU,KAAK,IAAI,KAAK,IAAI,KAAK;IAAM,CAAC;IACzG,KAAK,EAAE;IACP,KAAK,EAAE;GACT;GACA,UAAU;EACZ,OAAO;GACL,MAAM,KAAK,IAAI,KAAM,KAAK;GAC1B,KAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;IAC/B,MAAM,IAAI,IAAI;IACd,SAAS,KAAK;KACZ,GAAG,KAAK,KAAM,KAAK;KACnB,GAAG,KAAK,KAAM,KAAK;KACnB,OAAO,KAAK,KAAM,KAAK;KACvB,QAAQ,SAAS,WAAW;KAC5B,KAAK,IAAI,IAAI;IACf,CAAC;GACH;GACA,UAAU;EACZ;CACF;CAEA,IAAI,WAAW;CACf,KAAK,MAAM,KAAK,KAAK,YAAY,EAAE;CAEnC,OAAO;EAAE;EAAU,UAAU;EAAQ,UAAU,WAAW;CAAE;AAC9D;;;ACpGA,MAAM,YACJ,OAAO,SAAS,eAAe,OAAO,KAAK,cAAc,aAAa,IAAI,KAAK,UAAU,KAAA,GAAW,EAAE,aAAa,WAAW,CAAC,IAAI;;AAGrI,SAAgB,iBAAiB,OAAkB,UAA0B;CAC3E,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,OAAO,WAAW,KAAK,IAAI;AAC7B;AAEA,SAAgB,UAAU,MAAwB;CAChD,IAAI,WAAW,OAAO,MAAM,KAAK,UAAU,QAAQ,IAAI,IAAI,MAAM,EAAE,OAAO;CAK1E,OAAO,MAAM,KAAK,IAAI;AACxB;;;;;AAMA,SAAgB,cAAc,QAA8B;CAC1D,IAAI,OAAO,YAAY,OAAO,IAAI,OAAO,OAAO,MAAM,OAAO,WAAW;CACxE,OAAO,IAAI,OAAO,OAAO;AAC3B;;;;;;;;;;;;AAaA,SAAgB,gBAAgB,MAAoB,MAA2C;CAC7F,MAAM,SAAS,KAAK,UAAU;CAC9B,IAAI,UAAU,KAAK,UAAU,GAAG,OAAO;CACvC,MAAM,KAAK,KAAK,YAAY,CAAC;CAC7B,IAAI,OAAO,KAAA,GAAW,OAAO,KAAA;CAC7B,OAAO,KAAK,UAAU,OAAO,cAAc,EAAE;AAC/C;AAIA,SAAgB,eAAe,OAAwB;CACrD,IAAI,SAAS,QAAQ,OAAO,UAAU,WAAW,OAAO;CACxD,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU,OAAO,OAAO,KAAK;CAC/E,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,IAAI,cAAc,CAAC,CAAC,KAAK,EAAE;CAClE,OAAO;AACT;;;AChCA,SAAS,WAAW,OAAiD;CACnE,MAAM,IAAI,MAAM,QAAQ,KAAK,EAAE;CAC/B,IAAI,EAAE,WAAW,GACf,OAAO;EAAC,SAAS,EAAE,KAAM,EAAE,IAAK,EAAE;EAAG,SAAS,EAAE,KAAM,EAAE,IAAK,EAAE;EAAG,SAAS,EAAE,KAAM,EAAE,IAAK,EAAE;EAAG;CAAC;CAElG,IAAI,EAAE,WAAW,GACf,OAAO;EAAC,SAAS,EAAE,KAAM,EAAE,IAAK,EAAE;EAAG,SAAS,EAAE,KAAM,EAAE,IAAK,EAAE;EAAG,SAAS,EAAE,KAAM,EAAE,IAAK,EAAE;EAAG,SAAS,EAAE,KAAM,EAAE,IAAK,EAAE,IAAI;CAAG;CAElI,IAAI,EAAE,WAAW,GACf,OAAO;EAAC,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;EAAG,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;EAAG,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;EAAG,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,IAAI;CAAG;CAElI,OAAO;EAAC,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;EAAG,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;EAAG,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;EAAG;CAAC;AAClG;AAEA,SAAS,UAAU,GAAqC,GAAqC,GAAmB;CAC9G,MAAM,IAAI,KAAK,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC;CAC7C,MAAM,IAAI,KAAK,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC;CAC7C,MAAM,KAAK,KAAK,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC;CAC9C,MAAM,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAClC,IAAI,MAAM,GAAG,OAAO,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG;CACxC,OAAO,QAAQ,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,QAAQ,CAAC,EAAE;AAC/C;AAEA,SAAS,cAAc,UAAkB,QAAkB,MAAsB;CAC/E,IAAI,OAAO,WAAW,GAAG,OAAO;CAChC,IAAI,OAAO,WAAW,GAAG,OAAO,OAAO;CAEvC,MAAM,YADO,WAAW,OAAO,MAAO,IAAK,KAAK,KAC3B,OAAO,SAAS;CACrC,MAAM,IAAI,KAAK,IAAI,KAAK,MAAM,OAAO,GAAG,OAAO,SAAS,CAAC;CACzD,MAAM,OAAO,UAAU;CACvB,OAAO,UAAU,WAAW,OAAO,EAAG,GAAG,WAAW,OAAO,IAAI,EAAG,GAAG,IAAI;AAC3E;AAEA,SAAS,aAAa,UAAkB,YAAoB,WAAmB,MAAsB;CAEnG,OAAO,QADM,WAAW,MAAM,OAAO,SAAS,IAC5B,IAAI,WAAW,KAAK,UAAU;AAClD;AAIA,SAAS,KAAK,GAAmB;CAC/B,IAAI,IAAK,IAAI,aAAc;CAC3B,KAAM,MAAM,KAAM,KAAK;CACvB,KAAM,MAAM,KAAM,KAAK;CACvB,IAAK,MAAM,KAAM;CACjB,QAAQ,IAAI,cAAc;AAC5B;AAEA,SAAS,QAAQ,GAAW,MAAsB;CAChD,MAAM,IAAI,KAAK,MAAM,CAAC;CACtB,MAAM,IAAI,IAAI;CACd,MAAM,IAAI,IAAI,KAAK,IAAI,IAAI;CAC3B,OAAO,KAAK,IAAI,OAAO,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,IAAI,IAAI;AACvE;;AAGA,SAAS,oBAAoB,GAAmB;CAC9C,OAAO,KAAK,IAAI,MAAM,IAAI;AAC5B;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,UACd,KACA,OACA,KACA,WACA,SACA,OACA,UAA4B,CAAC,GAC7B,OAAO,GACP,eACA,eAAoD,qBACpD,cAAc,GACd,qBACA,cACA,kBAAkB,GAClB;CAMA,MAAM,qBAA8D,uBAAuB;CAC3F,MAAM,QAAQ,IAAI,WAAW,IAAI;CACjC,MAAM,KAAK,IAAI;CACf,MAAM,KAAK,IAAI;CAEf,MAAM,cAAc,YAAY,SAAS,MAAM;CAC/C,MAAM,eAAe,WAAW,SAAS,QAAQ;CACjD,MAAM,iBAAiB,WAAW,SAAS,eAAe;CAC1D,MAAM,cAAc,WAAW,SAAS,OAAO;CAC/C,MAAM,uBAAuB,WAAW,SAAS,gBAAgB;CAGjE,MAAM,iBAAiB,iBAAiB,KAAK,IAAI,GAAG,KAAK,IAAI,eAAe,OAAO,YAAY,GAAG,CAAC,CAAC,IAAI;CAGxG,MAAM,kBAAkB,eAAgB,aAAa,OAAO,aAAa,MAAO;CAChF,MAAM,kBAAkB,eAAgB,aAAa,OAAO,aAAa,IAAK;CAC9E,MAAM,aAAa,cAAc,OAAO,QAAQ;CAChD,MAAM,YAAY,CAAC,CAAC;CAGpB,MAAM,aAAa,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,YAAY,OAAO,eAAe,KAAM,CAAC,CAAC,IAAI;CACpG,MAAM,WAAW,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,YAAY,OAAO,aAAa,KAAM,CAAC,CAAC,IAAI;CAGhG,MAAM,iBAAiB,sBAAsB,OAAO;CACpD,MAAM,YAAY,mBAAmB;CACrC,MAAM,qBAAqB,MAAM,QAAQ,cAAc,IAAI,iBAAiB,KAAA;CAC5E,MAAM,qBAAqB,sBAAsB,OAAO,cAAc;CACtE,MAAM,oBAAoB,sBAAsB,OAAO,aAAa;CACpE,MAAM,oBAAoB,CAAC,CAAC;CAK5B,MAAM,kBAAkB,iBAAiB,KAAK,CAAC,CAAC;CAIhD,MAAM,YAAY,mBAAmB,MAAc,gBAAgB,GAAG,QAAQ;CAM9E,MAAM,YAAY,IAAY,GAAW,QAAwB;EAC/D,IAAI,CAAC,WAAW,OAAO;EACvB,IAAI,eAAe,SAAS,OAAO,mBAAmB,QAAQ,IAAI,KAAM,MAAM,IAAK,IAAI,IAAI,IAAI;EAC/F,OAAO,kBAAkB,KAAK,IAAI,mBAAmB,IAAI,MAAO,MAAM,MAAO,IAAI;CACnF;CACA,MAAM,YAAY,GAAW,IAAY,QAAwB;EAC/D,IAAI,CAAC,WAAW,OAAO;EACvB,IAAI,eAAe,SAAS,OAAO,mBAAmB,QAAQ,IAAI,KAAM,MAAM,IAAK,OAAO,MAAM,GAAI,IAAI,IAAI;EAC5G,OAAO,kBAAkB,KAAK,IAAI,mBAAmB,IAAI,MAAO,MAAM,MAAO,OAAO,GAAG;CACzF;CAGA,MAAM,MAAM,MAAc,KAAK,IAAI;CACnC,MAAM,MAAM,MAAc,MAAM,IAAI,IAAI,YAAY;CAGpD,MAAM,WAAW,aAA6B;EAC5C,IAAI,WAAW,OAAO,aAAa,UAAU,oBAAoB,mBAAmB,IAAI;EACxF,IAAI,oBAAoB,OAAO,cAAc,UAAU,oBAAoB,IAAI;EAC/E,OAAO;CACT;CAGA,MAAM,mBAAmB,aAA6B;EACpD,IAAI,IAAI;EACR,IAAI,aAAa,KAAK,WAAW,YAAY,IAAI,KAAK,IAAI,GAAG,WAAW,UAAU;EAClF,IAAI,WAAW,KAAK,WAAW,IAAI,UAAU,IAAI,KAAK,IAAI,IAAI,IAAI,YAAY,QAAQ;EACtF,OAAO;CACT;CAEA,KAAK,IAAI,KAAK,GAAG,KAAK,MAAM,EAAE,QAAQ,MAAM;EAC1C,MAAM,SAAS,MAAM,EAAE;EAIvB,MAAM,QAAQ,eAAe,OAAO,OAAO,IAAI;EAC/C,IAAI,YAAY,OAAO;EACvB,MAAM,UAAU,YAAY;EAC5B,MAAM,eAAe,OAAO,IAAI;EAChC,MAAM,iBAAiB,eAAe,IAAI,KAAK,IAAI,UAAU,cAAc,CAAC,IAAI;EAChF,MAAM,WAAW,eAAe,aAAa,cAAc,IAAI;EAE/D,MAAM,SAAS,OAAO;EACtB,IAAI,OAAO,WAAW,GAAG;EAOzB,MAAM,eAAe,OAAO,SAAS,KAAK,OAAO,OAAO,MAAM,EAAE,OAAO,OAAO,EAAE,CAAE,MAAM,EAAE,OAAO,OAAO,EAAE,CAAE,EAAE;EAG9G,IAAI,OAAO,WAAW,KAAK,cAAc;GACvC,IAAI,YAAY,GAAG;GACnB,MAAM,IAAI,OAAO;GACjB,MAAM,OAAO,GAAG,EAAE,KAAM,SAAS,EAAE,IAAK,EAAE,IAAK,CAAC,CAAC;GACjD,MAAM,OAAO,GAAG,EAAE,KAAM,SAAS,EAAE,IAAK,EAAE,IAAK,CAAC,CAAC;GACjD,MAAM,gBAAgB,KAAK,IAAI,EAAE,IAAK,EAAG,IAAI,QAAQ;GAErD,IAAI,WAAW,iBADK,KAAK,IAAI,EAAE,IAAK,EAAG,IAAI,QAAQ,cACL,iBAAiB;GAC/D,YAAY,gBAAgB,EAAG;GAG/B,KAAK,MAAM,QAAQ,aAAa;IAC9B,IAAI,KAAK;IACT,IAAI,aAAa,iBAAiB,KAAK,OAAO,UAAU,GAAG,IAAI,QAAQ;IACvE,IAAI,cAAc,KAAK,OAAO,SAAS;IACvC,IAAI,iBAAiB,KAAK,OAAO,WAAW,KAAK;IACjD,IAAI,iBAAiB,KAAK,OAAO,WAAW,KAAK;IACjD,IAAI,YAAY,KAAK,OAAO,SAAS;IACrC,IAAI,UAAU;IACd,IAAI,YAAY,SACd,IAAI,IAAI,MAAM,MAAM,WAAW,GAAG,GAAG,KAAK,KAAK,CAAC;SAEhD,IAAI,KAAK,OAAO,WAAW,GAAG,OAAO,WAAW,GAAG,UAAU,QAAQ;IAEvE,IAAI,KAAK;IACT,IAAI,QAAQ;GACd;GAKA,IAAI,YAAY,oBAAoB,QAAQ,CAAC,IAAI;GACjD,IAAI,UAAU;GACd,IAAI,YAAY,SAAS;IACvB,IAAI,IAAI,MAAM,MAAM,WAAW,GAAG,GAAG,KAAK,KAAK,CAAC;IAChD,IAAI,KAAK;GACX,OACE,IAAI,SAAS,OAAO,WAAW,GAAG,OAAO,WAAW,GAAG,UAAU,QAAQ;GAE3E;EACF;EAIA,MAAM,EAAE,UAAU,UAAU,aADb,UAAU,MACqB;EAC9C,IAAI,SAAS,SAAS,KAAK,YAAY,GAAG;EAE1C,MAAM,UAAU,WAAW;EAC3B,IAAI,WAAW,GAAG;EAElB,MAAM,gBAAgB,KAAK,IAAI,UAAU,EAAG,IAAI,QAAQ;EAIxD,IAAI,KAAK;EACT,IAAI,KAAK,SAAS,SAAS;EAC3B,OAAO,KAAK,IAAI;GACd,MAAM,MAAO,KAAK,KAAK,MAAO;GAC9B,IAAI,SAAS,IAAI,CAAE,UAAU,SAAS,KAAK;QACtC,KAAK,MAAM;EAClB;EACA,MAAM,UAAU;EAGhB,IAAI,QAAQ;EACZ,IAAI,QAAQ;EACZ,IAAI,YAAY;EAChB,IAAI,UAAU;EACd,IAAI,aAAa;EACjB,IAAI,UAAU;EACd,IAAI,UAAU,IAAI,SAAS,UAAU,UAAU,SAAS,QAAQ,CAAE,QAAQ;GACxE,MAAM,IAAI,SAAS;GACnB,MAAM,IAAI,SAAS,UAAU;GAC7B,MAAM,SAAS,EAAE,SAAS,EAAE;GAC5B,MAAM,IAAI,SAAS,KAAK,UAAU,EAAE,UAAU,SAAS;GACvD,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;GAC5B,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;GAC5B,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS;GAC5C,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO;GACpC,aAAa;GACb,UAAU;EACZ;EAKA,MAAM,SAAS,UAAU,KAAK,UAAU,IAAI;EAC5C,MAAM,MAAgB,IAAI,MAAM,MAAM;EACtC,MAAM,MAAgB,IAAI,MAAM,MAAM;EACtC,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,KAAK;GACjC,MAAM,IAAI,SAAS;GACnB,IAAI,KAAK,GAAG,EAAE,IAAI,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;GAC3C,IAAI,KAAK,GAAG,EAAE,IAAI,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;EAC7C;EACA,IAAI,SAAS;GACX,IAAI,SAAS,KAAK,GAAG,QAAQ,SAAS,OAAO,OAAO,OAAO,CAAC;GAC5D,IAAI,SAAS,KAAK,GAAG,QAAQ,SAAS,OAAO,OAAO,OAAO,CAAC;EAC9D;EAEA,IAAI,UAAU;EACd,IAAI,WAAW;EAMf,MAAM,sBAAsB;GAC1B,IAAI,UAAU;GACd,IAAI,OAAO,IAAI,IAAK,IAAI,EAAG;GAC3B,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK,IAAI,OAAO,IAAI,IAAK,IAAI,EAAG;EAC9D;EAGA,KAAK,MAAM,QAAQ,aAAa;GAC9B,IAAI,KAAK;GACT,IAAI,aAAa,iBAAiB,KAAK,OAAO,UAAU,GAAG,IAAI,QAAQ;GACvE,IAAI,cAAc,KAAK,OAAO,SAAS;GACvC,IAAI,iBAAiB,KAAK,OAAO,WAAW,KAAK;GACjD,IAAI,iBAAiB,KAAK,OAAO,WAAW,KAAK;GACjD,IAAI,cAAc,KAAK,OAAO,SAAS;GACvC,IAAI,YAAY;GAChB,cAAc;GACd,IAAI,OAAO;GACX,IAAI,QAAQ;EACd;EAGA,IAAI,CAAC,mBAAmB,CAAC,mBAAmB;GAE1C,IAAI,cAAc;GAClB,IAAI,YAAY;GAChB,cAAc;GACd,IAAI,OAAO;EACb,OAAO;GAIL,MAAM,cAAc,IAAI;GACxB,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK;IAC/B,MAAM,OAAO,IAAI,KAAK,UAAU,SAAS,IAAI,EAAE,CAAE,SAAS;IAC1D,MAAM,OAAO,KAAK,UAAU,SAAS,EAAE,CAAE,SAAS;IAClD,MAAM,SAAS,IAAI,KAAK,UAAU,SAAS,IAAI,EAAE,CAAE,QAAQ;IAC3D,MAAM,SAAS,KAAK,UAAU,SAAS,EAAE,CAAE,QAAQ;IACnD,MAAM,eAAe,OAAO,QAAQ,KAAM;IAE1C,IAAI,KAAK;IACT,IAAI,iBAAiB;KACnB,MAAM,YAAY,SAAS,UAAU,KAAM,QAAQ;KAEnD,KADU,KAAK,IAAI,iBAAiB,WAAW,iBAAiB,gBAAgB,KAAM,QAAQ,WACzF,IAAI,gBAAgB,WAAW;IACtC;IACA,IAAI,YAAY;IAChB,IAAI,cAAc,oBAAoB,QAAQ,WAAW,IAAI;IAC7D,IAAI,UAAU;IACd,IAAI,OAAO,IAAI,IAAI,IAAK,IAAI,IAAI,EAAG;IACnC,IAAI,OAAO,IAAI,IAAK,IAAI,EAAG;IAC3B,IAAI,OAAO;GACb;EACF;CACF;AACF;;;;;;;;;;;;;;ACpXA,MAAM,0CAA0B,IAAI,IAAI;CAAC;CAAQ;CAAQ;CAAQ;CAAQ;AAAM,CAAC;;;;;;;;;;AAWhF,SAAgB,qBAAqB,SAAoC;CACvE,IAAI,QAAQ,WAAW,GAAG,OAAO;CACjC,MAAM,WAAW,QAAQ,QAAQ,QAAQ,CAAC,wBAAwB,IAAI,GAAG,CAAC;CAC1E,IAAI,SAAS,WAAW,GAAG,OAAO;CAClC,OAAO,SAAS,KAAK,MAAM,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI;AAClD;;;ACxBA,MAAM,gCAAgB,IAAI,IAA2B;AACrD,MAAM,+BAAe,IAAI,IAAY;;;;;AAMrC,eAAsB,eAAe,QAAqC;CACxE,MAAM,WAAW,OAAO,QAAQ,OAAO,SAAS,OAAO,UAAU,OAAO,aAAa;AACvF;AAEA,SAAgB,WAAW,QAAgB,KAAa,UAA8B,eAAgD;CACpI,IAAI,OAAO,aAAa,aAAa,OAAO,QAAQ,QAAQ;CAI5D,MAAM,OAAO,CAAC,KAAK,GAAI,iBAAiB,CAAC,CAAE;CAC3C,IAAI,KAAK,OAAO,MAAM,aAAa,IAAI,CAAC,CAAC,GAAG,OAAO;CAOnD,MAAM,kBAAkB,qBAAqB,YAAY,CAAC,CAAC;CAC3D,MAAM,UAAU,KAAK,KAAK,MAAM;EAC9B,IAAI,SAAS,cAAc,IAAI,CAAC;EAChC,IAAI,CAAC,QAAQ;GACX,SAAS,IAAI,SAAS,QAAQ,OAAO,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,WAAW;IACtF,SAAS,MAAM,IAAI,MAAM;IACzB,aAAa,IAAI,CAAC;GACpB,CAAC;GACD,cAAc,IAAI,GAAG,MAAM;EAC7B;EACA,OAAO;CACT,CAAC;CACD,OAAO,QAAQ,IAAI,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC;AAC3C;;;AChCA,MAAM,cAAc;;;;;;AAoCpB,SAAgB,kBAAkB,QAAoB,UAAkB,YAAgC;CACtG,IAAI,WAAW;CACf,KAAK,MAAM,eAAe,OAAO,OAC/B,KAAK,MAAM,WAAW,aAAa;EAGjC,MAAM,UAFS,OAAO,YAAY,YAAY,MAChC,OAAO,WAAW,YAAY,MACX;EACjC,IAAI,QAAQ,UAAU,WAAW;CACnC;CAEF,OAAO;EACL,GAAG;EACH,GAAG;EACH,OAAO;EACP,QAAQ,OAAO,MAAM,SAAS;CAChC;AACF;AAYA,SAAgB,kBACd,UACA,UACA,YACA,YACA,UACY;CACZ,IAAI,OAAO,aAAa,UACtB,OAAO,uBAAuB,UAAU,YAAa,UAAU,YAAa,QAAS;CAEvF,OAAO,eAAe,UAAU,QAAQ;AAC1C;AAEA,SAAS,eAAe,IAAiB,UAA8B;CACrE,MAAM,WAAW,GAAG;CACpB,IAAI,CAAC,YAAY,SAAS,aAAa,KAAK,WAC1C,OAAO;EAAE,OAAO,CAAC;EAAG,aAAa,CAAC;EAAG,YAAY,CAAC;CAAE;CAItD,MAAM,QAAQ,UADD,SAAS,eAAe,EACT;CAC5B,IAAI,CAAC,MAAM,QAAQ,OAAO;EAAE,OAAO,CAAC;EAAG,aAAa,CAAC;EAAG,YAAY,CAAC;CAAE;CAKvE,MAAM,SAAS,GAAG,sBAAsB;CACxC,MAAM,SAAS,OAAO;CAKtB,MAAM,QAAQ,GAAG,cAAc,IAAI,OAAO,QAAQ,GAAG,cAAc;CACnE,MAAM,QAAQ,SAAS,YAAY;CAEnC,MAAM,cAAwB,CAAC;CAC/B,MAAM,aAAuB,CAAC;CAC9B,MAAM,QAAoB,CAAC;CAC3B,IAAI,cAAwB,CAAC;CAC7B,IAAI,UAAU;CACd,IAAI,cAAc;CAElB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,OAAO,MAAM;EAEnB,IAAI,SAAS,MAAM;GACjB,YAAY,KAAK,CAAC;GAClB,WAAW,KAAK,CAAC;GACjB,YAAY,KAAK,CAAC;GAClB,MAAM,KAAK,WAAW;GACtB,cAAc,CAAC;GACf,UAAU;GACV,eAAe,KAAK;GACpB;EACF;EAEA,MAAM,SAAS,UAAU,WAAW;EACpC,MAAM,OAAO,UAAU,cAAc,KAAK,MAAM;EAChD,MAAM,QAAQ,MAAM,eAAe;EACnC,eAAe,KAAK;EAEpB,IAAI,MAAM,WAAW,GAAG;GACtB,YAAY,KAAK,CAAC;GAClB,WAAW,KAAK,CAAC;GACjB,YAAY,KAAK,CAAC;GAClB;EACF;EAEA,MAAM,OAAO,MAAM,MAAM,SAAS;EAIlC,IAAI,YAAY,SAAS,KAAK,KAAK,MAAM,UAAU,WAAW,MAAO,OAAO;GAC1E,MAAM,KAAK,WAAW;GACtB,cAAc,CAAC;EACjB;EAEA,IAAI,YAAY,WAAW,GACzB,UAAU,KAAK;EAGjB,YAAY,MAAM,KAAK,OAAO,UAAU,QAAQ,QAAQ;EACxD,WAAW,KAAK,KAAK,QAAQ,QAAQ,QAAQ;EAC7C,YAAY,KAAK,CAAC;CACpB;CACA,IAAI,YAAY,SAAS,GAAG,MAAM,KAAK,WAAW;CAElD,OAAO;EAAE;EAAO;EAAa;CAAW;AAC1C;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,qBACd,QACA,IACA,MACA,UACA,MACA,QACA,UACY;CACZ,MAAM,QAAQ,UAAU,IAAI;CAC5B,IAAI,CAAC,MAAM,QAAQ,OAAO;CAE1B,MAAM,WAAW,GAAG;CACpB,IAAI,CAAC,YAAY,SAAS,aAAa,KAAK,WAAW,OAAO;CAC9D,MAAM,SAAS,GAAG,sBAAsB;CACxC,MAAM,SAAS,OAAO;CACtB,MAAM,QAAQ,GAAG,cAAc,IAAI,OAAO,QAAQ,GAAG,cAAc;CACnE,MAAM,QAAQ,SAAS,YAAY;CAGnC,MAAM,iBAA2B,CAAC;CAClC;EACE,IAAI,IAAI;EACR,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,eAAe,KAAK,CAAC;GACrB,KAAK,MAAM,EAAE,CAAE;EACjB;CACF;CACA,MAAM,kBAAkB,IAAI,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE;CAC/D,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,gBAAgB,eAAe,MAAO;CAC7E,gBAAgB,KAAK,UAAU,MAAM;CAErC,MAAM,cAAc,OAAO,YAAY,MAAM;CAC7C,MAAM,aAAa,OAAO,WAAW,MAAM;CAC3C,MAAM,YAAsB,IAAI,MAAM,OAAO,MAAM,MAAM,CAAC,CAAC,KAAK,CAAC;CACjE,MAAM,YAAY,IAAI,KAAK;CAM3B,MAAM,6BAAa,IAAI,IAAsB;CAC7C,IAAI,UACF,KAAK,IAAI,KAAK,GAAG,KAAK,SAAS,QAAQ,QAAQ,MAAM;EACnD,MAAM,IAAI,SAAS,QAAQ;EAC3B,IAAI,EAAE,YAAY,KAAA,GAAW;EAC7B,MAAM,MAAM,GAAG,EAAE,cAAc,GAAG,EAAE;EACpC,MAAM,OAAO,WAAW,IAAI,GAAG;EAC/B,IAAI,MAAM,KAAK,KAAK,EAAE;OACjB,WAAW,IAAI,KAAK,CAAC,EAAE,CAAC;CAC/B;CAGF,KAAK,IAAI,KAAK,GAAG,KAAK,OAAO,MAAM,QAAQ,MAAM;EAE/C,MAAM,cADc,OAAO,MAAM,GACF,CAAC,QAAQ,QAAQ,MAAM,SAAS,IAAI;EACnE,IAAI,YAAY,WAAW,GAAG;EAE9B,MAAM,aAAa,eAAe,YAAY;EAC9C,MAAM,WAAW,YAAY,YAAY,SAAS;EAClD,MAAM,WAAW,eAAe,YAAa,MAAM,SAAS,CAAE;EAK9D,MAAM,SAAS,UAAU,UAAU;EACnC,MAAM,OAAO,UAAU,QAAQ;EAC/B,MAAM,YAAY,MAAM,eAAe;EACvC,IAAI,UAAU,WAAW,GAAG;EAC5B,IAAI,aAAa;EACjB,KAAK,MAAM,KAAK,WAAW,IAAI,EAAE,OAAO,YAAY,aAAa,EAAE;EACnE,MAAM,cAAc,aAAa,UAAU,QAAQ;EACnD,UAAU,MAAM;EAWhB,MAAM,WAAW,KAAK,MAAM,YAAY,QAAQ;EAChD,MAAM,UAAU,YAAY,KAAK,QAAQ;EACzC,MAAM,SAAS,OAAO,MAAM,QAAQ;EACpC,IAAI,OAAO,WAAW,GAAG;EACzB,MAAM,eAAe,UAAU,eAAe,MAAM,IAAI;EASxD,MAAM,8BAAc,IAAI,IAAoB;EAC5C,MAAM,iCAAiB,IAAI,IAAoB;EAC/C,IAAI,QAAQ;EACZ,IAAI,SAAS;EACb,KAAK,MAAM,KAAK,cAAc;GAC5B,MAAM,OAAO,EAAE,KAAK;GACpB,MAAM,OAAO,EAAE,KAAK;GACpB,MAAM,OAAO,EAAE,KAAK;GACpB,MAAM,OAAO,EAAE,KAAK;GACpB,MAAM,WAAW,QAAQ;GACzB,MAAM,WAAW,SAAS;GAC1B,IAAI,CAAC,YAAY,IAAI,EAAE,EAAE,GAAG,YAAY,IAAI,EAAE,IAAI,QAAQ;GAC1D,eAAe,IAAI,EAAE,KAAK,eAAe,IAAI,EAAE,EAAE,KAAK,KAAK,IAAI;GAE/D,IAAI,UAAU;IACZ,MAAM,OAAO,gBAAgB,aAAa,EAAE;IAC5C,IAAI,SAAS,KAAA,KAAa,QAAQ,GAAG;KAEnC,MAAM,KADQ,WAAW,IAAI,GAAG,KAAK,GAAG,EAAE,GAC3B,CAAC,EAAE,MAAM;KACxB,IAAI,OAAO,KAAA,GAAW;MACpB,MAAM,QAAQ,SAAS,QAAQ;MAC/B,MAAM,YAAY;MAClB,MAAM,YAAY;KACpB;IACF;GACF;GAEA,SAAS;GACT,UAAU;EACZ;EAGA,MAAM,2BAAW,IAAI,IAAY;EACjC,KAAK,MAAM,CAAC,IAAI,WAAW,aAAa;GACtC,MAAM,OAAO,gBAAgB,aAAa;GAC1C,IAAI,SAAS,KAAA,KAAa,OAAO,GAAG;GACpC,YAAY,QAAQ,aAAa;GACjC,WAAW,QAAQ,eAAe,IAAI,EAAE,KAAK;GAC7C,SAAS,IAAI,IAAI;EACnB;EAKA,MAAM,YAAY,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;EAC9D,KAAK,MAAM,OAAO,aAAa;GAC7B,IAAI,SAAS,IAAI,GAAG,GAAG;GACvB,MAAM,IAAI,eAAe,OAAQ;GACjC,IAAI,SAAS;GACb,KAAK,MAAM,MAAM,WACf,IAAI,MAAM,GAAG,SAAS;QACjB;GAEP,IAAI,SAAS,GAAG;GAChB,YAAY,OAAO,cAAc,YAAY,IAAI,MAAM,KAAK;GAC5D,WAAW,OAAO;EACpB;CACF;CAEA,OAAO;EAAE,OAAO,OAAO;EAAO;EAAa;EAAY;CAAU;AACnE;;;;;;;;AASA,SAAS,eAAe,QAAsC;CAC5D,MAAM,OAAwB,CAAC;CAC/B,IAAI,MAAqB,CAAC;CAC1B,KAAK,MAAM,KAAK,QAAQ;EACtB,IAAI,IAAI,UAAU,EAAE,KAAK,IAAI,IAAI,SAAS,EAAE,CAAE,IAAI;GAChD,KAAK,KAAK,GAAG;GACb,MAAM,CAAC;EACT;EACA,IAAI,KAAK,CAAC;CACZ;CACA,IAAI,IAAI,QAAQ,KAAK,KAAK,GAAG;CAC7B,MAAM,MAAqB,CAAC;CAC5B,KAAK,IAAI,IAAI,KAAK,SAAS,GAAG,KAAK,GAAG,KAAK,IAAI,KAAK,GAAG,KAAK,EAAG;CAC/D,OAAO;AACT;AAEA,SAAS,uBAAuB,MAAc,YAAoB,UAAkB,YAAoB,UAA8B;CACpI,MAAM,KAAK,SAAS,cAAc,KAAK;CACvC,GAAG,MAAM,WAAW;CACpB,GAAG,MAAM,OAAO;CAChB,GAAG,MAAM,MAAM;CACf,GAAG,MAAM,aAAa;CACtB,GAAG,MAAM,aAAa;CACtB,GAAG,MAAM,WAAW,GAAG,SAAS;CAChC,GAAG,MAAM,aAAa,GAAG,WAAW;CACpC,GAAG,MAAM,aAAa;CACtB,GAAG,MAAM,eAAe;CACxB,GAAG,MAAM,QAAQ,GAAG,SAAS;CAC7B,GAAG,cAAc;CACjB,SAAS,KAAK,YAAY,EAAE;CAE5B,MAAM,SAAS,eAAe,IAAI,QAAQ;CAE1C,SAAS,KAAK,YAAY,EAAE;CAC5B,OAAO;AACT;;;ACzTA,MAAM,WAAW;CACf,UAAU;CACV,SAAS;CACT,SAAS;CACT,iBAAiB;CACjB,WAAW;AACb;AA6DA,SAAgB,gBAAgB,MAAc,MAAoB,QAAyB,QAAwC;CACjI,IAAI,UAAU,KAAK,eACjB,OAAO,sBAAsB,MAAM,MAAM,QAAQ,MAAM;CAEzD,OAAO,wBAAwB,MAAM,MAAM,MAAM;AACnD;;AAUA,SAAS,eAAe,SAAgC,aAA6B;CACnF,IAAI,OAAO,YAAY,UAAU,OAAO,KAAK,IAAI,GAAG,OAAO;CAE3D,MAAM,IAAI,0BAA0B,KAAK,OAAO;CAChD,IAAI,GAAG;EACL,MAAM,MAAM,OAAO,EAAE,EAAE,IAAI;EAC3B,OAAO,KAAK,IAAI,GAAG,MAAM,WAAW;CACtC;CAEA,MAAM,IAAI,OAAO,OAAO;CACxB,OAAO,OAAO,SAAS,CAAC,IAAI,KAAK,IAAI,GAAG,CAAC,IAAI;AAC/C;AAEA,IAAM,mBAAN,MAAuB;CAeF;CACA;CACA;CACA;CAjBnB,UAAoC,CAAC;CACrC,SAAiB;;;;;;;CAOjB,gBAAwB;CACxB,UAAkB;;CAElB,aAAqB;CAErB,YACE,SACA,SACA,SACA,gBACA;EAJiB,KAAA,UAAA;EACA,KAAA,UAAA;EACA,KAAA,UAAA;EACA,KAAA,iBAAA;CAChB;CAEH,SAAS,QAAqB,iBAA+B;EAC3D,IAAI,KAAK,SAAS;GAChB,KAAK,UAAU,eAAe,KAAK,SAAS,KAAK,aAAa,IAAI,KAAK;GACvE,KAAK,aAAa;EACpB;EACA,MAAM,WAAW,KAAK,kBAAkB;EAGxC,MAAM,kBAAkB,kBAAkB,IAAI,WAAW,kBAAkB;EAC3E,KAAK,QAAQ,KAAK;GAChB,GAAG;GACH,QAAQ,KAAK;GACb;GACA,GAAI,oBAAoB,IAAI,EAAE,gBAAgB,IAAI,CAAC;EACrD,CAAC;EACD,KAAK,gBAAgB;EACrB,KAAK,UAAU;CACjB;;CAGA,UAAU,QAA2B;EACnC,KAAK,QAAQ,KAAK;GAAE,GAAG;GAAQ,QAAQ,KAAK;GAAQ,UAAU;EAAE,CAAC;CACnE;CAEA,UAAU,KAAsB;EAC9B,KAAK,cAAc,QAAQ,SAAS,KAAK,UAAU,KAAK;CAC1D;CAEA,WAAqB;EAKnB,IAAI,QAAQ;EACZ,KAAK,MAAM,KAAK,KAAK,SAAS;GAC5B,MAAM,MAAM,EAAE,SAAS,EAAE;GACzB,IAAI,MAAM,OAAO,QAAQ;EAC3B;EACA,OAAO;GAAE,SAAS,KAAK;GAAS,eAAe;EAAM;CACvD;AACF;;AAyBA,SAAS,eAAe,OAAwB,eAAuB,WAAoB;CACzF,MAAM,UAAU,MAAM;CACtB,IAAI,eAAe;CACnB,IAAI,UAAU;CACd,IAAI,YAAY;CAChB,MAAM,aAAuB,CAAC;CAC9B,MAAM,YAAsB,CAAC;CAC7B,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,IAAI,QAAQ;EAClB,MAAM,MAAM,EAAE,IAAI,EAAE;EACpB,IAAI,cAAc,EAAE,KAAK,KAAK,GAAG;GAC/B,WAAW,KAAK,CAAC;GACjB,UAAU,KAAK,EAAE,CAAC;GAClB,IAAI,EAAE,IAAI,SAAS,UAAU,EAAE;GAC/B,IAAI,MAAM,WAAW,YAAY;EACnC,OAAO,IAAI,MAAM,cACf,eAAe;CAEnB;CACA,IAAI,WAAW,WAAW,GACxB,OAAO;EAAE,cAAc,MAAM,KAAK;EAAe,aAAa;EAAG,SAAS;EAAG;EAAY;CAAU;CAIrG,IAAI,iBAAiB,GACnB,OAAO;EAAE,cAAc,MAAM,KAAK;EAAe,aAAa;EAAG,SAAS;EAAG,YAAY,CAAC;EAAG,WAAW,CAAC;CAAE;CAE7G,OAAO;EAAE;EAAc,aAAa,YAAY;EAAS;EAAS;EAAY;CAAU;AAC1F;AAEA,IAAM,YAAN,MAAgB;CAQK;CACA;CACA;CATnB,UAAoC,CAAC;CACrC,QAAoC,CAAC;CACrC,SAAiB;;CAEjB,UAAkB;CAElB,YACE,UACA,SACA,SACA;EAHiB,KAAA,WAAA;EACA,KAAA,UAAA;EACA,KAAA,UAAA;CAChB;CAEH,IAAI,GAAkB;EAGpB,KAAK,UAAU;EACf,KAAK,MAAM,KAAK,CAAC;CACnB;;;;;;CAOA,SAAS,KAAgB,QAA0D;EACjF,KAAK,WAAW;EAChB,IAAI,QAAQ;GACV,KAAK,QAAQ,KAAK;IAAE,GAAG,OAAO;IAAQ,QAAQ,KAAK;IAAQ,UAAU,OAAO;GAAS,CAAC;GACtF,KAAK,UAAU,OAAO;EACxB;EACA,MAAM,MAAM,QAAQ,SAAS,KAAK,UAAU,KAAK;EACjD,KAAK,UAAU;EACf,KAAK,UAAU;CACjB;CAEA,WAAqB;EACnB,KAAK,WAAW;EAKhB,OAAO;GAAE,SAAS,KAAK;GAAS,eAAe,KAAK,IAAI,GAAG,KAAK,SAAS,KAAK,OAAO;EAAE;CACzF;CAEA,aAA2B;EACzB,IAAI,KAAK,MAAM,WAAW,GAAG;EAC7B,MAAM,QAAQ,KAAK;EAGnB,MAAM,aAAuB,IAAI,MAAM,MAAM,MAAM;EACnD,IAAI,SAAS,KAAK;EAClB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,WAAW,KAAK;GAChB,UAAU,MAAM,EAAE,CAAE;GACpB,IAAI,IAAI,MAAM,SAAS,GAAG,UAAU,KAAK;EAC3C;EACA,MAAM,UAAU;EAMhB,MAAM,aAAa,MAAM,MAAM,MAAM,EAAE,WAAW,SAAS,CAAC;EAC5D,MAAM,YAAoC,IAAI,MAAM,MAAM,MAAM;EAChE,IAAI,WAAW;EACf,IAAI,YAAY;GACd,SAAS,UAAU,KAAK;GACxB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IACrC,MAAM,IAAI,MAAM;IAChB,IAAI,EAAE,WAAW,WAAW,GAAG;IAC/B,UAAU,KAAK;IACf,UAAU,EAAE,cAAc,KAAK;GACjC;GAGA,WAAW,SAAS,KAAK;EAC3B;EAGA,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,MAAM,IAAI,MAAM;GAChB,MAAM,YAAY,WAAW;GAC7B,MAAM,WAAW,UAAU;GAE3B,IAAI;GACJ,IAAI,UAAU,YAAY,EAAE;GAC5B,IAAI,aAAa,KAAA,KAAa,EAAE,WAAW,SAAS,GAAG;IAMrD,eAAe,CAAC;IAChB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,WAAW,QAAQ,KAAK;KAC5C,MAAM,YAAY,EAAE,WAAW;KAC/B,aAAa,aAAa,WAAW,aAAa,EAAE,UAAU,KAAM,EAAE;IACxE;IACA,UAAU,WAAW,EAAE;GACzB;GAEA,KAAK,QAAQ,KAAK;IAChB,GAAG,EAAE;IACL,QAAQ;IACR,UAAU,UAAU;IACpB,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;GACzC,CAAC;EACH;EAEA,KAAK,MAAM,SAAS;EACpB,KAAK,SAAS;EACd,KAAK,UAAU;CACjB;AACF;AAMA,SAAS,wBAAwB,MAAc,MAAoB,QAAmC;CACpG,MAAM,WAAW,QAAQ,YAAY,SAAS;CAC9C,MAAM,UAAU,QAAQ,WAAW,SAAS;CAC5C,MAAM,UAAU,QAAQ,WAAW,SAAS;CAC5C,MAAM,kBAAkB,QAAQ,mBAAmB,SAAS;CAC5D,MAAM,YAAY,QAAQ,aAAa,SAAS;CAEhD,MAAM,QAAQ,UAAU,IAAI;CAE5B,IAAI,QAAQ,SAAS;EACnB,MAAM,YAAY,OAAO,QAAQ,aAAa,UAAU,OAAO,QAAQ,aAAa,KAAA,IAAY,KAAA,IAAY,OAAO,QAAQ;EAC3H,MAAM,QAAQ,IAAI,iBAAiB,SAAS,SAAS,OAAO,QAAQ,SAAS,SAAS;EACtF,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,MAAM,OAAO,MAAM;GACnB,MAAM,cAAc,SAAS;GAC7B,MAAM,eAAe,CAAC,eAAe,QAAQ,KAAK,IAAI;GACtD,IAAI,aAAa;IACf,MAAM,UAAU,MAAM;IACtB;GACF;GACA,IAAI,cAAc;IAChB,MAAM,UAAU;KAAE;KAAM,eAAe;KAAG,UAAU;IAAM,CAAC;IAC3D,MAAM,UAAU,MAAM;IACtB;GACF;GACA,MAAM,QAAQ,gBAAgB,MAAM,IAAI;GACxC,IAAI,OACF,MAAM,SAAS;IAAE;IAAM,eAAe;IAAG,UAAU;GAAK,GAAG,MAAM,KAAK,eAAe;QAErF,MAAM,SAAS;IAAE;IAAM,eAAe;IAAG,UAAU;GAAM,GAAG,eAAe;EAE/E;EACA,OAAO,MAAM,SAAS;CACxB;CAEA,MAAM,QAAQ,IAAI,UAAU,UAAU,SAAS,OAAO;CAEtD,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,OAAO,MAAM;EACnB,MAAM,cAAc,SAAS;EAC7B,MAAM,eAAe,CAAC,eAAe,QAAQ,KAAK,IAAI;EAEtD,IAAI,aAAa;GACf,MAAM,SAAS,MAAM;GACrB;EACF;EACA,IAAI,cAAc;GAChB,MAAM,SAAS,QAAQ;IAAE,QAAQ;KAAE;KAAM,eAAe;KAAG,UAAU;IAAM;IAAG,UAAU;GAAE,CAAC;GAC3F;EACF;EAEA,MAAM,QAAQ,gBAAgB,MAAM,IAAI;EACxC,IAAI,OAAO;GACT,MAAM,OAAO,eAAe,OAAO,iBAAiB,SAAS;GAC7D,MAAM,IAAI;IACR,QAAQ;KAAE;KAAM,eAAe;KAAG,UAAU;IAAK;IACjD,cAAc,KAAK;IACnB,aAAa,KAAK;IAClB,SAAS,KAAK;IACd,YAAY,KAAK;IACjB,WAAW,KAAK;GAClB,CAAC;EACH,OACE,MAAM,IAAI;GACR,QAAQ;IAAE;IAAM,eAAe;IAAG,UAAU;GAAM;GAClD,cAAc;GACd,aAAa;GACb,SAAS;GACT,YAAY,CAAC;GACb,WAAW,CAAC;EACd,CAAC;CAEL;CAEA,OAAO,MAAM,SAAS;AACxB;AAMA,SAAS,sBAAsB,MAAc,MAAoB,QAAoC,QAAgC;CACnI,MAAM,WAAW,QAAQ,YAAY,SAAS;CAC9C,MAAM,UAAU,QAAQ,WAAW,SAAS;CAC5C,MAAM,UAAU,QAAQ,WAAW,SAAS;CAC5C,MAAM,kBAAkB,QAAQ,mBAAmB,SAAS;CAC5D,MAAM,YAAY,QAAQ,aAAa,SAAS;CAIhD,MAAM,QAAQ,UAAU,IAAI;CAC5B,MAAM,kBAAkB,IAAI,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE;CAC/D;EACE,IAAI,IAAI;EACR,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,gBAAgB,KAAK;GACrB,KAAK,MAAM,EAAE,CAAE;EACjB;EACA,gBAAgB,KAAK,UAAU,MAAM;CACvC;CAEA,MAAM,eAAe,QAAQ,UACzB,IAAI,iBACF,SACA,SACA,OAAO,QAAQ,SACf,OAAO,QAAQ,aAAa,UAAU,OAAO,QAAQ,aAAa,KAAA,IAAY,KAAA,IAAY,OAAO,QAAQ,QAC3G,IACA;CACJ,MAAM,QAAQ,eAAe,OAAO,IAAI,UAAU,UAAU,SAAS,OAAO;CAI5E,IAAI,YAAY;CAChB,KAAK,IAAI,IAAI,GAAG,KAAK,KAAK,QAAQ,KAAK;EACrC,MAAM,QAAQ,MAAM,KAAK;EACzB,IAAI,CAAC,SAAS,KAAK,OAAO,MAAM;EAEhC,MAAM,WAAW,KAAK,MAAM,WAAW,CAAC;EACxC,IAAI,SAAS,SAAS,GAAG;GACvB,MAAM,SAAS,OAAO,MAAM,QAAQ;GAMpC,MAAM,QAAQ,OAAO,KAAK,GAAG,QAAQ,GAAG;GACxC,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE,CAAE,KAAK,OAAO,EAAE,CAAE,MAAM,IAAI,CAAC;GAC3D,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IAErC,MAAM,QAAQ,OADJ,MAAM;IAEhB,MAAM,eAAe,YAAY,MAAM;IAKvC,MAAM,YAAY,IAAI,IAAI,MAAM,SAAS,MAAM,IAAI,KAAM;IACzD,MAAM,aAAa,aAAa,IAAI,YAAY,OAAO,UAAU,CAAE,KAAK;IACxE,MAAM,cAAc,gBAAgB,iBAAiB;IACrD,IAAI,cAAc,GAAG;IACrB,MAAM,cAAc,KAAK,MAAM,cAAc,UAAU;IACvD,MAAM,YAAY,MAAM;IACxB,MAAM,eAAe,QAAQ,KAAK,WAAW;IAC7C,MAAM,OAAO,KAAK,gBAAgB,MAAM,MAAM,gBAAgB,MAAM,SAAS;IAC7E,MAAM,WAAW,CAAC,CAAC;IAEnB,IAAI,cAAc;KAChB,IAAI,cAAc;MAChB,aAAa,UAAU;OAAE,MAAM;OAAW,eAAe;OAAa,SAAS,MAAM;OAAG;MAAS,CAAC;MAClG,aAAa,UAAU,MAAM;KAC/B,OACE,MAAO,SAAS,QAAQ;MACtB,QAAQ;OAAE,MAAM;OAAW,eAAe;OAAa,SAAS,MAAM;OAAG;MAAS;MAClF,UAAU;KACZ,CAAC;KAEH;IACF;IAEA,IAAI,cAAc;KAChB,MAAM,UAAU,YAAY,OAAQ,KAAK,KAAK,kBAAmB;KACjE,aAAa,SAAS;MAAE,MAAM;MAAW,eAAe;MAAa,SAAS,MAAM;MAAG,UAAU,CAAC,EAAE,YAAY;KAAM,GAAG,OAAO;IAClI,OAAO,IAAI,YAAY,MAAM;KAC3B,MAAM,OAAO,eAAe,MAAM,iBAAiB,SAAS;KAC5D,MAAO,IAAI;MACT,QAAQ;OAAE,MAAM;OAAW,eAAe;OAAa,SAAS,MAAM;OAAG,UAAU;MAAK;MACxF,cAAc,KAAK;MACnB,aAAa,KAAK;MAClB,SAAS,KAAK;MACd,YAAY,KAAK;MACjB,WAAW,KAAK;KAClB,CAAC;IACH,OACE,MAAO,IAAI;KACT,QAAQ;MAAE,MAAM;MAAW,eAAe;MAAa,SAAS,MAAM;MAAG,UAAU;KAAM;KACzF,cAAc;KACd,aAAa;KACb,SAAS;KACT,YAAY,CAAC;KACb,WAAW,CAAC;IACd,CAAC;GAEL;EACF;EAEA,IAAI,CAAC,OAAO;GACV,IAAI,cAAc,aAAa,UAAU,MAAM;QAC1C,MAAO,SAAS,MAAM;GAC3B,YAAY,IAAI;EAClB;CACF;CAEA,OAAO,eAAe,aAAa,SAAS,IAAI,MAAO,SAAS;AAClE;;;;;;;AC3kBA,MAAa,iBAAiB;;;;;;AAO9B,MAAa,6CAAkD,IAAI,IAAI,CAAA,CAAe,CAAC;;;ACTvF,MAAM,0BAAU,IAAI,IAA0B;AAC9C,MAAM,gCAAgB,IAAI,IAAkB;AAE5C,SAAS,mBAAmB,QAA4B;CACtD,IAAI,cAAc,IAAI,MAAM,GAAG;CAC/B,IAAI,OAAO,WAAW,QAAQ,CAAC,2BAA2B,IAAI,OAAO,OAAO,GAAG;EAC7E,cAAc,IAAI,MAAM;EACxB,QAAQ,KACN,oBAAoB,OAAO,OAAO,gBAAgB,OAAO,WAAW,YAAY,uCACxC,CAAC,GAAG,0BAA0B,CAAC,CAAC,KAAK,IAAI,EAAE,qGAErF;CACF;AACF;;AAGA,SAAgB,eAAe,QAA4B;CACzD,mBAAmB,MAAM;CACzB,QAAQ,IAAI,OAAO,QAAQ,MAAM;CACjC,IAAI,OAAO,cAAc,OAAO,eAAe,OAAO,QACpD,QAAQ,IAAI,OAAO,YAAY,MAAM;AAEzC;;AAGA,SAAgB,UAAU,QAA0C;CAClE,OAAO,QAAQ,IAAI,MAAM;AAC3B;AAEA,SAAgB,cAAc,MAAmE;CAC/F,IAAI,OAAO,SAAS,UAAU;EAC5B,MAAM,SAAS,UAAU,IAAI;EAC7B,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,2CAA2C,KAAK,6CAA6C;EAC1H,OAAO;CACT;CACA,IAAI,MAAM,mBAAmB,IAAI;CACjC,OAAO;AACT;;;;;;;;;;;;;;;;;;ACtBA,SAAgB,aAAa,EAC3B,QACA,YACA,SACA,aACA,WACA,UAAU,SACV,aAAa,KACb,WAAW,KACX,YAAY,QAWG;CACf,MAAM,QAAQ,CAAC,8BAA8B,OAAO,cAAc,QAAQ,KAAK;CAC/E,IAAI,cAAc,aAChB,MAAM,KAAK,8BAA8B,WAAW,cAAc,YAAY,KAAK;CAErF,OAAO;EACL,SAAA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa,MAAM,KAAK,GAAG;EAC3B;EACA;EACA;EACA;CACF;AACF;;;ACvDA,MAAa,WAAW;AACxB,MAAa,eAAe;AAC5B,MAAa,eAAe;AAE5B,MAAa,eAAe;AAC5B,MAAa,qBAAqB;AAClC,MAAa,mBAAmB;AAIhC,IAAI,0BAA0B;AAC9B,SAAgB,wBAAwB;CACtC,IAAI,yBAAyB;CAC7B,0BAA0B;CAC1B,IAAI,OAAO,QAAQ,eAAe,sBAAsB,KACtD,KAAK,MAAM,QAAQ;EAAC;EAAU;EAAc;CAAY,GACtD,IAAI;EACF,IAAI,iBAAiB;GAAE,MAAM;GAAM,QAAQ;GAAY,UAAU;GAAM,cAAc;EAAI,CAAC;CAC5F,QAAQ,CAER;AAGN;;;;;;ACjBA,SAAgB,kBACd,KACA,MACA,GACA,UACA,UACA,YACA,OACA,UAA4B,CAAC,GAC7B,OAAO,GACP;CACA,MAAM,cAAc,YAAY,SAAS,MAAM;CAC/C,MAAM,eAAe,WAAW,SAAS,QAAQ;CACjD,MAAM,uBAAuB,WAAW,SAAS,gBAAgB;CAGjE,IAAI,KAAK;CACT,IAAI,KAAK;CACT,IAAI,cAAc;EAChB,MAAM,aAAa,aAAa,OAAO,aAAa,QAAQ,WAAW;EACvE,MAAM,YAAY,aAAa,OAAO,aAAa;EACnD,KAAK,YAAY,KAAK,IAAI,aAAa,WAAW,OAAQ,IAAI;EAC9D,KAAK,YAAY,KAAK,IAAI,aAAa,IAAI,OAAQ,OAAO,GAAG;CAC/D;CAEA,MAAM,QAAQ,IAAI;CAClB,MAAM,QAAQ,WAAW;CAGzB,IAAI,YAAY;CAChB,IAAI,sBAAsB;EACxB,MAAM,SAAS,qBAAqB,OAAO;EAC3C,IAAI,WAAW,WAAW;GACxB,MAAM,aAAa,qBAAqB,OAAO,cAAc;GAC7D,MAAM,YAAY,qBAAqB,OAAO,aAAa;GAE3D,YAAY,OADC,OAAO,QAAS,IACN,IAAI,WAAW,KAAK,UAAU;EACvD,OAAO,IAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,GAClD,YAAY,OAAO,KAAK,MAAM,IAAI,IAAI,OAAO;CAEjD;CAEA,IAAI,KAAK;CACT,IAAI,OAAO,GAAG,SAAS,KAAK;CAC5B,IAAI,eAAe;CAGnB,KAAK,MAAM,QAAQ,aAAa;EAC9B,IAAI,KAAK;EACT,IAAI,aAAa,iBAAiB,KAAK,OAAO,UAAU,GAAG,QAAQ;EACnE,IAAI,cAAc,KAAK,OAAO,SAAS;EACvC,IAAI,gBAAgB,KAAK,OAAO,WAAW;EAC3C,IAAI,gBAAgB,KAAK,OAAO,WAAW;EAC3C,IAAI,YAAY,KAAK,OAAO,SAAS;EACrC,IAAI,SAAS,MAAM,OAAO,KAAK;EAC/B,IAAI,QAAQ;CACd;CAGA,IAAI,YAAY;CAChB,IAAI,SAAS,MAAM,OAAO,KAAK;CAE/B,IAAI,QAAQ;AACd;;;AC/DA,MAAa,YAAY;AAEzB,SAAgB,eAAe,SAAmD;CAChF,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,OAAO,cAAc,QAAQ,IAAI;CACvC,MAAM,aAAa,OAAO,cAAc,IAAI,IAAI,KAAA;CAEhD,MAAM,WAAW,QAAQ,OAAO,gBAAgB,MAAM,MAAM,QAAQ,MAAM,CAAC,CAAC,gBAAgB;CAC5F,MAAM,UAAU,OAAO,QAAQ,SAAS,WAAW,QAAQ,OAAO;CAClE,MAAM,UACJ,OAAO,QAAQ,SAAS,WACpB,QAAQ,OACR,SAAS,SAAS,eAChB,QAAQ,SAAS,aACf,QAAQ,QAAQ,WAChB,QAAQ,QACV,SAAS,SAAS,iBACf,QAAQ,eAAe,IACxB;CACV,MAAM,SAAS,SAAS,SAAS,iBAAiB,QAAQ,SAAS,KAAA;CACnE,MAAM,OAAO,UAAU,WAAW,IAAI,OAAO,UAAU,QAAQ,IAAI,WAAW;CAC9E,MAAM,WAAW,WAAW,IAAI,OAAO,WAAW;CAElD,OAAO;EACL,eAAe;EACf,KAAK,SAAS,aAAa;EAC3B,OAAO;GACL,UAAU;GACV,UAAU;GACV,OAAO;GACP,QAAQ;GACR;GACA,WAAW,QAAQ,aAAa,KAAA;IAC/B,eAAe;IACf,WAAW;IACX,eAAe;EAClB;CACF;AACF;AAEA,SAAgB,cAAiB,SAA8B,GAA0B;CACvF,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,QAAQ,QAAQ,SAAS,SAAU,OAAO,QAAQ,SAAS,YAAY,QAAQ,MAAM,SAAS;CACpG,MAAM,cAAc,QAAQ;CAE5B,OAAO,EACL,QACA,EAAE,OAAO;EAAE,SAAS;EAAS,UAAU;CAAW,EAAE,GACpD,EAAE,QAAQ;EACR,eAAe;EACf,eAAe;EACf,OAAO;GACL,UAAU;GACV,OAAO;GACP,UAAU;GACV,eAAe;GACf,UAAU;GACV,YAAY;GACZ,YAAY;GACZ,YAAY,QACR,uDAAuD,aAAa,WACpE;EACN;CACF,CAAC,GACD,EACE,UACA;EACE,eAAe;EACf,eAAe;EACf,OAAO;GAEL,UAAU;GACV,KAAK,aAAa,UAAU;GAC5B,OAAO;GACP,QAAQ,aAAa,UAAU;GAC/B,MAAM;GACN,OAAO;GACP,QAAQ,mBAAmB,UAAU;GACrC,eAAe;GACf,UAAU;EACZ;CACF,GACA,EACE,QACA;EACE,eAAe;EACf,OAAO;GAAE,SAAS;GAAgB,SAAS,GAAG,UAAU;EAAQ;CAClE,GACA,IACF,CACF,GACA,EACE,QACA;EACE,eAAe;EACf,OAAO;GACL,SAAS;GACT,YAAY;GACZ,YAAY;GACZ,cAAc;GACd,kBAAkB;GAClB,eAAe;GACf,qBAAqB,cAAc,KAAA,IAAY;GAC/C,OAAO,cAAc,yBAAyB,KAAA;EAChD;CACF,GACA,IACF,CACF;AACF;AAMA,SAAgB,iBAAiB,KAAa,OAA4B,GAAG,UAAiD;CAC5H,MAAM,KAAK,SAAS,cAAc,GAAG;CACrC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC7C,IAAI,QAAQ,WAAW,OAAO,UAAU;OACjC,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,KAA4B,GAC9D,IAAI,MAAM,KAAA,KAAa,MAAM,MAC3B,IAAI,EAAE,WAAW,IAAI,GACnB,GAAG,MAAM,YAAY,GAAG,OAAO,CAAC,CAAC;OAEjC,GAAI,MAAc,KAAK,OAAO,MAAM,YAAY,MAAM,aAAa,MAAM,WAAW,GAAG,EAAE,MAAM;CAAA,OAIhG,IAAI,QAAQ,eACjB,GAAG,aAAa,eAAe,OAAO,KAAK,CAAC;MACvC,IAAI,IAAI,WAAW,OAAO,GAC/B,GAAG,aAAa,KAAK,OAAO,KAAK,CAAC;CAGtC,KAAK,MAAM,SAAS,UAClB,IAAI,OAAO,UAAU,UACnB,GAAG,YAAY,SAAS,eAAe,KAAK,CAAC;MAE7C,GAAG,YAAY,KAAK;CAGxB,OAAO;AACT;;;ACxIA,IAAI,UAAgC;AACpC,MAAM,8BAAc,IAAI,IAAmC;;;;;;;;;;AAW3D,SAAgB,eAAe,GAA+B;CAC5D,UAAU;CACV,YAAY,MAAM;AACpB;;;;;AAMA,SAAgB,mBAAmB,QAAoD;CACrF,IAAI,CAAC,SAAS,OAAO;CACrB,MAAM,MAAM,OAAO;CACnB,IAAI,QAAQ,YAAY,IAAI,GAAG;CAC/B,IAAI,CAAC,OAAO;EACV,MAAM,SAAS,QAAQ,MAAM;EAC7B,IAAI,CAAC,QAAQ,OAAO;EACpB,YAAY,IAAI,KAAK,MAAM;EAC3B,QAAQ;CACV;CACA,OAAO;AACT;;;;;;;;;ACDA,SAAS,gBAAgB,GAA0B;CACjD,MAAM,UAAU,EAAE,KAAK;CACvB,IAAI,CAAC,QAAQ,SAAS,GAAG,GAAG,OAAO;CACnC,MAAM,MAAM,OAAO,QAAQ,MAAM,GAAG,EAAE,CAAC;CACvC,OAAO,OAAO,SAAS,GAAG,IAAI,MAAM,MAAM;AAC5C;AAEA,SAAS,mBAAmB,MAA+D;CACzF,IAAI,QAAQ,MAAM,OAAO,EAAE,MAAM,eAAe;CAChD,IAAI,OAAO,SAAS,UAAU,OAAO;EAAE,MAAM;EAAc,OAAO;CAAK;CACvE,IAAI,OAAO,SAAS,UAAU;EAC5B,IAAI,SAAS,OAAO,OAAO,EAAE,MAAM,MAAM;EACzC,MAAM,MAAM,gBAAgB,IAAI;EAChC,IAAI,OAAO,MAAM,OAAO;GAAE,MAAM;GAAc,OAAO;GAAK,MAAM;EAAW;EAC3E,OAAO,EAAE,MAAM,eAAe;CAChC;CACA,OAAO;AACT;AAMA,IAAa,eAAb,MAA0B;;CAIxB,OAAO,iBAAiB;;CAGxB,OAAO,YAAY;;;;;;;;;;CAanB,OAAO,iBAAiB;CAGxB;CACA,aAAyC;CACzC;CACA;CACA;CACA;CACA,cAAgD;CAGhD,QAAgB;CAChB,QAAqC;CACrC,eAA+D,EAAE,MAAM,eAAe;CACtF;CACA;CACA;CACA,eAAuB;CACvB;CACA;CACA;CAGA,mBAA6C,eAAe,KAAA,CAAS;CACrE;CACA,YAA8B;EAAE,SAAS,CAAC;EAAsB,eAAe;CAAE;CACjF,UAAqC;CACrC,aAAqB;CACrB,aAAqB;CACrB,UAAuC;CACvC,eAAuB;CACvB,iBAAyB;CAMzB,+BAAgF,IAAI,QAAQ;CAC5F,kBAA0B;CAG1B,kBAA0B;CAC1B,YAAoB;CACpB,cAAsB;CACtB,gBAAwB;CAGxB,gBAAwB;CACxB,WAAmB;CACnB,WAAmB;CACnB,iBAAyB;CACzB,kBAA0B;CAC1B,oBAA4B;CAC5B,UAAiC;CACjC,SAAiB;CACjB,iBAAyB;CACzB,wBAAgC;CAChC,aAAqB;CAGrB;CACA,OAAsC;;;;;;;;CAStC,OAAO,eACL,SACA,eACgD;EAChD,OAAO;GACL,WAAW,eAAe,OAAO;GACjC,SAAS,cAAc,SAAS,aAAa;EAC/C;CACF;CAEA,YAAY,WAAwB,SAAqD;EACvF,sBAAsB;EACtB,KAAK,QAAQ,KAAK,OAAO,IAAI;EAK7B,KAAK,UAAU;EAEf,IAAI,SAAS,OAAO,CAEpB,OAAO;GAEL,MAAM,UAAU,cAAc,WAAW,CAAC,GAAG,gBAAgB;GAC7D,UAAU,YAAY,OAAO;GAC7B,KAAK,aAAa;GAElB,MAAM,YAAY,eAAe,WAAW,CAAC,CAAC;GAC9C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,UAAU,KAA4B,GAC9E,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC,IAAI,IAAI,WAAW,IAAI,GACrB,UAAU,MAAM,YAAY,KAAK,OAAO,KAAK,CAAC;QAE9C,UAAW,MAAc,OAAO,OAAO,UAAU,YAAY,QAAQ,aAAa,QAAQ,WAAW,GAAG,MAAM,MAAM;GAI1H,UAAU,QAAQ,SAAS;GAC3B,UAAU,MAAM,SAAS,aAAa;EACxC;EAEA,KAAK,cAAc,UAAU,cAAc,4BAA0B;EACrE,KAAK,YAAY,UAAU,cAAc,0BAAwB;EACjE,KAAK,oBAAoB,UAAU,cAAc,mCAAiC;EAClF,KAAK,aAAa,UAAU,cAAc,2BAAyB;EAGnE,KAAK,kBAAkB,IAAI,eAAe,KAAK,SAAS;EACxD,KAAK,gBAAgB,QAAQ,KAAK,OAAO;EAGzC,KAAK,YAAY,iBAAiB,iBAAiB,KAAK,qBAAqB;EAG7E,IAAI,OAAO,WAAW,aAAa;GACjC,KAAK,OAAO,OAAO,WAAW,kCAAkC;GAChE,KAAK,wBAAwB,KAAK,KAAK;GACvC,IAAI,KAAK,KAAK,kBAAkB,KAAK,KAAK,iBAAiB,UAAU,KAAK,sBAAsB;QAE3F,KAAK,KAAK,YAAY,KAAK,sBAAsB;EACxD;EAGA,KAAK,SAAS;EAGd,IAAI,SAAS,KAAK,OAAO,OAAO;CAClC;CAMA,IAAI,cAAsB;EACxB,MAAM,KAAK,KAAK;EAChB,IAAI,GAAG,SAAS,OAAO,OAAO,KAAK;EACnC,IAAI,GAAG,SAAS,cAAc,OAAO,GAAG,SAAS,aAAa,GAAG,QAAQ,KAAK,UAAU,gBAAgB,GAAG;EAC3G,MAAM,WAAW,KAAK,UAAU;EAChC,IAAI,GAAG,UAAU,WAAW,GAC1B,OAAO,GAAG,OAAO,KAAK,gBAAgB,QAAQ,IAAI;EAEpD,OAAO,KAAK;CACd;CAEA,IAAI,WAAmB;EACrB,OAAO,KAAK,UAAU;CACxB;;;;;;;CAQA,IAAI,WAAqB;EACvB,OAAO,KAAK;CACd;;;;;;;;;;;CAYA,gBAAgB,MAAwB;EACtC,IAAI,CAAC,KAAK,OAAO,OAAO;GAAE,SAAS,CAAC;GAAG,eAAe;EAAE;EACxD,OAAO,gBAAgB,MAAM,KAAK,OAAO,KAAK,SAAS,KAAK,OAAO;CACrE;CAEA,IAAI,YAAqB;EACvB,OAAO,KAAK;CACd;CAEA,IAAI,aAAsB;EACxB,MAAM,WAAW,KAAK,UAAU;EAChC,IAAI,aAAa,GAAG,OAAO;EAI3B,IADW,KAAK,aACT,SAAS,gBAAgB,OAAO,KAAK,iBAAiB;EAC7D,OAAO,KAAK,eAAe;CAC7B;CAEA,IAAI,UAAuB;EACzB,OAAO,KAAK;CACd;CAEA,OAAa;EACX,IAAI,KAAK,aAAa,SAAS,gBAAgB;EAC/C,KAAK,WAAW;EAChB,KAAK,kBAAkB;CACzB;CAEA,QAAc;EACZ,IAAI,KAAK,aAAa,SAAS,gBAAgB;EAC/C,KAAK,WAAW;EAChB,KAAK,kBAAkB;CACzB;;;;;;CAOA,KAAK,MAAmC;EACtC,IAAI,KAAK,aAAa,SAAS,gBAAgB;EAC/C,IAAI;EACJ,IAAI,OAAO,SAAS,UAAU;GAC5B,MAAM,MAAM,gBAAgB,IAAI;GAChC,IAAI,OAAO,MAAM;GACjB,WAAW,MAAM,KAAK,UAAU;EAClC,OACE,WAAW;EAEb,KAAK,gBAAgB,KAAK,IAAI,GAAG,KAAK,IAAI,UAAU,KAAK,UAAU,aAAa,CAAC;EACjF,KAAK,kBAAkB;EACvB,KAAK,oBAAoB;EACzB,KAAK,iBAAiB;EACtB,KAAK,kBAAkB;EACvB,KAAK,QAAQ;EACb,KAAK,qBAAqB;CAC5B;CAEA,UAAgB;EACd,IAAI,KAAK,aAAa,SAAS,gBAAgB;EAC/C,KAAK,gBAAgB;EACrB,KAAK,WAAW;EAChB,KAAK,iBAAiB;EACtB,KAAK,kBAAkB,KAAK,aAAa,SAAS;EAClD,KAAK,oBAAoB;EACzB,KAAK,kBAAkB;EACvB,KAAK,kBAAkB;CACzB;CAEA,OAAO,SAA6C;EAClD,IAAI,KAAK,YAAY;EAErB,IAAI,gBAAgB;EACpB,IAAI,cAAc;EAClB,IAAI,cAAc;EAClB,IAAI,gBAAgB;EAEpB,IAAI,UAAU,SAAS;GAQrB,MAAM,YAAY,QAAQ,QAAQ,GAAA,CAAI,QAAQ,UAAU,IAAI,CAAC,CAAC,UAAU,KAAK;GAC7E,IAAI,aAAa,KAAK,OAAO;IAC3B,KAAK,QAAQ;IACb,gBAAgB;IAChB,cAAc;GAChB;EACF;EAEA,IAAI,YAAY,SAAS;GACvB,MAAM,OAAO,QAAQ,WAAW;GAChC,IAAI,SAAS,KAAK,gBAAgB;IAChC,KAAK,iBAAiB;IACtB,KAAK,YAAY;IACjB,KAAK,oBAAoB;IACzB,gBAAgB;IAChB,cAAc;IACd,gBAAgB;IAChB,cAAc;GAChB;EACF;EAEA,IAAI,UAAU,SAAS;GACrB,MAAM,WAAW,cAAc,QAAQ,IAAI,KAAK;GAChD,IAAI,aAAa,KAAK,OAAO;IAC3B,KAAK,UAAU,QAAQ;IACvB,gBAAgB;IAChB,cAAc;IACd,gBAAgB;GAClB;EACF;EAEA,IAAI,UAAU,SAAS;GACrB,MAAM,QAAQ,mBAAmB,QAAQ,IAAI;GAC7C,MAAM,QAAQ,KAAK;GAGnB,MAAM,cAAc,MAAM,SAAS,MAAM;GACzC,MAAM,yBACJ,MAAM,SAAS,gBAAgB,MAAM,SAAS,iBAAiB,MAAM,UAAU,MAAM,SAAS,MAAM,SAAS,MAAM;GACrH,MAAM,sBACJ,MAAM,SAAS,kBACf,MAAM,SAAS,mBACd,MAAM,UAAU,MAAM,SACrB,MAAM,aAAa,MAAM,YACzB,MAAM,YAAY,MAAM,WACxB,MAAM,SAAS,MAAM,QACrB,MAAM,UAAU,MAAM,SACtB,MAAM,YAAY,MAAM,WACxB,MAAM,YAAY,MAAM,WACxB,MAAM,WAAW,MAAM;GAE3B,IAAI,eAAe,0BAA0B,qBAAqB;IAChE,KAAK,eAAe;IAEpB,IAAI,MAAM,SAAS,gBAAgB;KACjC,KAAK,WAAW,MAAM,WAAW;KACjC,MAAM,WAAW,MAAM,SAAS,iBAAkB,MAAM,SAAS,IAAK;KACtE,MAAM,WAAW,MAAM,SAAS;KAChC,IAAI,eAAe,aAAa,UAAU;MACxC,KAAK,kBAAkB;MACvB,KAAK,oBAAoB;KAC3B;IACF;IAEA,gBAAgB;IAChB,cAAc;IAGd,KAAK,0BAA0B;GACjC;EACF;EAEA,IAAI,aAAa,WAAW,QAAQ,YAAY,KAAK,UAAU;GAC7D,KAAK,WAAW,QAAQ;GACxB,KAAK,mBAAmB,eAAe,KAAK,QAAQ;GACpD,cAAc;EAChB;EAEA,IAAI,YAAY,WAAW,QAAQ,WAAW,KAAK,SAAS;GAC1D,KAAK,UAAU,QAAQ;GACvB,gBAAgB;EAClB;EAEA,IAAI,aAAa,WAAW,QAAQ,YAAY,KAAK,UAAU;GAC7D,KAAK,WAAW,QAAQ;GACxB,cAAc;EAChB;EAEA,IAAI,eAAe,WAAW,QAAQ,cAAc,KAAK,YAAY;GACnE,KAAK,aAAa,QAAQ;GAC1B,cAAc;GACd,cAAc;EAChB;EAEA,IAAI,iBAAiB,WAAW,QAAQ,gBAAgB,KAAK,cAAc;GACzE,KAAK,eAAe,QAAQ,eAAe;GAC3C,KAAK,oBAAoB;GACzB,cAAc;EAChB;EAEA,IAAI,gBAAgB,SAClB,KAAK,cAAc,QAAQ;EAG7B,IAAI,sBAAsB,SACxB,KAAK,oBAAoB,QAAQ;EAInC,IAAI,eAAe,KAAK,mBAAmB;EAC3C,IAAI,eAAe,iBAAiB,aAAa,KAAK,WAAW;EACjE,IAAI,aAAa,KAAK,iBAAiB;EACvC,IAAI,eAAe,KAAK,kBAAkB;EAC1C,IAAI,eAAe,iBAAiB,aAAa,KAAK,QAAQ;CAChE;CAEA,UAAgB;EACd,KAAK,aAAa;EAClB,KAAK,UAAU;EACf,KAAK,gBAAgB,WAAW;EAChC,KAAK,YAAY,oBAAoB,iBAAiB,KAAK,qBAAqB;EAChF,IAAI,KAAK,MACP,IAAI,KAAK,KAAK,qBAAqB,KAAK,KAAK,oBAAoB,UAAU,KAAK,sBAAsB;OACjG,KAAK,KAAK,eAAe,KAAK,sBAAsB;EAG3D,KAAK,YAAY,OAAO;EAGxB,KAAK,+BAAe,IAAI,QAAQ;EAChC,KAAK,kBAAkB;EACvB,KAAK,cAAc;CACrB;;CAOA,oBAA4B,UAA0B;EACpD,IAAI,KAAK,OACP,QAAS,KAAK,MAAM,WAAW,KAAK,MAAM,aAAa,KAAK,MAAM,aAAc;EAElF,OAAO,WAAW;CACpB;CAEA,WAAyB;EACvB,MAAM,SAAS,iBAAiB,KAAK,OAAO;EAC5C,KAAK,kBAAkB,KAAK,QAAQ,sBAAsB,CAAC,CAAC;EAC5D,KAAK,YAAY,OAAO,WAAW,OAAO,QAAQ;EAClD,MAAM,WAAW,OAAO,WAAW,OAAO,UAAU;EACpD,KAAK,cAAc,OAAO,MAAM,QAAQ,IAAI,KAAK,oBAAoB,KAAK,SAAS,IAAI;EACvF,KAAK,gBAAgB,OAAO;CAC9B;CAEA,aAA2B;EAEzB,KAAK,QAAQ,MAAM,aAAa,KAAK,QAAQ,cAAc,KAAK,KAAK,IAAI;EAGzE,KAAK,QAAQ,MAAM,YAAY,KAAK,cAAc;EAGlD,KAAK,qBAAqB;EAG1B,IAAI,KAAK,WAAW,gBAAgB,KAAK,OACvC,KAAK,WAAW,cAAc,KAAK;EAErC,KAAK,kBAAkB,cAAc,KAAK;CAC5C;CAEA,uBAAqC;EACnC,MAAM,OAAO,KAAK;EAClB,MAAM,MAAM,KAAK,UAAU;EAC3B,KAAK,QAAQ,MAAM,YAAY,cAAc,OAAO,GAAG,CAAC;EACxD,KAAK,QAAQ,MAAM,YAAY,UAAU,OAAO,IAAI,CAAC;EACrD,KAAK,QAAQ,MAAM,YAAY,cAAc,OAAO,MAAM,IAAI,OAAO,MAAM,CAAC,CAAC;CAC/E;CAEA,sBAAoC;EAClC,IAAI,KAAK,cAAc;GACrB,KAAK,WAAW,MAAM,sBAAsB;GAC5C,KAAK,WAAW,MAAM,QAAQ;EAChC,OAAO;GACL,KAAK,WAAW,MAAM,sBAAsB;GAC5C,KAAK,WAAW,MAAM,QAAQ;EAChC;EAOA,KAAK,WAAW,MAAM,sBAAsB,KAAK,iBAC7C,KACA;CACN;CAEA,4BAA0C;EACxC,MAAM,QAAQ,KAAK,aAAa,SAAS;EACzC,KAAK,YAAY,MAAM,aAAa,QAChC,uDAAuD,aAAa,WACpE;CACN;CAMA,aAAqB,YAAyC;EAC5D,MAAM,QAAQ,QAAQ;EACtB,IAAI,CAAC,OAAO;EACZ,MAAM,WAAW,MAAM,YAAY;EACnC,MAAM,SAAS,iBAAiB,KAAK,OAAO;EAC5C,MAAM,cAAc,OAAO,WAAW,OAAO,QAAQ;EACrD,MAAM,WAAW,OAAO,WAAW,OAAO,UAAU;EACpD,MAAM,gBAAgB,OAAO,MAAM,QAAQ,IAAI,KAAK,oBAAoB,WAAW,IAAI;EACvF,MAAM,WAAW,OAAO;EAExB,IAAI,UAAU;EACd,IAAI,gBAAgB;EAEpB,IAAI,aAAa,KAAK,iBAAiB;GACrC,KAAK,kBAAkB;GACvB,gBAAgB;GAChB,UAAU;EACZ;EACA,IAAI,gBAAgB,KAAK,WAAW;GAClC,KAAK,YAAY;GACjB,gBAAgB;GAChB,UAAU;EACZ;EACA,IAAI,kBAAkB,KAAK,aAAa;GACtC,KAAK,cAAc;GACnB,gBAAgB;GAChB,UAAU;EACZ;EACA,IAAI,aAAa,KAAK,eAAe;GACnC,KAAK,gBAAgB;GACrB,UAAU;EACZ;EAEA,IAAI,eAAe,KAAK,iBAAiB;EACzC,IAAI,SAAS,KAAK,QAAQ;CAC5B;CAEA,yBAAiC,MAA6B;EAC5D,MAAM,SAAS,iBAAiB,KAAK,WAAW;EAChD,IAAI,UAAU;EAEd,IAAI,EAAE,iBAAiB,eAAe,EAAE,iBAAiB,eAAe;GACtE,MAAM,cAAc,OAAO,WAAW,OAAO,QAAQ;GACrD,MAAM,WAAW,OAAO,WAAW,OAAO,UAAU;GACpD,MAAM,gBAAgB,OAAO,MAAM,QAAQ,IAAI,KAAK,oBAAoB,WAAW,IAAI;GACvF,IAAI,gBAAgB,KAAK,aAAa,kBAAkB,KAAK,aAAa;IACxE,KAAK,YAAY;IACjB,KAAK,cAAc;IACnB,KAAK,iBAAiB;IACtB,UAAU;GACZ;EACF;EAEA,IAAI,EAAE,iBAAiB,SAAS;GAC9B,MAAM,WAAW,OAAO;GACxB,IAAI,aAAa,KAAK,eAAe;IACnC,KAAK,gBAAgB;IACrB,UAAU;GACZ;EACF;EAEA,IAAI,EAAE,iBAAA,qBAA+B;GACnC,MAAM,cAAc,OAAO,OAAO,iBAAiB,YAAY,CAAC;GAChE,KAAK,WAAW,cAAc,KAAK,UAAU;GAC7C,UAAU;EACZ;EAEA,IAAI,SAAS,KAAK,QAAQ;CAC5B;CAMA,0BAAkC,MAAiC;EACjE,KAAK,wBAAwB,EAAE;EAC/B,IAAI,KAAK,yBAAyB,KAAK,aAAa,SAAS,kBAAkB,KAAK,UAAU,gBAAgB,GAC5G,KAAK,gBAAgB,KAAK,UAAU;EAEtC,KAAK,kBAAkB;EACvB,KAAK,QAAQ;CACf;CAMA,UAAkB,MAAiC;EACjD,KAAK,QAAQ;EACb,KAAK,aAAa;EAElB,IAAI,CAAC,MAAM;GACT,KAAK,YAAY;GACjB;EACF;EAEA,MAAM,UAAU,WAAW,KAAK,QAAQ,KAAK,SAAS,KAAK,UAAU,KAAK,aAAa;EACvF,IAAI,YAAY,MACd,KAAK,aAAa;OACb;GACL,MAAM,cAAc;GACpB,QAAQ,WAAW;IACjB,IAAI,KAAK,UAAU,eAAe,CAAC,KAAK,YAAY;KAClD,KAAK,aAAa;KAClB,KAAK,mBAAmB;KACxB,KAAK,WAAW;KAChB,KAAK,iBAAiB;KACtB,KAAK,kBAAkB;KACvB,KAAK,QAAQ;IACf;GACF,CAAC;EACH;EAEA,KAAK,YAAY;CACnB;;;;;;;CAQA,cAA4B;EAC1B,KAAK,UAAU;EACf,KAAK,eAAe;EACpB,IAAI,CAAC,KAAK,kBAAkB,CAAC,KAAK,OAAO;EAEzC,MAAM,gBAAgB,mBAAmB,KAAK,KAAK;EACnD,IAAI,CAAC,eAAe;EAEpB,KAAK,eAAe;EACpB,MAAM,cAAc,KAAK;EACzB,cAAc,MAAM,WAAW;GAC7B,IAAI,KAAK,UAAU,eAAe,KAAK,kBAAkB,CAAC,KAAK,YAAY;IACzE,KAAK,UAAU;IACf,KAAK,eAAe;IACpB,KAAK,mBAAmB;IACxB,KAAK,iBAAiB;IACtB,KAAK,kBAAkB;IACvB,KAAK,QAAQ;GACf;EACF,CAAC;CACH;CAMA,qBAAmC;EACjC,IAAI,KAAK,SAAS,KAAK,OACrB,KAAK,YAAY,gBAAgB,KAAK,OAAO,KAAK,OAAO,KAAK,SAAS,KAAK,OAAO;OAEnF,KAAK,YAAY;GAAE,SAAS,CAAC;GAAsB,eAAe;EAAE;EAEtE,KAAK,oBAAoB,KAAK,SAAS;CACzC;CAEA,mBAAiC;EAC/B,IAAI,KAAK,cAAc,KAAK,OAAO,UAAU,KAAK,aAAa,KAAK,mBAAmB,KAAK,OAAO;GACjG,MAAM,WAAW,KAAK,UAAU,MAAM;GACtC,MAAM,MAAM,GAAG,KAAK,MAAM,IAAI,KAAK,MAAM,OAAO,IAAI,KAAK,UAAU,IAAI,KAAK,YAAY,IAAI,KAAK,gBAAgB,IAAI,KAAK,cAAc,GAAG,IAAI;GAC/I,IAAI,QAAQ,KAAK,YAAY;GAC7B,KAAK,aAAa;GAClB,IAAI,SAAS,kBAAkB,KAAK,YAAY,KAAK,SAAS;GAC9D,IAAI,KAAK,WAAW,KAAK,OAOvB,SAAS,qBAAqB,QAAQ,KAAK,YAAY,KAAK,OAAO,KAAK,WAAW,KAAK,OAAO,KAAK,SAAS,KAAK,SAAS;GAE7H,KAAK,UAAU;EACjB,OAAO;GACL,KAAK,aAAa;GAClB,KAAK,UAAU;EACjB;CACF;CAMA,oBAAkC;EAKhC,IAJW,KAAK,aAEX,SAAS,kBAAkB,KAAK,YAAY,CAAC,CAAC,KAAK,SAAS,KAAK,cAAc,KAAK,gBAAgB,CAAC,KAAK,uBAG7G,KAAK,WAAW;OAEhB,KAAK,UAAU;CAEnB;CAEA,aAA2B;EACzB,IAAI,KAAK,QAAQ;EACjB,KAAK,UAAU;EACf,KAAK,iBAAiB;EACtB,KAAK,SAAS,sBAAsB,KAAK,KAAK;CAChD;CAEA,YAA0B;EACxB,IAAI,KAAK,QAAQ;GACf,qBAAqB,KAAK,MAAM;GAChC,KAAK,SAAS;EAChB;CACF;CAEA,SAAiB,OAAqB;EACpC,IAAI,KAAK,YAAY;EAErB,IAAI,KAAK,YAAY,MAAM,KAAK,UAAU;EAC1C,MAAM,SAAS,KAAK,KAAK,WAAW;EACpC,KAAK,UAAU;EAEf,MAAM,KAAK,KAAK;EAChB,IAAI,GAAG,SAAS,gBAAgB;EAEhC,MAAM,OAAO,GAAG,QAAQ;EACxB,MAAM,WAAW,KAAK,UAAU;EAChC,MAAM,mBAAmB,GAAG;EAC5B,MAAM,cAAc,qBAAqB,KAAA,KAAa,mBAAmB;EAEzE,IAAI,aAAa,KAAM,CAAC,QAAQ,KAAK,iBAAiB,UAAW;GAC/D,KAAK,gBAAgB;GACrB,KAAK,SAAS,sBAAsB,KAAK,KAAK;GAC9C;EACF;EAGA,IAAI,KAAK,kBAAkB,GAAG;GAC5B,KAAK,kBAAkB,KAAK,IAAI,GAAG,KAAK,kBAAkB,KAAK;GAC/D,KAAK,SAAS,sBAAsB,KAAK,KAAK;GAC9C;EACF;EAGA,IAAI,KAAK,oBAAoB,GAAG;GAC9B,KAAK,oBAAoB,KAAK,IAAI,GAAG,KAAK,oBAAoB,KAAK;GACnE,IAAI,KAAK,qBAAqB,GAAG;IAC/B,KAAK,gBAAgB;IACrB,KAAK,iBAAiB;IACtB,KAAK,iBAAiB;GACxB;GACA,KAAK,kBAAkB;GACvB,KAAK,QAAQ;GACb,KAAK,qBAAqB;GAC1B,KAAK,SAAS,sBAAsB,KAAK,KAAK;GAC9C;EACF;EAIA,IAAI;EACJ,IAAI,aACF,iBAAiB,WAAW;OACvB;GACL,MAAM,QAAQ,GAAG,SAAS;GAC1B,MAAM,UAAU,GAAG,WAAW;GAC9B,iBAAiB;GACjB,IAAI,UAAU,GAAG;IACf,MAAM,YAAY,KAAK,IAAI,GAAG,WAAW,KAAK,aAAa;IAE3D,MAAM,cAAc,UADL,KAAK,IAAI,GAAG,YAAY,CACJ;IACnC,MAAM,aAAa;IACnB,MAAM,cAAc,OAAO,KAAK;IAChC,MAAM,OAAO,cAAc,KAAK,iBAAiB,aAAa;IAC9D,KAAK,mBAAmB,cAAc,KAAK,mBAAmB,IAAI,KAAK,IAAI,CAAC,OAAO,KAAK;IACxF,iBAAiB,QAAQ,KAAK;GAChC;EACF;EAEA,IAAI,OAAO,KAAK,gBAAgB,QAAQ;EACxC,IAAI,QAAQ,UAAU;GACpB,IAAI,MAAM;IACR,MAAM,UAAU,GAAG,WAAW;IAC9B,IAAI,UAAU,GAAG;KAEf,OAAO;KACP,KAAK,oBAAoB;IAC3B,OAAO,IAAI,KAAK,gBAAgB,UAI9B,OAAO;SAEP,QAAQ;GAEZ,OACE,OAAO;GAET,KAAK,iBAAiB;EACxB;EACA,KAAK,gBAAgB;EAErB,KAAK,kBAAkB;EACvB,KAAK,iBAAiB;EACtB,KAAK,QAAQ;EACb,KAAK,qBAAqB;EAE1B,KAAK,SAAS,sBAAsB,KAAK,KAAK;CAChD;CAEA,oBAAkC;EAChC,MAAM,KAAK,KAAK;EAChB,IAAI,GAAG,SAAS,kBAAkB,GAAG,cAEnC,GAAG,aAAa,KAAK,WAAW;CAEpC;CAEA,mBAAiC;EAC/B,MAAM,WAAW,KAAK;EACtB,IAAI,YAAY,CAAC,KAAK,gBAAgB;GACpC,KAAK,iBAAiB;GACtB,KAAK,cAAc;EACrB,OAAO,IAAI,CAAC,UACV,KAAK,iBAAiB;CAE1B;CAMA,UAAwB;EACtB,MAAM,SAAS,KAAK;EACpB,MAAM,OAAO,KAAK;EAClB,MAAM,SAAS,KAAK;EACpB,MAAM,WAAW,KAAK;EAOtB,MAAM,gBALM,OAAO,oBAAoB,KAIpB,KAAK,IAAI,KAAK,UAAU,cAAc,GAAG,CACxB;EACpC,MAAM,IAAI,OAAO;EACjB,MAAM,IAAI,OAAO;EAGjB,IADoB,OAAO,UAAU,KAAK,MAAM,IAAI,YAAY,KAAK,OAAO,WAAW,KAAK,MAAM,IAAI,YAAY,GACjG;GACf,OAAO,QAAQ,KAAK,MAAM,IAAI,YAAY;GAC1C,OAAO,SAAS,KAAK,MAAM,IAAI,YAAY;EAC7C;EAEA,MAAM,MAAM,OAAO,WAAW,IAAI;EAClC,IAAI,CAAC,KAAK;EAEV,IAAI,aAAa,cAAc,GAAG,GAAG,cAAc,GAAG,CAAC;EACvD,IAAI,UAAU,GAAG,GAAG,GAAG,CAAC;EAIxB,IAAI,CAAC,MAAM,aAAa,CAAC,UAAU,CAAC,UAAU;EAE9C,MAAM,OAAO,eAAe;EAC5B,MAAM,aAAa,KAAK;EACxB,MAAM,OAAO,KAAK,IAAI,mBAAmB,WAAW,qBAAqB,WAAW,cAAc,CAAC;EACnG,IAAI,UAAU,MAAM,IAAI;EAExB,MAAM,QAAQ,KAAK,iBAAiB;EAGpC,MAAM,eAAe,cAFH,KAAK,WAAW,KAAK,aAAa,KAAK,aAC3B,YACkB;EAChD,MAAM,aAAa,UAAU,KAAK,KAAK;EACvC,MAAM,cAAc,KAAK;EAQzB,MAAM,yBACJ,CAAC,CAAC,WAAW,KAAK,kBAAkB,QAAQ,KAC5C,CAAC,CAAC,WAAW,KAAK,kBAAkB,gBAAgB,KACpD,CAAC,CAAC,WAAW,KAAK,kBAAkB,OAAO,YACpC;GACL,MAAM,IAAI,WAAW,KAAK,kBAAkB,eAAe;GAC3D,OAAO,CAAC,CAAC,KAAK,KAAK,IAAI,GAAG,KAAK,IAAI,EAAE,OAAO,YAAY,GAAG,CAAC,CAAC,IAAI;EACnE,EAAA,CAAG;EACL,MAAM,YAAY,KAAK,UAAU,cAAc;EAE/C,MAAM,sBADkB,KAAK,UAAU,gBACS,0BAA0B,YAAY,IAAI,KAAA;EAC1F,MAAM,QAAQ,WAAW,KAAK;EAC9B,MAAM,cAAc,uBAAuB,OAAO,sBAAsB,QAAQ;EAChF,MAAM,WAAW,GAAG,KAAK,OAAO,GAAG,YAAY,GAAG,YAAY,MAAM;EACpE,IAAI,aAAa,KAAK,iBAAiB;GACrC,KAAK,+BAAe,IAAI,QAAQ;GAChC,KAAK,kBAAkB;EACzB;EACA,MAAM,cAAc,KAAK;EACzB,MAAM,iBAAiB,WAA2D;GAChF,IAAI,MAAM,YAAY,IAAI,MAAM;GAChC,IAAI,CAAC,KAAK;IACR,MAAM,gBAAgB,QAAQ,aAAa,SAAS;IACpD,YAAY,IAAI,QAAQ,GAAG;GAC7B;GACA,OAAO;EACT;EAEA,MAAM,WAAW,KAAK,UAAU;EAChC,MAAM,cAAc,OAAO,aAAa,WAAW,WAAW;EAQ9D,MAAM,QADW,eAAe,KAAK,gBACW,IAC5C;GACE;GACA;GACA;GACA;GACA,YAAY,KAAK;GACjB,UAAU,KAAK;GACf,WAAW,KAAK;GAChB,MAAM,kBAAkB,QAAQ,UAAU,UAAU;GACpD,WAAW;GACX,MAAM,KAAK;EACb,IACA;EAEJ,IAAI,OACF,KAAK,MAAM,UAAU,KAAK,kBACxB,oBAAoB,OAAO,MAAM,CAAC,EAAE,eAAe,OAAO,OAAO,MAAM;EAM3E,MAAM,iBAAiB,IAAI,WAAW,WAAW,MAAM,CAAC,CAAC,KAAK,EAAE;EAChE,KAAK,IAAI,KAAK,GAAG,KAAK,OAAO,MAAM,QAAQ,MAAM;GAC/C,MAAM,cAAc,OAAO,MAAM;GACjC,KAAK,MAAM,WAAW,aAAa,eAAe,WAAW;EAC/D;EAEA,KAAK,IAAI,KAAK,GAAG,KAAK,KAAK,UAAU,QAAQ,QAAQ,MAAM;GACzD,MAAM,QAAQ,KAAK,UAAU,QAAQ;GACrC,IAAI,MAAM,SAAS,MAAM;GACzB,MAAM,UAAU,MAAM;GACtB,MAAM,UAAU,eAAe,YAAY;GAC3C,IAAI,UAAU,GAAG;GACjB,MAAM,IAAI,UAAU;GAOpB,MAAM,aAAa,OAAO,YAAY;GACtC,MAAM,IACJ,MAAM,cAAc,KAAA,KAAa,eAAe,KAAA,KAC3C,aAAa,MAAM,aAAa,YAChC,OAAO,YAAY,YAAY,KAAK;GAC3C,MAAM,SAAS,MAAM,YAAY,KAAA,IAAY,KAAK,gBAAgB,MAAM,WAAW,KAAA,MAAc,gBAAgB,MAAM,MAAM,IAAI;GAEjI,IAAI,SAAS,MAAM,UAAU;IAC3B,IAAI,YAAY,KAAK,IAAI,GAAG,KAAK,IAAI,cAAc,MAAM,QAAQ,MAAM,QAAQ,CAAC;IAChF,MAAM,cAAc,KAAK,SAAS;IAClC,IAAI,eAAe,MAAM,WAAW,GAClC,YAAY,YAAY,YAAY,MAAM,QAAQ,IAAI,MAAM;IAM9D,UACE,KACA,OACA;KACE;KACA,GANW,IAAI,eAAe,MAAM,aAAa,KAAK;KAOtD;KACA,YAAY,KAAK;KACjB,UAAU,KAAK;KACf,WAAW,KAAK;IAClB,GACA,WACA,KAAK,SACL,OACA,KAAK,kBACL,KAAK,QAAQ,SACb,eACA,KAAK,SAAS,cACd,aACA,OAAO,aACP,MAAM,cACN,MAAM,eACR;GACF,OAAO,IAAI,CAAC,MAAM,YAAY,eAAe,MAAM,SAAS,MAAM,UAAU;IAC1E,MAAM,WAAW,IAAI,cAAe,KAAK,WAAW,KAAK,aAAc;IACvE,kBAAkB,KAAK,MAAM,MAAM,GAAG,UAAU,UAAU,cAAc,IAAI,GAAG,OAAO,KAAK,kBAAkB,KAAK,QAAQ,OAAO;GACnI;EACF;EAMA,IAAI,OACF,KAAK,IAAI,IAAI,KAAK,iBAAiB,SAAS,GAAG,KAAK,GAAG,KAAK;GAC1D,MAAM,SAAS,KAAK,iBAAiB;GACrC,oBAAoB,OAAO,MAAM,CAAC,EAAE,cAAc,OAAO,OAAO,MAAM;EACxE;EAQF,IAAI,UAAU;GACZ,IAAI,CAAC,KAAK,aAAa,KAAK,cAAc,SAAS,cAAc,QAAQ;GACzE,MAAM,aAAa,KAAK;GACxB,IAAI,WAAW,UAAU,OAAO,SAAS,WAAW,WAAW,OAAO,QAAQ;IAC5E,WAAW,QAAQ,OAAO;IAC1B,WAAW,SAAS,OAAO;GAC7B;GACA,MAAM,UAAU,WAAW,WAAW,IAAI;GAC1C,QAAQ,aAAa,cAAc,GAAG,GAAG,cAAc,GAAG,CAAC;GAC3D,QAAQ,UAAU,GAAG,GAAG,GAAG,CAAC;GAC5B,QAAQ,UAAU,MAAM,IAAI;GAC5B,QAAQ,OAAO,GAAG,SAAS,KAAK,cAAc,IAAI;GAClD,QAAQ,eAAe;GAMvB,IAAI,QAAQ;GACZ,KAAK,MAAM,eAAe,OAAO,OAAO;IACtC,IAAI,WAAW;IACf,KAAK,MAAM,WAAW,aAAa;KACjC,MAAM,OAAO,WAAW;KACxB,IAAI,SAAS,MAAM;KACnB,YAAY;IACd;IACA,IAAI,UAAU;KACZ,MAAM,WAAW,QAAQ,cAAe,KAAK,WAAW,KAAK,aAAc;KAC3E,QAAQ,SAAS,UAAU,GAAG,QAAQ;IACxC;IACA,SAAS;GACX;GAEA,IAAI,KAAK;GACT,IAAI,aAAa,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;GACjC,IAAI,2BAA2B;GAC/B,IAAI,UAAU,YAAY,GAAG,CAAC;GAC9B,IAAI,QAAQ;EACd;CACF;AACF"}