@sarmal/core 0.36.0 → 0.36.2

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engine.ts","../src/renderer-shared.ts","../src/terminal.ts"],"names":[],"mappings":"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","file":"terminal.cjs","sourcesContent":["import type { CurveDef, Engine, JumpOptions, MorpStrategy, Point, SeekOptions } from \"./types\";\n\nconst TWO_PI = Math.PI * 2;\nconst POINTS_PER_PERIOD_UNIT = 50;\n\ntype SpeedTransition = {\n from: number;\n to: number;\n elapsed: number;\n duration: number;\n resolve: () => void;\n reject: (err: Error) => void;\n};\n\n/** Linearly interpolate from start to end by factor t (0→1) */\nfunction lerp(start: number, end: number, t: number): number {\n return start + (end - start) * t;\n}\n\n/** Reused across all curve fn calls but params is never populated, allocation is wasteful */\nconst EMPTY_PARAMS: Record<string, number> = {};\n\n/**\n * A fixed-size list of points with first in, last out method\n * The oldest entry is automatically discarded when the list is at capacity\n *\n * Note: `result.length` is *never* changed,\n * so callers use the separate `count` getter to know valid size\n */\nclass CircularBuffer {\n private data: Array<Point>;\n private result: Array<Point>;\n private capacity: number;\n private head: number = 0;\n private count: number = 0;\n\n constructor(capacity: number) {\n this.capacity = capacity;\n this.data = Array.from({ length: capacity }, () => ({ x: 0, y: 0 }));\n this.result = Array.from({ length: capacity }, () => ({ x: 0, y: 0 }));\n }\n\n /** Mutates in-place */\n push(x: number, y: number): void {\n const slot = this.data[this.head]!;\n\n slot.x = x;\n slot.y = y;\n this.head = (this.head + 1) % this.capacity;\n\n if (this.count < this.capacity) {\n this.count++;\n }\n }\n\n /**\n * Copies ordered points into the pre-allocated result buffer and returns it\n * Note: The *same* array reference is returned every call,\n * so `result.length` is also always `capacity`\n */\n toArray(): Array<Point> {\n const start = this.count < this.capacity ? 0 : this.head;\n\n for (let i = 0; i < this.count; i++) {\n const src = this.data[(start + i) % this.capacity]!;\n const dst = this.result[i]!;\n dst.x = src.x;\n dst.y = src.y;\n }\n\n return this.result;\n }\n\n clear(): void {\n this.head = 0;\n this.count = 0;\n }\n\n get length() {\n return this.count;\n }\n}\n\n/**\n * Creates the core simulation engine for a sarmal\n *\n * it runs a clock (`phase`), asks the curve for the current Point position at that time,\n * and remembers the last N positions so the renderer can draw the trail\n *\n * The engine is only responsible for math coordinates,\n * so it is not responsible for drawing or colors\n *\n * @param curveDef A curve definition\n * @param trailLength default: `120`\n */\n/** Normalised resolution of a CurveDef, with required fields filled in */\ntype ResolvedCurve = {\n name: string;\n fn: CurveDef[\"fn\"];\n period: number;\n speed: number;\n skeleton?: CurveDef[\"skeleton\"];\n skeletonFn?: CurveDef[\"skeletonFn\"];\n};\n\nfunction resolveCurve(curveDef: CurveDef): ResolvedCurve {\n const period = curveDef.period ?? TWO_PI;\n\n if (!Number.isFinite(period) || period <= 0) {\n throw new RangeError(`[sarmal] period must be a positive finite number, got ${period}`);\n }\n\n const speed = curveDef.speed ?? 1;\n\n if (!Number.isFinite(speed)) {\n throw new RangeError(`[sarmal] speed must be a finite number, got ${speed}`);\n }\n\n return {\n name: curveDef.name,\n fn: curveDef.fn,\n period,\n speed,\n skeleton: curveDef.skeleton,\n skeletonFn: curveDef.skeletonFn,\n };\n}\n\nexport function createEngine(curveDef: CurveDef, trailLength: number = 120): Engine {\n if (!Number.isFinite(trailLength) || trailLength <= 0) {\n throw new RangeError(\n `[sarmal] trailLength must be a positive finite number, got ${trailLength}`,\n );\n }\n\n let curve = resolveCurve(curveDef);\n const trail = new CircularBuffer(trailLength);\n let phase = 0;\n let actualTime = 0;\n let userSpeedOverride: number | null = null;\n\n // Morph state which is `null` when not morphing\n let morphCurveB: ResolvedCurve | null = null;\n let _morphAlpha: number | null = null;\n let _morphStrategy: MorpStrategy = \"normalized\";\n\n // Speed transition state which is `null` when not transitioning\n let _speedTransition: SpeedTransition | null = null;\n\n /** Samples a resolved curve's skeleton at position `samplePhase` */\n function sampleSkeleton(c: ResolvedCurve, samplePhase: number): Point {\n if (c.skeletonFn) {\n return c.skeletonFn(samplePhase);\n }\n\n if (c.skeleton === \"live\") {\n return c.fn(samplePhase, actualTime, EMPTY_PARAMS);\n }\n\n return c.fn(samplePhase, 0, EMPTY_PARAMS);\n }\n\n return {\n tick(deltaTime: number): Array<Point> {\n if (_speedTransition !== null) {\n // tick() receives dt in seconds, but SpeedTransition.duration is in milliseconds.\n // Convert dt to ms so the elapsed/duration ratio is dimensionless.\n _speedTransition.elapsed += deltaTime * 1000;\n const alpha = Math.min(_speedTransition.elapsed / _speedTransition.duration, 1);\n userSpeedOverride = lerp(_speedTransition.from, _speedTransition.to, alpha);\n if (alpha >= 1) {\n userSpeedOverride = _speedTransition.to;\n _speedTransition.resolve();\n _speedTransition = null;\n }\n }\n\n let effectiveSpeed = userSpeedOverride ?? curve.speed;\n if (morphCurveB !== null && _morphAlpha !== null) {\n effectiveSpeed = lerp(effectiveSpeed, morphCurveB.speed, _morphAlpha);\n }\n phase = (phase + effectiveSpeed * deltaTime) % curve.period;\n actualTime += deltaTime;\n\n if (morphCurveB !== null && _morphAlpha !== null) {\n const a = curve.fn(phase, actualTime, EMPTY_PARAMS);\n const phaseB =\n _morphStrategy === \"normalized\" ? (phase / curve.period) * morphCurveB.period : phase;\n const b = morphCurveB.fn(phaseB, actualTime, EMPTY_PARAMS);\n trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);\n } else {\n const point = curve.fn(phase, actualTime, EMPTY_PARAMS);\n trail.push(point.x, point.y);\n }\n\n return trail.toArray();\n },\n\n get trailCount() {\n return trail.length;\n },\n\n get trailLength() {\n return trailLength;\n },\n\n get isLiveSkeleton() {\n return curve.skeleton === \"live\";\n },\n\n get morphAlpha() {\n return _morphAlpha;\n },\n\n reset() {\n phase = 0;\n actualTime = 0;\n trail.clear();\n },\n\n jump(newPhase: number, { clearTrail = false }: JumpOptions = {}) {\n phase = ((newPhase % curve.period) + curve.period) % curve.period;\n\n if (clearTrail) {\n trail.clear();\n }\n },\n\n seek(\n targetPhase: number,\n { wrap = false, step = curve.period / trailLength }: SeekOptions = {},\n ) {\n const advance = curve.speed * step;\n const target = ((targetPhase % curve.period) + curve.period) % curve.period;\n const targetTime = target / curve.speed;\n\n phase = target;\n actualTime = targetTime;\n trail.clear();\n\n const pointsFromStart = Math.floor(target / advance) + 1;\n const count = wrap ? trailLength : Math.min(trailLength, pointsFromStart);\n\n for (let i = count - 1; i >= 0; i--) {\n const samplePhase = target - i * advance;\n const wrappedPhase = ((samplePhase % curve.period) + curve.period) % curve.period;\n const elapsed = targetTime - i * step;\n const point = curve.fn(wrappedPhase, elapsed, EMPTY_PARAMS);\n\n trail.push(point.x, point.y);\n }\n },\n\n startMorph(target: CurveDef, strategy: MorpStrategy = \"normalized\") {\n const resolvedTarget = resolveCurve(target);\n\n if (morphCurveB !== null && _morphAlpha !== null) {\n const frozenAlpha = _morphAlpha;\n const frozenA = curve;\n const frozenB = morphCurveB;\n const frozenStrategy = _morphStrategy;\n\n curve = {\n ...frozenB,\n fn: (samplePhase: number, elapsed: number, params: Record<string, number>) => {\n const a = frozenA.fn(samplePhase, elapsed, params);\n const phaseB =\n frozenStrategy === \"normalized\"\n ? (samplePhase / frozenA.period) * frozenB.period\n : samplePhase;\n const b = frozenB.fn(phaseB, elapsed, params);\n\n return {\n x: a.x + (b.x - a.x) * frozenAlpha,\n y: a.y + (b.y - a.y) * frozenAlpha,\n };\n },\n };\n }\n\n _morphStrategy = strategy;\n morphCurveB = resolvedTarget;\n _morphAlpha = 0;\n },\n\n setMorphAlpha(alpha: number) {\n _morphAlpha = alpha;\n },\n\n completeMorph() {\n if (morphCurveB !== null) {\n // Normalized strategy drives `curveB` at `phaseB` = `(phase / periodA) * periodB`\n // Remap `phase` so the trail continues from the same position on `curveB`,\n // not from a raw `phase` value that belongs to `curveA`'s smaller range.\n if (_morphStrategy === \"normalized\" && curve.period !== morphCurveB.period) {\n phase = (phase / curve.period) * morphCurveB.period;\n }\n curve = morphCurveB;\n }\n morphCurveB = null;\n _morphAlpha = null;\n },\n\n getSarmalSkeleton(): Array<Point> {\n const steps = Math.ceil(curve.period * POINTS_PER_PERIOD_UNIT);\n // oxlint-disable-next-line unicorn/no-new-array -- array is pre-allocated, filled immediately below\n const points: Array<Point> = new Array(steps);\n\n if (morphCurveB !== null && _morphAlpha !== null) {\n for (let i = 0; i < steps; i++) {\n const samplePhase = (i / (steps - 1)) * curve.period;\n const a = sampleSkeleton(curve, samplePhase);\n const phaseB =\n _morphStrategy === \"normalized\"\n ? (samplePhase / curve.period) * morphCurveB.period\n : samplePhase;\n const b = sampleSkeleton(morphCurveB, phaseB);\n\n points[i] = {\n x: a.x + (b.x - a.x) * _morphAlpha,\n y: a.y + (b.y - a.y) * _morphAlpha,\n };\n }\n return points;\n }\n\n for (let i = 0; i < steps; i++) {\n const samplePhase = (i / (steps - 1)) * curve.period;\n points[i] = sampleSkeleton(curve, samplePhase);\n }\n\n return points;\n },\n\n setSpeed(speed: number): void {\n if (!Number.isFinite(speed)) {\n throw new Error(\"speed must be a finite number\");\n }\n if (_speedTransition !== null) {\n _speedTransition.reject(new Error(\"Speed transition cancelled\"));\n _speedTransition = null;\n }\n userSpeedOverride = speed;\n },\n\n getSpeed(): number {\n return userSpeedOverride ?? curve.speed;\n },\n\n resetSpeed(): void {\n userSpeedOverride = null;\n },\n\n setSpeedOver(speed: number, duration: number): Promise<void> {\n if (!Number.isFinite(speed)) {\n throw new Error(\"speed must be a finite number\");\n }\n if (!Number.isFinite(duration) || duration <= 0) {\n throw new Error(\"duration must be a finite number greater than 0\");\n }\n\n if (_speedTransition !== null) {\n _speedTransition.reject(new Error(\"Speed transition cancelled\"));\n _speedTransition = null;\n }\n\n const from = userSpeedOverride ?? curve.speed;\n\n return new Promise<void>((resolve, reject) => {\n _speedTransition = { from, to: speed, elapsed: 0, duration, resolve, reject };\n });\n },\n\n cancelSpeedTransition(): void {\n if (_speedTransition !== null) {\n _speedTransition.reject(new Error(\"Speed transition cancelled\"));\n _speedTransition = null;\n }\n },\n };\n}\n","import type {\n BaseRuntimeRenderOptions,\n Engine,\n Point,\n RuntimeRenderOptions,\n TrailColor,\n TrailStyle,\n} from \"./types\";\n\nexport const DEFAULT_MORPH_DURATION_MS = 300;\nexport const DEFAULT_SKELETON_OPACITY = 0.15;\n/** Fraction of the bounding-box dimension added as proportional padding on each side when fitting the curve. */\nexport const FIT_PADDING = 0.1;\nexport const FIT_PADDING_MIN = 4;\n/** Higher values = sharper fade near the tail, more of the trail appears faint */\nexport const TRAIL_FADE_CURVE = 1.5;\nexport const TRAIL_MAX_OPACITY = 0.88;\n/** Pixel-space stroke/line width at the tail which the SVG renderer overrides with viewBox unit values */\nexport const TRAIL_MIN_WIDTH = 0.5;\n/** Pixel-space stroke/line width at the head which the SVG renderer overrides with viewBox unit values */\nexport const TRAIL_MAX_WIDTH = 2.5;\n\nexport interface TrailPoint {\n x: number;\n y: number;\n}\n\n/**\n * Computes the unit tangent vector at a point on the trail.\n * - Interior points: central difference (previous -> next)\n * - Endpoints: forward/backward difference\n */\nexport function computeTangent(trail: TrailPoint[], i: number): TrailPoint {\n const count = trail.length;\n if (count < 2) {\n return { x: 1, y: 0 };\n }\n\n if (i === 0) {\n const dx = trail[1]!.x - trail[0]!.x;\n const dy = trail[1]!.y - trail[0]!.y;\n const len = Math.sqrt(dx * dx + dy * dy) || 1;\n return { x: dx / len, y: dy / len };\n }\n\n if (i === count - 1) {\n const dx = trail[count - 1]!.x - trail[count - 2]!.x;\n const dy = trail[count - 1]!.y - trail[count - 2]!.y;\n const len = Math.sqrt(dx * dx + dy * dy) || 1;\n return { x: dx / len, y: dy / len };\n }\n\n const dx = trail[i + 1]!.x - trail[i - 1]!.x;\n const dy = trail[i + 1]!.y - trail[i - 1]!.y;\n const len = Math.sqrt(dx * dx + dy * dy) || 1;\n return { x: dx / len, y: dy / len };\n}\n\n/**\n * Computes the unit normal vector at a point on the trail.\n * The normal is perpendicular to the tangent, rotated 90° counter-clockwise\n */\nexport function computeNormal(trail: TrailPoint[], i: number): TrailPoint {\n const tangent = computeTangent(trail, i);\n return { x: -tangent.y, y: tangent.x };\n}\n\n/** The four pixel-space corners and per-segment style values for one ribbon quad */\nexport interface TrailQuad {\n /** Left corner at the tail end of this segment */\n l0x: number;\n l0y: number;\n /** Right corner at the tail end of this segment */\n r0x: number;\n r0y: number;\n /** Left corner at the head end of this segment */\n l1x: number;\n l1y: number;\n /** Right corner at the head end of this segment */\n r1x: number;\n r1y: number;\n /** Fill opacity for this segment (0–1) */\n opacity: number;\n /** Position along the trail (0 = tail, 1 = head) */\n progress: number;\n}\n\n/**\n * Computes the quad corners and style for one ribbon segment in the renderer's coordinate space (pixel-space for canvas, viewBox-space for SVG)\n *\n * @param trail Full trail array\n * @param i Segment index (draws from point i to point i+1)\n * @param trailCount Number of active trail points\n * @param toX Convert a trail point to its pixel X coordinate\n * @param toY Convert a trail point to its pixel Y coordinate\n *\n * @see https://mattdesl.svbtle.com/drawing-lines-is-hard\n * DesLauriers: \"Triangulated Lines\" - expand points outward by half\n * the thickness on either side using normals to create thick lines.\n */\nexport function computeTrailQuad(\n trail: TrailPoint[],\n i: number,\n trailCount: number,\n toX: (p: TrailPoint) => number,\n toY: (p: TrailPoint) => number,\n minWidth = TRAIL_MIN_WIDTH,\n maxWidth = TRAIL_MAX_WIDTH,\n): TrailQuad {\n const progress = i / (trailCount - 1);\n const nextProgress = (i + 1) / (trailCount - 1);\n const opacity = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;\n const w0 = (minWidth + progress * (maxWidth - minWidth)) / 2;\n const w1 = (minWidth + nextProgress * (maxWidth - minWidth)) / 2;\n\n const curr = trail[i]!;\n const next = trail[i + 1]!;\n const n0 = computeNormal(trail, i);\n const n1 = computeNormal(trail, i + 1);\n\n const cx = toX(curr);\n const cy = toY(curr);\n const nx = toX(next);\n const ny = toY(next);\n\n return {\n l0x: cx + n0.x * w0,\n l0y: cy + n0.y * w0,\n r0x: cx - n0.x * w0,\n r0y: cy - n0.y * w0,\n l1x: nx + n1.x * w1,\n l1y: ny + n1.y * w1,\n r1x: nx - n1.x * w1,\n r1y: ny - n1.y * w1,\n opacity,\n progress,\n };\n}\n\nexport interface BoundaryResult {\n scale: number;\n offsetX: number;\n offsetY: number;\n}\n\n/**\n * Computes how to map engine coordinates into a viewport of the given logical size.\n * ! Returns `null` if `pts` is empty\n * ! Throws if all points are identical\n *\n * Padding per side is `max(FIT_PADDING * dim, minPaddingPx)`, so the stricter constraint wins.\n * `minPaddingPx` defaults to `FIT_PADDING_MIN` (4px) for pixel-space callers.\n * Pass `0` when the logical space is itself a normalized viewBox (e.g. SVG export).\n */\nexport function computeBoundaries(\n pts: Point[],\n logicalWidth: number,\n logicalHeight: number,\n minPaddingPx = FIT_PADDING_MIN,\n): BoundaryResult | null {\n if (pts.length === 0) {\n return null;\n }\n\n const first = pts[0]!;\n let minX = first.x,\n maxX = first.x,\n minY = first.y,\n maxY = first.y;\n\n for (const p of pts) {\n if (p.x < minX) {\n minX = p.x;\n }\n if (p.x > maxX) {\n maxX = p.x;\n }\n if (p.y < minY) {\n minY = p.y;\n }\n if (p.y > maxY) {\n maxY = p.y;\n }\n }\n\n const w = maxX - minX;\n const h = maxY - minY;\n\n if (w === 0 && h === 0) {\n throw new Error(\n \"[sarmal] Degenerate curve: all skeleton points are identical. \" +\n \"Check that your curve fn returns distinct points for different values of t.\",\n );\n }\n\n const scaleXProportional = logicalWidth / (w * (1 + FIT_PADDING * 2));\n const scaleYProportional = logicalHeight / (h * (1 + FIT_PADDING * 2));\n\n const scaleXMinPadding = (logicalWidth - minPaddingPx * 2) / w;\n const scaleYMinPadding = (logicalHeight - minPaddingPx * 2) / h;\n\n const scale = Math.min(\n scaleXProportional,\n scaleYProportional,\n scaleXMinPadding,\n scaleYMinPadding,\n );\n\n return {\n scale,\n offsetX: (logicalWidth - w * scale) / 2 - minX * scale,\n offsetY: (logicalHeight - h * scale) / 2 - minY * scale,\n };\n}\n\n/**\n * Returns the engine methods that are pure pass-throughs on both renderers\n * The engine does not use `this`, so direct assignment is safe\n */\nexport function enginePassthroughs(engine: Engine) {\n return {\n jump: engine.jump,\n seek: engine.seek,\n setSpeed: engine.setSpeed,\n getSpeed: engine.getSpeed,\n resetSpeed: engine.resetSpeed,\n setSpeedOver: engine.setSpeedOver,\n getSarmalSkeleton: engine.getSarmalSkeleton,\n };\n}\n\n/**\n * Can be passed directly to `trailColor`,\n * or can be mixed/sliced before passing as a prop.\n */\nexport const palettes = {\n bard: [\"#a855f7\", \"#3b82f6\", \"#14b8a6\", \"#ec4899\"],\n carnival: [\"#ff6b6b\", \"#4ecdc4\", \"#ffe66d\"],\n ocean: [\"#1e3a8a\", \"#06b6d4\", \"#22d3ee\", \"#e0f2fe\"],\n sunset: [\"#f97316\", \"#dc2626\", \"#9333ea\", \"#f472b6\"],\n ice: [\"#1e3a8a\", \"#67e8f9\"],\n rocketpop: [\"#08b8cd\", \"#ffffff\", \"#ff001f\"],\n neon: [\"#00e5ff\", \"#7c3aed\", \"#e040fb\"],\n vaporwave: [\"#ff71ce\", \"#01cdfe\", \"#b967ff\"],\n pastel: [\"#c4b5fd\", \"#fbcfe8\", \"#bae6fd\"],\n sakura: [\"#fff1f2\", \"#fda4af\", \"#fb7185\"],\n} as const satisfies Record<string, Array<string>>;\nexport type SarmalPalette = keyof typeof palettes; // TODO: reconsider naming convention. Should this represent the value type instead of the key union?\n\n/** RGB color components */\nexport interface Rgb {\n r: number;\n g: number;\n b: number;\n}\n\n/** Converts a hex color string to RGB components */\nexport function hexToRgb(hex: string): Rgb {\n const n = parseInt(hex.slice(1), 16);\n return { r: n >> 16, g: (n >> 8) & 255, b: n & 255 };\n}\n\nconst HEX_3_RE = /^#([0-9a-fA-F]{3})$/;\nconst HEX_6_RE = /^#([0-9a-fA-F]{6})$/;\nconst HEX_8_RE = /^#([0-9a-fA-F]{8})$/;\nconst RGB_RE =\n /^rgba?\\(\\s*(-?\\d{1,3})\\s*,\\s*(-?\\d{1,3})\\s*,\\s*(-?\\d{1,3})(?:\\s*,\\s*[\\d.]+)?\\s*\\)$/i;\n\n/**\n * Parses a color string in any supported format into Rgb components.\n * Returns `null` for unrecognized or malformed input.\n *\n * Supported formats:\n * - 3-digit hex: #rgb\n * - 6-digit hex: #rrggbb\n * - 8-digit hex: #rrggbbaa (alpha is silently stripped)\n * - rgb(r, g, b) channels clamped to 0–255\n * - rgba(r, g, b, a) alpha is silently stripped\n */\nexport function parseColorToRgb(s: string): Rgb | null {\n const trimmed = s.trim();\n\n const m3 = HEX_3_RE.exec(trimmed);\n if (m3) {\n const [r, g, b] = m3[1]!;\n return hexToRgb(`#${r}${r}${g}${g}${b}${b}`);\n }\n\n const m6 = HEX_6_RE.exec(trimmed);\n if (m6) {\n return hexToRgb(trimmed);\n }\n\n const m8 = HEX_8_RE.exec(trimmed);\n if (m8) {\n return hexToRgb(`#${trimmed.slice(1, 7)}`);\n }\n\n const mRgb = RGB_RE.exec(trimmed);\n if (mRgb) {\n return {\n r: Math.max(0, Math.min(255, parseInt(mRgb[1]!, 10))),\n g: Math.max(0, Math.min(255, parseInt(mRgb[2]!, 10))),\n b: Math.max(0, Math.min(255, parseInt(mRgb[3]!, 10))),\n };\n }\n\n return null;\n}\n\nconst OKLCH_RE = /^oklch\\(\\s*([\\d.]+)\\s+([\\d.]+)\\s+([\\d.]+)(?:\\s*\\/\\s*[\\d.]+)?\\s*\\)$/i;\n\n/**\n * Parses an oklch() color string directly to Oklab.\n * Returns `null` for unrecognized or malformed input.\n *\n * Accepted syntax (subset of CSS Color 4):\n * - oklch(L C H) bare floats, L clamped to 0–1, C clamped to 0–0.4, H in degrees\n * - oklch(L C H / alpha) alpha silently stripped\n *\n * ! Not supported: percentages, `none` keyword, negative hues\n */\nexport function parseOklchToOklab(s: string): Oklab | null {\n const m = OKLCH_RE.exec(s.trim());\n if (!m) {\n return null;\n }\n\n const L = parseFloat(m[1]!);\n const C = parseFloat(m[2]!);\n const H = parseFloat(m[3]!);\n\n if (Number.isNaN(L) || Number.isNaN(C) || Number.isNaN(H)) {\n return null;\n }\n\n const clampedL = Math.max(0, Math.min(1, L));\n const clampedC = Math.max(0, Math.min(0.4, C));\n const H_rad = H * (Math.PI / 180);\n\n return {\n L: clampedL,\n a: clampedC * Math.cos(H_rad),\n b: clampedC * Math.sin(H_rad),\n };\n}\n\n/**\n * Unified color-to-Oklab entry point.\n * Tries oklch() first (direct to Oklab), then falls back through the sRGB path.\n */\nexport function parseColorToOklab(s: string): Oklab | null {\n const oklab = parseOklchToOklab(s);\n if (oklab !== null) {\n return oklab;\n }\n\n const rgb = parseColorToRgb(s);\n if (rgb === null) {\n return null;\n }\n\n return rgbToOklab(rgb);\n}\n\n/** Converts any accepted color string to Rgb. Throws if the format is unrecognized. */\nexport function colorToRgb(color: string): Rgb {\n const rgb = parseColorToRgb(color);\n if (rgb !== null) {\n return rgb;\n }\n\n const lab = parseOklchToOklab(color);\n if (lab !== null) {\n return oklabToRgb(lab);\n }\n\n throw new Error(`[sarmal] unrecognized color \"${color}\"`);\n}\n\n/** sRGB byte (0–255) to linear light (0–1)\n * @see {@link https://bottosson.github.io/posts/oklab/}\n */\nfunction srgbByteToLinear(c: number): number {\n const n = c / 255;\n return n <= 0.04045 ? n / 12.92 : Math.pow((n + 0.055) / 1.055, 2.4);\n}\n\n/** Linear light (0–1) to sRGB byte (0–255), gamma-compressed and clamped */\nfunction linearToSrgbByte(c: number): number {\n const v = Math.max(0, Math.min(1, c));\n return Math.round((v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055) * 255);\n}\n\nexport interface Oklab {\n L: number;\n a: number;\n b: number;\n}\n\n/**\n * sRGB Rgb to OKLab\n * @see {@link https://bottosson.github.io/posts/oklab/}\n */\nexport function rgbToOklab({ r, g, b }: Rgb): Oklab {\n const rl = srgbByteToLinear(r),\n gl = srgbByteToLinear(g),\n bl = srgbByteToLinear(b);\n const l = Math.cbrt(0.4122214708 * rl + 0.5363325363 * gl + 0.0514459929 * bl);\n const m = Math.cbrt(0.2119034982 * rl + 0.6806995451 * gl + 0.1073969566 * bl);\n const s = Math.cbrt(0.0883024619 * rl + 0.2817188376 * gl + 0.6299787005 * bl);\n\n return {\n L: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n };\n}\n\n/** OKLab to sRGB Rgb, out-of-gamut values clamped to 0–255 */\nexport function oklabToRgb({ L, a, b }: Oklab): Rgb {\n const l_ = L + 0.3963377774 * a + 0.2158037573 * b;\n const m_ = L - 0.1055613458 * a - 0.0638541728 * b;\n const s_ = L - 0.0894841775 * a - 1.29145603 * b;\n const l = l_ * l_ * l_,\n m = m_ * m_ * m_,\n s = s_ * s_ * s_;\n\n return {\n r: linearToSrgbByte(+4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),\n g: linearToSrgbByte(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),\n b: linearToSrgbByte(-0.0041960863 * l - 0.7034186147 * m + 1.6076099202 * s),\n };\n}\n\n/** Interpolates between two OKLab colors without any gray dead zone */\nexport const lerpOklab = (a: Oklab, b: Oklab, t: number): Oklab => {\n if (t <= 0) {\n return a;\n }\n\n if (t >= 1) {\n return b;\n }\n\n return {\n L: a.L + (b.L - a.L) * t,\n a: a.a + (b.a - a.a) * t,\n b: a.b + (b.b - a.b) * t,\n };\n};\n\n/**\n * Gets a color from a palette based on position (0-1) with optional time-based cycling\n * @param palette Array of Oklab colors\n * @param position Position along the gradient (0 = start, 1 = end)\n * @param timeOffset Optional time offset for animated gradients\n */\nexport function getPaletteColor(palette: Oklab[], position: number, timeOffset: number = 0): Oklab {\n if (palette.length === 0) {\n return { L: 1, a: 0, b: 0 };\n }\n\n if (palette.length === 1) {\n return palette[0]!;\n }\n\n const cyclePos = (((position + timeOffset) % 1) + 1) % 1;\n const scaled = cyclePos * palette.length;\n const idx = Math.floor(scaled);\n const t = scaled - idx;\n\n const c1 = palette[idx % palette.length]!;\n const c2 = palette[(idx + 1) % palette.length]!;\n\n return lerpOklab(c1, c2, t);\n}\n\n// TODO: maybe should infer the union type from the variable instead of making the variable respect the predefined union type\nconst TRAIL_STYLES: readonly TrailStyle[] = [\"default\", \"gradient-static\", \"gradient-animated\"];\n\n// FIXME: Should inherit from the actual render option object to avoid drift\nconst BASE_RENDER_OPTION_KEYS: ReadonlySet<string> = new Set([\n \"trailColor\",\n \"trailStyle\",\n \"skeletonColor\",\n]);\n\nconst RENDER_OPTION_KEYS: ReadonlySet<string> = new Set([\n \"trailColor\",\n \"headColor\",\n \"skeletonColor\",\n \"trailStyle\",\n \"headRadius\",\n \"trailWidth\",\n]);\n\n/**\n * Validates options for renderers that support `trailColor`, `trailStyle`, and `skeletonColor`.\n * Throws a `TypeError` if any canvas-only field (`headColor`, `headRadius`, `trailWidth`) is passed.\n */\nexport function validateBaseRenderOptions(partial: BaseRuntimeRenderOptions) {\n for (const key of Object.keys(partial)) {\n if (!BASE_RENDER_OPTION_KEYS.has(key)) {\n throw new TypeError(`[sarmal] setRenderOptions: unsupported key \"${key}\" for this renderer`);\n }\n }\n if (partial.trailColor !== undefined) {\n assertTrailColor(partial.trailColor);\n }\n if (partial.trailStyle !== undefined) {\n assertTrailStyle(partial.trailStyle);\n }\n if (partial.skeletonColor !== undefined) {\n assertSkeletonColor(partial.skeletonColor);\n }\n}\n\n/**\n * Checks a `RuntimeRenderOptions` payload against the library's acceptance criteria.\n *\n * If this throws, no field has been assigned yet on the caller side,\n * so the renderer continues on the previous valid state.\n *\n * Rules:\n * - Unknown keys throw for runtime type safety\n * - `trailColor` must match expected string/Array<string> format\n * - `headColor` must match expected color string format OR `null`\n * - `skeletonColor` must match expected color string format OR the literal `\"transparent\"`.\n * - `trailStyle` must match one of the accepted modes\n *\n * ! Field combinations like `trailColor=\"#ffffff\"` with `trailStyle=\"gradient-animated\"` are NOT rejected\n * They only produce a console warning at the renderer to indicate an unexpected outcome\n */\nexport function validateRenderOptions(partial: RuntimeRenderOptions) {\n for (const key of Object.keys(partial)) {\n if (!RENDER_OPTION_KEYS.has(key)) {\n throw new TypeError(`[sarmal] setRenderOptions: unknown key \"${key}\"`);\n }\n }\n\n if (partial.trailColor !== undefined) {\n assertTrailColor(partial.trailColor);\n }\n if (partial.headColor !== undefined) {\n assertHeadColor(partial.headColor);\n }\n if (partial.skeletonColor !== undefined) {\n assertSkeletonColor(partial.skeletonColor);\n }\n if (partial.trailStyle !== undefined) {\n assertTrailStyle(partial.trailStyle);\n }\n if (partial.headRadius !== undefined) {\n assertHeadRadius(partial.headRadius);\n }\n if (partial.trailWidth !== undefined) {\n assertTrailWidth(partial.trailWidth);\n }\n}\n\nfunction assertTrailColor(value: TrailColor) {\n if (typeof value === \"string\") {\n if (parseColorToOklab(value) === null) {\n throw new TypeError(\n `[sarmal] setRenderOptions: trailColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()), got \"${value}\"`,\n );\n }\n return;\n }\n\n if (Array.isArray(value)) {\n if (value.length < 2) {\n throw new RangeError(\n `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`,\n );\n }\n\n for (let i = 0; i < value.length; i++) {\n const entry = value[i];\n if (typeof entry !== \"string\" || parseColorToOklab(entry) === null) {\n throw new TypeError(\n `[sarmal] setRenderOptions: trailColor[${i}] must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()), got ${JSON.stringify(entry)}`,\n );\n }\n }\n return;\n }\n\n throw new TypeError(\n `[sarmal] setRenderOptions: trailColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()) or an array of color strings, got ${JSON.stringify(value)}`,\n );\n}\n\nfunction assertHeadColor(value: string | null) {\n if (value === null) {\n return;\n }\n\n if (typeof value !== \"string\" || parseColorToOklab(value) === null) {\n throw new TypeError(\n `[sarmal] setRenderOptions: headColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()) or null, got ${JSON.stringify(value)}`,\n );\n }\n}\n\nfunction assertSkeletonColor(value: string) {\n if (value === \"transparent\") {\n return;\n }\n\n if (typeof value !== \"string\" || parseColorToOklab(value) === null) {\n throw new TypeError(\n `[sarmal] setRenderOptions: skeletonColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()) or \"transparent\", got ${JSON.stringify(value)}`,\n );\n }\n}\n\nfunction assertTrailStyle(value: TrailStyle) {\n if (!TRAIL_STYLES.includes(value)) {\n // TODO: perhaps make Trail Style inferred from a variable so it can be spread here to keep it dynamic.\n throw new RangeError(\n `[sarmal] setRenderOptions: trailStyle must be one of \"default\", \"gradient-static\", \"gradient-animated\", got ${JSON.stringify(value)}`,\n );\n }\n}\n\nfunction assertHeadRadius(value: number) {\n if (typeof value !== \"number\") {\n throw new TypeError(\n `[sarmal] setRenderOptions: headRadius must be a number, got ${JSON.stringify(value)}`,\n );\n }\n if (!Number.isFinite(value) || value <= 0) {\n throw new TypeError(\n `[sarmal] setRenderOptions: headRadius must be a finite positive number, got ${value}`,\n );\n }\n}\n\nfunction assertTrailWidth(value: number) {\n if (typeof value !== \"number\") {\n throw new TypeError(\n `[sarmal] setRenderOptions: trailWidth must be a number, got ${JSON.stringify(value)}`,\n );\n }\n\n if (!Number.isFinite(value) || value <= 0) {\n throw new TypeError(\n `[sarmal] setRenderOptions: trailWidth must be a finite positive number, got ${value}`,\n );\n }\n}\n\n/**\n * Resolves the effective solid fill color used by the `\"default\"` trail style.\n * If the caller passed an array, the first entry is used.\n *\n * This is to avoid errors when an array of colors are passed for `trailStyle=\"default\"`\n */\nexport function resolveTrailMainColor(trailColor: TrailColor) {\n return typeof trailColor === \"string\" ? trailColor : trailColor[0]!;\n}\n\n/**\n * Resolves the effective palette used by gradient trail styles.\n *\n * This is to avoid errors when a single color string is passed for gradient trail styles\n */\nexport function resolveTrailPalette(trailColor: TrailColor): string[] {\n return typeof trailColor === \"string\" ? [trailColor] : trailColor;\n}\n\n/**\n * Computes the head color automatically from the current `trailColor` and `trailStyle` pair.\n *\n * Rules:\n * - `\"default\"` style: the head matches the solid trail color.\n * - Gradient styles: the head matches the *last* stop of the palette\n *\n * @returns a CSS-valid color string\n */\nexport function resolveHeadColor(trailColor: TrailColor, trailStyle: TrailStyle): string {\n if (trailStyle === \"default\") {\n return resolveTrailMainColor(trailColor);\n }\n\n const palette = resolveTrailPalette(trailColor);\n const last = palette[palette.length - 1]!;\n const { r, g, b } = colorToRgb(last);\n return `rgb(${r},${g},${b})`;\n}\n\n/**\n * Emits a console warning when `trailColor` and `trailStyle` don't *semantically* match up\n *\n * For example, an array passed with `\"default\"` style (only the first color is used) or\n * a single string passed with a gradient style (the trail renders as a solid color)\n *\n * ! This is only a warning, so the renderer will still produce a valid output\n */\nexport function warnIfTrailColorMismatch(trailColor: TrailColor, trailStyle: TrailStyle): void {\n if (trailStyle === \"default\" && Array.isArray(trailColor)) {\n // biome-ignore lint/suspicious/noConsole: advisory for developer feedback\n console.warn(\n '[sarmal] trailColor is an array but trailStyle is \"default\"; only the first color will be used. Pass a gradient trailStyle to use the whole palette.',\n );\n\n return;\n }\n\n if (trailStyle !== \"default\" && typeof trailColor === \"string\") {\n // biome-ignore lint/suspicious/noConsole: advisory for developer feedback\n console.warn(\n `[sarmal] trailColor is a single color but trailStyle is \"${trailStyle}\"; the trail will render as a solid color. Pass an array of hex colors to use a real gradient.`,\n );\n }\n}\n","import type { CurveDef } from \"./types\";\nimport type { Rgb } from \"./renderer-shared\";\n\nimport { createEngine } from \"./engine\";\nimport {\n computeBoundaries,\n lerpOklab,\n parseColorToRgb,\n rgbToOklab,\n oklabToRgb,\n} from \"./renderer-shared\";\n\nconst DEFAULT_TRAIL_HEX = \"#ec5571\"; // --color-primary\nconst DEFAULT_FPS = 30;\nconst DEFAULT_SIZE = 16; // visual-tested at 12/16/20/24 chars; 16 was the smallest that showed clear curve definition\nconst BRIGHTNESS_HEAD = 1.0;\nconst BRIGHTNESS_TAIL = 0.15;\nconst BRAILLE_BASE = 0x2800;\n\ntype BrailleBit = [number, number];\n\n/**\n * ! 0x40 and 0x80 are not unused. My LLM said `Omitting them reduces the grid to 2×3 and loses a quarter of the resolution`\n * ! Simple mode with one char per cell does not produce high enough fidelity, so Braille is the preffered approach.\n */\nconst BRAILLE_BIT: Array<BrailleBit> = [\n [0x01, 0x08],\n [0x02, 0x10],\n [0x04, 0x20],\n [0x40, 0x80],\n];\n\nexport type ColorCapability = \"truecolor\" | \"256-color\" | \"monochrome\";\ntype DotCol = 0 | 1;\ntype DotRow = 0 | 1 | 2 | 3;\ntype Boundary = { screenX: number; screenY: number };\n\nexport interface TerminalSarmalOptions {\n size?: number;\n fps?: number;\n trailColor?: string;\n headColor?: string;\n speed?: number;\n}\n\nexport function brailleChar(bits: number) {\n return String.fromCodePoint(BRAILLE_BASE + (bits & 0xff));\n}\n\nexport function brailleBit(row: number, col: number) {\n if (row < 0 || row >= BRAILLE_BIT.length || col < 0 || col >= (BRAILLE_BIT[0]?.length ?? 0)) {\n return 0;\n }\n return BRAILLE_BIT[row]![col]!;\n}\n\nexport function dotToCell(\n dotCol: number,\n dotRow: number,\n): {\n charCol: number;\n charRow: number;\n dotColInCell: DotCol;\n dotRowInCell: DotRow;\n} {\n return {\n charCol: Math.floor(dotCol / 2),\n charRow: Math.floor(dotRow / 4),\n dotColInCell: (dotCol % 2) as DotCol,\n dotRowInCell: (dotRow % 4) as DotRow,\n };\n}\n\nexport function detectColor(): ColorCapability {\n const colorterm = (process.env.COLORTERM ?? \"\").toLowerCase();\n\n if (colorterm === \"truecolor\" || colorterm === \"24bit\") {\n return \"truecolor\";\n }\n\n const term = (process.env.TERM ?? \"\").toLowerCase();\n\n if (term.includes(\"256color\")) {\n return \"256-color\";\n }\n\n if (term.includes(\"truecolor\") || term.includes(\"24bit\")) {\n return \"truecolor\";\n }\n\n if (term === \"\" || term === \"linux\" || term === \"dumb\") {\n return \"monochrome\";\n }\n\n return \"256-color\";\n}\n\nexport function rgbTo256(r: number, g: number, b: number) {\n const avg = Math.round((r + g + b) / 3);\n\n if (Math.abs(r - avg) <= 4 && Math.abs(g - avg) <= 4 && Math.abs(b - avg) <= 4) {\n if (avg <= 8) {\n return 16;\n }\n return 232 + Math.min(23, Math.round((avg - 8) / 10));\n }\n\n const ri = Math.round((r / 255) * 5);\n const gi = Math.round((g / 255) * 5);\n const bi = Math.round((b / 255) * 5);\n return 16 + 36 * ri + 6 * gi + bi;\n}\n\nexport function dimRgb(rgb: Rgb, brightness: number): Rgb {\n const t = 1 - Math.max(0, Math.min(1, brightness));\n if (t <= 0) {\n return rgb;\n }\n if (t >= 1) {\n return { r: 0, g: 0, b: 0 };\n }\n return oklabToRgb(lerpOklab(rgbToOklab(rgb), { L: 0, a: 0, b: 0 }, t));\n}\n\nexport function dimColor(color: string, brightness: number): Rgb {\n return dimRgb(parseColorToRgb(color)!, brightness);\n}\n\nconst AR = \"\\x1B[0m\";\n\nfunction ansiTruecolorFg(r: number, g: number, b: number) {\n return `\\x1B[38;2;${r};${g};${b}m`;\n}\n\nfunction ansi256Fg(code: number) {\n return `\\x1B[38;5;${code}m`;\n}\n\nfunction ansiColor(r: number, g: number, b: number, colorCap: ColorCapability) {\n if (colorCap === \"truecolor\") {\n return ansiTruecolorFg(r, g, b);\n }\n\n if (colorCap === \"256-color\") {\n return ansi256Fg(rgbTo256(r, g, b));\n }\n\n return \"\";\n}\n\ninterface BrailleCell {\n bits: number;\n brightness: number;\n isHead: boolean;\n}\n\nfunction snapCol(col: number, max: number) {\n return Math.max(0, Math.min(max - 1, Math.round(col)));\n}\n\nfunction applyBoundary(\n x: number,\n y: number,\n scale: number,\n offsetX: number,\n offsetY: number,\n): Boundary {\n return {\n screenX: x * scale + offsetX,\n screenY: y * scale + offsetY,\n };\n}\n\nfunction renderFrame(\n trail: Array<{ x: number; y: number }>,\n trailCount: number,\n charWidth: number,\n charHeight: number,\n scale: number,\n offsetX: number,\n offsetY: number,\n trailRgb: Rgb,\n headRgb: Rgb,\n colorCap: ColorCapability,\n): string {\n const dotWidth = charWidth * 2;\n const dotHeight = charHeight * 4;\n\n const grid: BrailleCell[][] = Array.from({ length: charHeight }, () =>\n Array.from(\n { length: charWidth },\n (): BrailleCell => ({ bits: 0, brightness: 0, isHead: false }),\n ),\n );\n\n for (let i = 0; i < trailCount; i++) {\n const pt = trail[i]!;\n const t = trailCount > 1 ? i / (trailCount - 1) : 1;\n const brightness = BRIGHTNESS_TAIL + (BRIGHTNESS_HEAD - BRIGHTNESS_TAIL) * t;\n const { screenX, screenY } = applyBoundary(pt.x, pt.y, scale, offsetX, offsetY);\n const dotCol = snapCol(screenX, dotWidth);\n const dotRow = snapCol(screenY, dotHeight);\n const cell = dotToCell(dotCol, dotRow);\n\n if (\n cell.charRow < 0 ||\n cell.charRow >= charHeight ||\n cell.charCol < 0 ||\n cell.charCol >= charWidth\n ) {\n continue;\n }\n\n const c = grid[cell.charRow]![cell.charCol]!;\n c.bits |= BRAILLE_BIT[cell.dotRowInCell]![cell.dotColInCell]!;\n if (brightness > c.brightness) {\n c.brightness = brightness;\n }\n\n if (i === trailCount - 1) {\n c.isHead = true;\n }\n }\n\n if (colorCap !== \"monochrome\") {\n return renderColorFrame(grid, charWidth, charHeight, trailRgb, headRgb, colorCap);\n }\n\n return renderMonochromeFrame(grid, charWidth, charHeight);\n}\n\nfunction renderColorFrame(\n grid: BrailleCell[][],\n charWidth: number,\n charHeight: number,\n trailRgb: Rgb,\n headRgb: Rgb,\n colorCap: ColorCapability,\n): string {\n const lines: string[] = [];\n\n for (let row = 0; row < charHeight; row++) {\n let line = \"\";\n for (let col = 0; col < charWidth; col++) {\n const cell = grid[row]![col]!;\n if (cell.bits === 0) {\n line += \" \";\n continue;\n }\n\n const ch = brailleChar(cell.bits);\n if (cell.isHead) {\n line += ansiColor(headRgb.r, headRgb.g, headRgb.b, colorCap) + ch + AR;\n } else {\n const dimmed = dimRgb(trailRgb, cell.brightness);\n line += ansiColor(dimmed.r, dimmed.g, dimmed.b, colorCap) + ch + AR;\n }\n }\n lines.push(line);\n }\n return lines.join(\"\\n\");\n}\n\nfunction renderMonochromeFrame(\n grid: Array<Array<BrailleCell>>,\n charWidth: number,\n charHeight: number,\n): string {\n const lines: string[] = [];\n for (let row = 0; row < charHeight; row++) {\n let line = \"\";\n\n for (let col = 0; col < charWidth; col++) {\n const cell = grid[row]![col]!;\n\n if (cell.bits === 0) {\n line += \" \";\n continue;\n }\n\n line += brailleChar(cell.bits);\n }\n lines.push(line);\n }\n return lines.join(\"\\n\");\n}\n\nexport function terminalSarmal(\n stream: NodeJS.WriteStream,\n curveDef: CurveDef,\n options?: TerminalSarmalOptions,\n) {\n if (!stream.isTTY) {\n return () => {};\n }\n\n const size = options?.size ?? DEFAULT_SIZE;\n const fps = options?.fps ?? DEFAULT_FPS;\n const trailHex = options?.trailColor ?? DEFAULT_TRAIL_HEX;\n const headHex = options?.headColor ?? trailHex;\n const userSpeed = options?.speed;\n const colorCap = detectColor();\n\n const trailRgb = parseColorToRgb(trailHex)!;\n const headRgb = parseColorToRgb(headHex)!;\n\n const engine = createEngine(curveDef);\n if (userSpeed !== undefined) {\n engine.setSpeed(userSpeed);\n }\n\n const charWidth = size;\n const charHeight = Math.ceil(size / 2);\n\n const skeleton = engine.getSarmalSkeleton();\n const b = computeBoundaries(skeleton, charWidth * 2, charHeight * 4, 1);\n if (!b) {\n return () => {};\n }\n\n const { scale, offsetX, offsetY } = b;\n\n let running = true;\n let firstFrame = true;\n\n stream.write(\"\\x1B[?25l\");\n\n function cleanup() {\n running = false;\n stream.write(\"\\x1B[?25h\");\n stream.write(\"\\n\");\n }\n\n const onSigint = () => {\n cleanup();\n process.exit(0);\n };\n process.on(\"SIGINT\", onSigint);\n\n function render() {\n const delta = 1 / fps;\n const trail = engine.tick(delta);\n const trailCount = engine.trailCount;\n\n const frame = renderFrame(\n trail,\n trailCount,\n charWidth,\n charHeight,\n scale,\n offsetX,\n offsetY,\n trailRgb,\n headRgb,\n colorCap,\n );\n\n if (firstFrame) {\n firstFrame = false;\n stream.write(frame + \"\\n\");\n return;\n }\n\n const rows = charHeight;\n stream.write(`\\x1B[${rows}A`);\n const lines = frame.split(\"\\n\");\n for (const line of lines) {\n stream.write(line + \"\\n\");\n }\n }\n\n render();\n\n const interval = setInterval(() => {\n if (!running) {\n return;\n }\n\n render();\n }, 1000 / fps);\n\n function stop() {\n clearInterval(interval);\n process.off(\"SIGINT\", onSigint);\n cleanup();\n }\n\n return stop;\n}\n"]}
1
+ {"version":3,"sources":["../src/engine.ts","../src/renderer-shared.ts","../src/terminal.ts"],"names":[],"mappings":"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","file":"terminal.cjs","sourcesContent":["import type { CurveDef, Engine, JumpOptions, MorpStrategy, Point, SeekOptions } from \"./types\";\n\nconst TWO_PI = Math.PI * 2;\nconst POINTS_PER_PERIOD_UNIT = 50;\n\ntype SpeedTransition = {\n from: number;\n to: number;\n elapsed: number;\n duration: number;\n resolve: () => void;\n reject: (err: Error) => void;\n};\n\n/** Linearly interpolate from start to end by factor t (0→1) */\nfunction lerp(start: number, end: number, t: number): number {\n return start + (end - start) * t;\n}\n\n/** Reused across all curve fn calls but params is never populated, allocation is wasteful */\nconst EMPTY_PARAMS: Record<string, number> = {};\n\n/**\n * A fixed-size list of points with first in, last out method\n * The oldest entry is automatically discarded when the list is at capacity\n *\n * Note: `result.length` is *never* changed,\n * so callers use the separate `count` getter to know valid size\n */\nclass CircularBuffer {\n private data: Array<Point>;\n private result: Array<Point>;\n private capacity: number;\n private head: number = 0;\n private count: number = 0;\n\n constructor(capacity: number) {\n this.capacity = capacity;\n this.data = Array.from({ length: capacity }, () => ({ x: 0, y: 0 }));\n this.result = Array.from({ length: capacity }, () => ({ x: 0, y: 0 }));\n }\n\n /** Mutates in-place */\n push(x: number, y: number): void {\n const slot = this.data[this.head]!;\n\n slot.x = x;\n slot.y = y;\n this.head = (this.head + 1) % this.capacity;\n\n if (this.count < this.capacity) {\n this.count++;\n }\n }\n\n /**\n * Copies ordered points into the pre-allocated result buffer and returns it\n * Note: The *same* array reference is returned every call,\n * so `result.length` is also always `capacity`\n */\n toArray(): Array<Point> {\n const start = this.count < this.capacity ? 0 : this.head;\n\n for (let i = 0; i < this.count; i++) {\n const src = this.data[(start + i) % this.capacity]!;\n const dst = this.result[i]!;\n dst.x = src.x;\n dst.y = src.y;\n }\n\n return this.result;\n }\n\n clear(): void {\n this.head = 0;\n this.count = 0;\n }\n\n get length() {\n return this.count;\n }\n}\n\n/**\n * Creates the core simulation engine for a sarmal\n *\n * it runs a clock (`phase`), asks the curve for the current Point position at that time,\n * and remembers the last N positions so the renderer can draw the trail\n *\n * The engine is only responsible for math coordinates,\n * so it is not responsible for drawing or colors\n *\n * @param curveDef A curve definition\n * @param trailLength default: `120`\n */\n/** Normalised resolution of a CurveDef, with required fields filled in */\ntype ResolvedCurve = {\n name: string;\n fn: CurveDef[\"fn\"];\n period: number;\n speed: number;\n skeleton?: CurveDef[\"skeleton\"];\n skeletonFn?: CurveDef[\"skeletonFn\"];\n};\n\nfunction resolveCurve(curveDef: CurveDef): ResolvedCurve {\n const period = curveDef.period ?? TWO_PI;\n\n if (!Number.isFinite(period) || period <= 0) {\n throw new RangeError(`[sarmal] period must be a positive finite number, got ${period}`);\n }\n\n const speed = curveDef.speed ?? 1;\n\n if (!Number.isFinite(speed)) {\n throw new RangeError(`[sarmal] speed must be a finite number, got ${speed}`);\n }\n\n return {\n name: curveDef.name,\n fn: curveDef.fn,\n period,\n speed,\n skeleton: curveDef.skeleton,\n skeletonFn: curveDef.skeletonFn,\n };\n}\n\nexport function createEngine(curveDef: CurveDef, trailLength: number = 120): Engine {\n if (!Number.isFinite(trailLength) || trailLength <= 0) {\n throw new RangeError(\n `[sarmal] trailLength must be a positive finite number, got ${trailLength}`,\n );\n }\n\n let curve = resolveCurve(curveDef);\n const trail = new CircularBuffer(trailLength);\n let phase = 0;\n let actualTime = 0;\n let userSpeedOverride: number | null = null;\n\n // Morph state which is `null` when not morphing\n let morphCurveB: ResolvedCurve | null = null;\n let _morphAlpha: number | null = null;\n let _morphStrategy: MorpStrategy = \"normalized\";\n\n // Speed transition state which is `null` when not transitioning\n let _speedTransition: SpeedTransition | null = null;\n\n /** Samples a resolved curve's skeleton at position `samplePhase` */\n function sampleSkeleton(c: ResolvedCurve, samplePhase: number): Point {\n if (c.skeletonFn) {\n return c.skeletonFn(samplePhase);\n }\n\n if (c.skeleton === \"live\") {\n return c.fn(samplePhase, actualTime, EMPTY_PARAMS);\n }\n\n return c.fn(samplePhase, 0, EMPTY_PARAMS);\n }\n\n return {\n tick(deltaTime: number): Array<Point> {\n if (_speedTransition !== null) {\n // tick() receives dt in seconds, but SpeedTransition.duration is in milliseconds.\n // Convert dt to ms so the elapsed/duration ratio is dimensionless.\n _speedTransition.elapsed += deltaTime * 1000;\n const alpha = Math.min(_speedTransition.elapsed / _speedTransition.duration, 1);\n userSpeedOverride = lerp(_speedTransition.from, _speedTransition.to, alpha);\n if (alpha >= 1) {\n userSpeedOverride = _speedTransition.to;\n _speedTransition.resolve();\n _speedTransition = null;\n }\n }\n\n let effectiveSpeed = userSpeedOverride ?? curve.speed;\n if (morphCurveB !== null && _morphAlpha !== null) {\n effectiveSpeed = lerp(effectiveSpeed, morphCurveB.speed, _morphAlpha);\n }\n phase = (phase + effectiveSpeed * deltaTime) % curve.period;\n actualTime += deltaTime;\n\n if (morphCurveB !== null && _morphAlpha !== null) {\n const a = curve.fn(phase, actualTime, EMPTY_PARAMS);\n const phaseB =\n _morphStrategy === \"normalized\" ? (phase / curve.period) * morphCurveB.period : phase;\n const b = morphCurveB.fn(phaseB, actualTime, EMPTY_PARAMS);\n trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);\n } else {\n const point = curve.fn(phase, actualTime, EMPTY_PARAMS);\n trail.push(point.x, point.y);\n }\n\n return trail.toArray();\n },\n\n get trailCount() {\n return trail.length;\n },\n\n get trailLength() {\n return trailLength;\n },\n\n get isLiveSkeleton() {\n return curve.skeleton === \"live\";\n },\n\n get morphAlpha() {\n return _morphAlpha;\n },\n\n reset() {\n phase = 0;\n actualTime = 0;\n trail.clear();\n },\n\n jump(newPhase: number, { clearTrail = false }: JumpOptions = {}) {\n phase = ((newPhase % curve.period) + curve.period) % curve.period;\n\n if (clearTrail) {\n trail.clear();\n }\n },\n\n seek(\n targetPhase: number,\n { wrap = false, step = curve.period / trailLength }: SeekOptions = {},\n ) {\n const advance = curve.speed * step;\n const target = ((targetPhase % curve.period) + curve.period) % curve.period;\n const targetTime = target / curve.speed;\n\n phase = target;\n actualTime = targetTime;\n trail.clear();\n\n const pointsFromStart = Math.floor(target / advance) + 1;\n const count = wrap ? trailLength : Math.min(trailLength, pointsFromStart);\n\n for (let i = count - 1; i >= 0; i--) {\n const samplePhase = target - i * advance;\n const wrappedPhase = ((samplePhase % curve.period) + curve.period) % curve.period;\n const elapsed = targetTime - i * step;\n const point = curve.fn(wrappedPhase, elapsed, EMPTY_PARAMS);\n\n trail.push(point.x, point.y);\n }\n },\n\n startMorph(target: CurveDef, strategy: MorpStrategy = \"normalized\") {\n const resolvedTarget = resolveCurve(target);\n\n if (morphCurveB !== null && _morphAlpha !== null) {\n const frozenAlpha = _morphAlpha;\n const frozenA = curve;\n const frozenB = morphCurveB;\n const frozenStrategy = _morphStrategy;\n\n curve = {\n ...frozenB,\n fn: (samplePhase: number, elapsed: number, params: Record<string, number>) => {\n const a = frozenA.fn(samplePhase, elapsed, params);\n const phaseB =\n frozenStrategy === \"normalized\"\n ? (samplePhase / frozenA.period) * frozenB.period\n : samplePhase;\n const b = frozenB.fn(phaseB, elapsed, params);\n\n return {\n x: a.x + (b.x - a.x) * frozenAlpha,\n y: a.y + (b.y - a.y) * frozenAlpha,\n };\n },\n };\n }\n\n _morphStrategy = strategy;\n morphCurveB = resolvedTarget;\n _morphAlpha = 0;\n },\n\n setMorphAlpha(alpha: number) {\n _morphAlpha = alpha;\n },\n\n completeMorph() {\n if (morphCurveB !== null) {\n // Normalized strategy drives `curveB` at `phaseB` = `(phase / periodA) * periodB`\n // Remap `phase` so the trail continues from the same position on `curveB`,\n // not from a raw `phase` value that belongs to `curveA`'s smaller range.\n if (_morphStrategy === \"normalized\" && curve.period !== morphCurveB.period) {\n phase = (phase / curve.period) * morphCurveB.period;\n }\n curve = morphCurveB;\n }\n morphCurveB = null;\n _morphAlpha = null;\n },\n\n getSarmalSkeleton(): Array<Point> {\n const steps = Math.ceil(curve.period * POINTS_PER_PERIOD_UNIT);\n // oxlint-disable-next-line unicorn/no-new-array -- array is pre-allocated, filled immediately below\n const points: Array<Point> = new Array(steps);\n\n if (morphCurveB !== null && _morphAlpha !== null) {\n for (let i = 0; i < steps; i++) {\n const samplePhase = (i / (steps - 1)) * curve.period;\n const a = sampleSkeleton(curve, samplePhase);\n const phaseB =\n _morphStrategy === \"normalized\"\n ? (samplePhase / curve.period) * morphCurveB.period\n : samplePhase;\n const b = sampleSkeleton(morphCurveB, phaseB);\n\n points[i] = {\n x: a.x + (b.x - a.x) * _morphAlpha,\n y: a.y + (b.y - a.y) * _morphAlpha,\n };\n }\n return points;\n }\n\n for (let i = 0; i < steps; i++) {\n const samplePhase = (i / (steps - 1)) * curve.period;\n points[i] = sampleSkeleton(curve, samplePhase);\n }\n\n return points;\n },\n\n setSpeed(speed: number): void {\n if (!Number.isFinite(speed)) {\n throw new Error(\"speed must be a finite number\");\n }\n if (_speedTransition !== null) {\n _speedTransition.reject(new Error(\"Speed transition cancelled\"));\n _speedTransition = null;\n }\n userSpeedOverride = speed;\n },\n\n getSpeed(): number {\n return userSpeedOverride ?? curve.speed;\n },\n\n resetSpeed(): void {\n userSpeedOverride = null;\n },\n\n setSpeedOver(speed: number, duration: number): Promise<void> {\n if (!Number.isFinite(speed)) {\n throw new Error(\"speed must be a finite number\");\n }\n if (!Number.isFinite(duration) || duration <= 0) {\n throw new Error(\"duration must be a finite number greater than 0\");\n }\n\n if (_speedTransition !== null) {\n _speedTransition.reject(new Error(\"Speed transition cancelled\"));\n _speedTransition = null;\n }\n\n const from = userSpeedOverride ?? curve.speed;\n\n return new Promise<void>((resolve, reject) => {\n _speedTransition = { from, to: speed, elapsed: 0, duration, resolve, reject };\n });\n },\n\n cancelSpeedTransition(): void {\n if (_speedTransition !== null) {\n _speedTransition.reject(new Error(\"Speed transition cancelled\"));\n _speedTransition = null;\n }\n },\n };\n}\n","import type {\n BaseRuntimeRenderOptions,\n Engine,\n Point,\n RuntimeRenderOptions,\n TrailColor,\n TrailStyle,\n} from \"./types\";\n\nexport const DEFAULT_MORPH_DURATION_MS = 300;\nexport const DEFAULT_SKELETON_OPACITY = 0.15;\n/** Fraction of the bounding-box dimension added as proportional padding on each side when fitting the curve. */\nexport const FIT_PADDING = 0.1;\nexport const FIT_PADDING_MIN = 4;\n/** Higher values = sharper fade near the tail, more of the trail appears faint */\nexport const TRAIL_FADE_CURVE = 1.5;\nexport const TRAIL_MAX_OPACITY = 0.88;\n/** Pixel-space stroke/line width at the tail which the SVG renderer overrides with viewBox unit values */\nexport const TRAIL_MIN_WIDTH = 0.5;\n/** Pixel-space stroke/line width at the head which the SVG renderer overrides with viewBox unit values */\nexport const TRAIL_MAX_WIDTH = 2.5;\n\nexport interface TrailPoint {\n x: number;\n y: number;\n}\n\n/**\n * Computes the unit tangent vector at a point on the trail.\n * - Interior points: central difference (previous -> next)\n * - Endpoints: forward/backward difference\n */\nexport function computeTangent(trail: TrailPoint[], i: number): TrailPoint {\n const count = trail.length;\n if (count < 2) {\n return { x: 1, y: 0 };\n }\n\n if (i === 0) {\n const dx = trail[1]!.x - trail[0]!.x;\n const dy = trail[1]!.y - trail[0]!.y;\n const len = Math.sqrt(dx * dx + dy * dy) || 1;\n return { x: dx / len, y: dy / len };\n }\n\n if (i === count - 1) {\n const dx = trail[count - 1]!.x - trail[count - 2]!.x;\n const dy = trail[count - 1]!.y - trail[count - 2]!.y;\n const len = Math.sqrt(dx * dx + dy * dy) || 1;\n return { x: dx / len, y: dy / len };\n }\n\n const dx = trail[i + 1]!.x - trail[i - 1]!.x;\n const dy = trail[i + 1]!.y - trail[i - 1]!.y;\n const len = Math.sqrt(dx * dx + dy * dy) || 1;\n return { x: dx / len, y: dy / len };\n}\n\n/**\n * Computes the unit normal vector at a point on the trail.\n * The normal is perpendicular to the tangent, rotated 90° counter-clockwise\n */\nexport function computeNormal(trail: TrailPoint[], i: number): TrailPoint {\n const tangent = computeTangent(trail, i);\n return { x: -tangent.y, y: tangent.x };\n}\n\n/** The four pixel-space corners and per-segment style values for one ribbon quad */\nexport interface TrailQuad {\n /** Left corner at the tail end of this segment */\n l0x: number;\n l0y: number;\n /** Right corner at the tail end of this segment */\n r0x: number;\n r0y: number;\n /** Left corner at the head end of this segment */\n l1x: number;\n l1y: number;\n /** Right corner at the head end of this segment */\n r1x: number;\n r1y: number;\n /** Fill opacity for this segment (0–1) */\n opacity: number;\n /** Position along the trail (0 = tail, 1 = head) */\n progress: number;\n}\n\n/**\n * Computes the quad corners and style for one ribbon segment in the renderer's coordinate space (pixel-space for canvas, viewBox-space for SVG)\n *\n * @param trail Full trail array\n * @param i Segment index (draws from point i to point i+1)\n * @param trailCount Number of active trail points\n * @param toX Convert a trail point to its pixel X coordinate\n * @param toY Convert a trail point to its pixel Y coordinate\n *\n * @see https://mattdesl.svbtle.com/drawing-lines-is-hard\n * DesLauriers: \"Triangulated Lines\" - expand points outward by half\n * the thickness on either side using normals to create thick lines.\n */\nexport function computeTrailQuad(\n trail: TrailPoint[],\n i: number,\n trailCount: number,\n toX: (p: TrailPoint) => number,\n toY: (p: TrailPoint) => number,\n minWidth = TRAIL_MIN_WIDTH,\n maxWidth = TRAIL_MAX_WIDTH,\n): TrailQuad {\n const progress = i / (trailCount - 1);\n const nextProgress = (i + 1) / (trailCount - 1);\n const opacity = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;\n const w0 = (minWidth + progress * (maxWidth - minWidth)) / 2;\n const w1 = (minWidth + nextProgress * (maxWidth - minWidth)) / 2;\n\n const curr = trail[i]!;\n const next = trail[i + 1]!;\n const n0 = computeNormal(trail, i);\n const n1 = computeNormal(trail, i + 1);\n\n const cx = toX(curr);\n const cy = toY(curr);\n const nx = toX(next);\n const ny = toY(next);\n\n return {\n l0x: cx + n0.x * w0,\n l0y: cy + n0.y * w0,\n r0x: cx - n0.x * w0,\n r0y: cy - n0.y * w0,\n l1x: nx + n1.x * w1,\n l1y: ny + n1.y * w1,\n r1x: nx - n1.x * w1,\n r1y: ny - n1.y * w1,\n opacity,\n progress,\n };\n}\n\nexport interface BoundaryResult {\n scale: number;\n offsetX: number;\n offsetY: number;\n}\n\n/**\n * Computes how to map engine coordinates into a viewport of the given logical size.\n * ! Returns `null` if `pts` is empty\n * ! Throws if all points are identical\n *\n * Padding per side is `max(FIT_PADDING * dim, minPaddingPx)`, so the stricter constraint wins.\n * `minPaddingPx` defaults to `FIT_PADDING_MIN` (4px) for pixel-space callers.\n * Pass `0` when the logical space is itself a normalized viewBox (e.g. SVG export).\n */\nexport function computeBoundaries(\n pts: Point[],\n logicalWidth: number,\n logicalHeight: number,\n minPaddingPx = FIT_PADDING_MIN,\n): BoundaryResult | null {\n if (pts.length === 0) {\n return null;\n }\n\n const first = pts[0]!;\n let minX = first.x,\n maxX = first.x,\n minY = first.y,\n maxY = first.y;\n\n for (const p of pts) {\n if (p.x < minX) {\n minX = p.x;\n }\n if (p.x > maxX) {\n maxX = p.x;\n }\n if (p.y < minY) {\n minY = p.y;\n }\n if (p.y > maxY) {\n maxY = p.y;\n }\n }\n\n const w = maxX - minX;\n const h = maxY - minY;\n\n if (w === 0 && h === 0) {\n throw new Error(\n \"[sarmal] Degenerate curve: all skeleton points are identical. \" +\n \"Check that your curve fn returns distinct points for different values of t.\",\n );\n }\n\n const scaleXProportional = logicalWidth / (w * (1 + FIT_PADDING * 2));\n const scaleYProportional = logicalHeight / (h * (1 + FIT_PADDING * 2));\n\n const scaleXMinPadding = (logicalWidth - minPaddingPx * 2) / w;\n const scaleYMinPadding = (logicalHeight - minPaddingPx * 2) / h;\n\n const scale = Math.min(\n scaleXProportional,\n scaleYProportional,\n scaleXMinPadding,\n scaleYMinPadding,\n );\n\n return {\n scale,\n offsetX: (logicalWidth - w * scale) / 2 - minX * scale,\n offsetY: (logicalHeight - h * scale) / 2 - minY * scale,\n };\n}\n\n/**\n * Returns the engine methods that are pure pass-throughs on both renderers\n * The engine does not use `this`, so direct assignment is safe\n */\nexport function enginePassthroughs(engine: Engine) {\n return {\n jump: engine.jump,\n seek: engine.seek,\n setSpeed: engine.setSpeed,\n getSpeed: engine.getSpeed,\n resetSpeed: engine.resetSpeed,\n setSpeedOver: engine.setSpeedOver,\n getSarmalSkeleton: engine.getSarmalSkeleton,\n };\n}\n\n/**\n * Can be passed directly to `trailColor`,\n * or can be mixed/sliced before passing as a prop.\n */\nexport const palettes = {\n bard: [\"#a855f7\", \"#3b82f6\", \"#14b8a6\", \"#ec4899\"],\n carnival: [\"#ff6b6b\", \"#4ecdc4\", \"#ffe66d\"],\n ocean: [\"#1e3a8a\", \"#06b6d4\", \"#22d3ee\", \"#e0f2fe\"],\n sunset: [\"#f97316\", \"#dc2626\", \"#9333ea\", \"#f472b6\"],\n ice: [\"#1e3a8a\", \"#67e8f9\"],\n rocketpop: [\"#08b8cd\", \"#ffffff\", \"#ff001f\"],\n neon: [\"#00e5ff\", \"#7c3aed\", \"#e040fb\"],\n vaporwave: [\"#ff71ce\", \"#01cdfe\", \"#b967ff\"],\n pastel: [\"#c4b5fd\", \"#fbcfe8\", \"#bae6fd\"],\n sakura: [\"#fff1f2\", \"#fda4af\", \"#fb7185\"],\n} as const satisfies Record<string, Array<string>>;\nexport type SarmalPalette = keyof typeof palettes; // TODO: reconsider naming convention. Should this represent the value type instead of the key union?\n\n/** RGB color components */\nexport interface Rgb {\n r: number;\n g: number;\n b: number;\n}\n\n/** Converts a hex color string to RGB components */\nexport function hexToRgb(hex: string): Rgb {\n const n = parseInt(hex.slice(1), 16);\n return { r: n >> 16, g: (n >> 8) & 255, b: n & 255 };\n}\n\nconst HEX_3_RE = /^#([0-9a-fA-F]{3})$/;\nconst HEX_6_RE = /^#([0-9a-fA-F]{6})$/;\nconst HEX_8_RE = /^#([0-9a-fA-F]{8})$/;\nconst RGB_RE =\n /^rgba?\\(\\s*(-?\\d{1,3})\\s*,\\s*(-?\\d{1,3})\\s*,\\s*(-?\\d{1,3})(?:\\s*,\\s*[\\d.]+)?\\s*\\)$/i;\n\n/**\n * Parses a color string in any supported format into Rgb components.\n * Returns `null` for unrecognized or malformed input.\n *\n * Supported formats:\n * - 3-digit hex: #rgb\n * - 6-digit hex: #rrggbb\n * - 8-digit hex: #rrggbbaa (alpha is silently stripped)\n * - rgb(r, g, b) channels clamped to 0–255\n * - rgba(r, g, b, a) alpha is silently stripped\n */\nexport function parseColorToRgb(s: string): Rgb | null {\n const trimmed = s.trim();\n\n const m3 = HEX_3_RE.exec(trimmed);\n if (m3) {\n const [r, g, b] = m3[1]!;\n return hexToRgb(`#${r}${r}${g}${g}${b}${b}`);\n }\n\n const m6 = HEX_6_RE.exec(trimmed);\n if (m6) {\n return hexToRgb(trimmed);\n }\n\n const m8 = HEX_8_RE.exec(trimmed);\n if (m8) {\n return hexToRgb(`#${trimmed.slice(1, 7)}`);\n }\n\n const mRgb = RGB_RE.exec(trimmed);\n if (mRgb) {\n return {\n r: Math.max(0, Math.min(255, parseInt(mRgb[1]!, 10))),\n g: Math.max(0, Math.min(255, parseInt(mRgb[2]!, 10))),\n b: Math.max(0, Math.min(255, parseInt(mRgb[3]!, 10))),\n };\n }\n\n return null;\n}\n\nconst OKLCH_RE = /^oklch\\(\\s*([\\d.]+)\\s+([\\d.]+)\\s+([\\d.]+)(?:\\s*\\/\\s*[\\d.]+)?\\s*\\)$/i;\n\n/**\n * Parses an oklch() color string directly to Oklab.\n * Returns `null` for unrecognized or malformed input.\n *\n * Accepted syntax (subset of CSS Color 4):\n * - oklch(L C H) bare floats, L clamped to 0–1, C clamped to 0–0.4, H in degrees\n * - oklch(L C H / alpha) alpha silently stripped\n *\n * ! Not supported: percentages, `none` keyword, negative hues\n */\nexport function parseOklchToOklab(s: string): Oklab | null {\n const m = OKLCH_RE.exec(s.trim());\n if (!m) {\n return null;\n }\n\n const L = parseFloat(m[1]!);\n const C = parseFloat(m[2]!);\n const H = parseFloat(m[3]!);\n\n if (Number.isNaN(L) || Number.isNaN(C) || Number.isNaN(H)) {\n return null;\n }\n\n const clampedL = Math.max(0, Math.min(1, L));\n const clampedC = Math.max(0, Math.min(0.4, C));\n const H_rad = H * (Math.PI / 180);\n\n return {\n L: clampedL,\n a: clampedC * Math.cos(H_rad),\n b: clampedC * Math.sin(H_rad),\n };\n}\n\n/**\n * Unified color-to-Oklab entry point.\n * Tries oklch() first (direct to Oklab), then falls back through the sRGB path.\n */\nexport function parseColorToOklab(s: string): Oklab | null {\n const oklab = parseOklchToOklab(s);\n if (oklab !== null) {\n return oklab;\n }\n\n const rgb = parseColorToRgb(s);\n if (rgb === null) {\n return null;\n }\n\n return rgbToOklab(rgb);\n}\n\n/** Converts any accepted color string to Rgb. Throws if the format is unrecognized. */\nexport function colorToRgb(color: string): Rgb {\n const rgb = parseColorToRgb(color);\n if (rgb !== null) {\n return rgb;\n }\n\n const lab = parseOklchToOklab(color);\n if (lab !== null) {\n return oklabToRgb(lab);\n }\n\n throw new Error(`[sarmal] unrecognized color \"${color}\"`);\n}\n\n/** sRGB byte (0–255) to linear light (0–1)\n * @see {@link https://bottosson.github.io/posts/oklab/}\n */\nfunction srgbByteToLinear(c: number): number {\n const n = c / 255;\n return n <= 0.04045 ? n / 12.92 : Math.pow((n + 0.055) / 1.055, 2.4);\n}\n\n/** Linear light (0–1) to sRGB byte (0–255), gamma-compressed and clamped */\nfunction linearToSrgbByte(c: number): number {\n const v = Math.max(0, Math.min(1, c));\n return Math.round((v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055) * 255);\n}\n\nexport interface Oklab {\n L: number;\n a: number;\n b: number;\n}\n\n/**\n * sRGB Rgb to OKLab\n * @see {@link https://bottosson.github.io/posts/oklab/}\n */\nexport function rgbToOklab({ r, g, b }: Rgb): Oklab {\n const rl = srgbByteToLinear(r),\n gl = srgbByteToLinear(g),\n bl = srgbByteToLinear(b);\n const l = Math.cbrt(0.4122214708 * rl + 0.5363325363 * gl + 0.0514459929 * bl);\n const m = Math.cbrt(0.2119034982 * rl + 0.6806995451 * gl + 0.1073969566 * bl);\n const s = Math.cbrt(0.0883024619 * rl + 0.2817188376 * gl + 0.6299787005 * bl);\n\n return {\n L: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n };\n}\n\n/** OKLab to sRGB Rgb, out-of-gamut values clamped to 0–255 */\nexport function oklabToRgb({ L, a, b }: Oklab): Rgb {\n const l_ = L + 0.3963377774 * a + 0.2158037573 * b;\n const m_ = L - 0.1055613458 * a - 0.0638541728 * b;\n const s_ = L - 0.0894841775 * a - 1.29145603 * b;\n const l = l_ * l_ * l_,\n m = m_ * m_ * m_,\n s = s_ * s_ * s_;\n\n return {\n r: linearToSrgbByte(+4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),\n g: linearToSrgbByte(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),\n b: linearToSrgbByte(-0.0041960863 * l - 0.7034186147 * m + 1.6076099202 * s),\n };\n}\n\n/** Interpolates between two OKLab colors without any gray dead zone */\nexport const lerpOklab = (a: Oklab, b: Oklab, t: number): Oklab => {\n if (t <= 0) {\n return a;\n }\n\n if (t >= 1) {\n return b;\n }\n\n return {\n L: a.L + (b.L - a.L) * t,\n a: a.a + (b.a - a.a) * t,\n b: a.b + (b.b - a.b) * t,\n };\n};\n\n/**\n * Gets a color from a palette based on position (0-1) with optional time-based cycling\n * @param palette Array of Oklab colors\n * @param position Position along the gradient (0 = start, 1 = end)\n * @param timeOffset Optional time offset for animated gradients\n */\nexport function getPaletteColor(palette: Oklab[], position: number, timeOffset: number = 0): Oklab {\n if (palette.length === 0) {\n return { L: 1, a: 0, b: 0 };\n }\n\n if (palette.length === 1) {\n return palette[0]!;\n }\n\n const cyclePos = (((position + timeOffset) % 1) + 1) % 1;\n const scaled = cyclePos * palette.length;\n const idx = Math.floor(scaled);\n const t = scaled - idx;\n\n const c1 = palette[idx % palette.length]!;\n const c2 = palette[(idx + 1) % palette.length]!;\n\n return lerpOklab(c1, c2, t);\n}\n\n// TODO: maybe should infer the union type from the variable instead of making the variable respect the predefined union type\nconst TRAIL_STYLES: readonly TrailStyle[] = [\"default\", \"gradient-static\", \"gradient-animated\"];\n\n// FIXME: Should inherit from the actual render option object to avoid drift\nconst BASE_RENDER_OPTION_KEYS: ReadonlySet<string> = new Set([\n \"trailColor\",\n \"trailStyle\",\n \"skeletonColor\",\n]);\n\nconst RENDER_OPTION_KEYS: ReadonlySet<string> = new Set([\n \"trailColor\",\n \"headColor\",\n \"skeletonColor\",\n \"trailStyle\",\n \"headRadius\",\n \"trailWidth\",\n]);\n\n/**\n * Validates options for renderers that support `trailColor`, `trailStyle`, and `skeletonColor`.\n * Throws a `TypeError` if any canvas-only field (`headColor`, `headRadius`, `trailWidth`) is passed.\n */\nexport function validateBaseRenderOptions(partial: BaseRuntimeRenderOptions) {\n for (const key of Object.keys(partial)) {\n if (!BASE_RENDER_OPTION_KEYS.has(key)) {\n throw new TypeError(`[sarmal] setRenderOptions: unsupported key \"${key}\" for this renderer`);\n }\n }\n if (partial.trailColor !== undefined) {\n assertTrailColor(partial.trailColor);\n }\n if (partial.trailStyle !== undefined) {\n assertTrailStyle(partial.trailStyle);\n }\n if (partial.skeletonColor !== undefined) {\n assertSkeletonColor(partial.skeletonColor);\n }\n}\n\n/**\n * Checks a `RuntimeRenderOptions` payload against the library's acceptance criteria.\n *\n * If this throws, no field has been assigned yet on the caller side,\n * so the renderer continues on the previous valid state.\n *\n * Rules:\n * - Unknown keys throw for runtime type safety\n * - `trailColor` must match expected string/Array<string> format\n * - `headColor` must match expected color string format OR `null`\n * - `skeletonColor` must match expected color string format OR the literal `\"transparent\"`.\n * - `trailStyle` must match one of the accepted modes\n *\n * ! Field combinations like `trailColor=\"#ffffff\"` with `trailStyle=\"gradient-animated\"` are NOT rejected\n * They only produce a console warning at the renderer to indicate an unexpected outcome\n */\nexport function validateRenderOptions(partial: RuntimeRenderOptions) {\n for (const key of Object.keys(partial)) {\n if (!RENDER_OPTION_KEYS.has(key)) {\n throw new TypeError(`[sarmal] setRenderOptions: unknown key \"${key}\"`);\n }\n }\n\n if (partial.trailColor !== undefined) {\n assertTrailColor(partial.trailColor);\n }\n if (partial.headColor !== undefined) {\n assertHeadColor(partial.headColor);\n }\n if (partial.skeletonColor !== undefined) {\n assertSkeletonColor(partial.skeletonColor);\n }\n if (partial.trailStyle !== undefined) {\n assertTrailStyle(partial.trailStyle);\n }\n if (partial.headRadius !== undefined) {\n assertHeadRadius(partial.headRadius);\n }\n if (partial.trailWidth !== undefined) {\n assertTrailWidth(partial.trailWidth);\n }\n}\n\nfunction assertTrailColor(value: TrailColor) {\n if (typeof value === \"string\") {\n if (parseColorToOklab(value) === null) {\n throw new TypeError(\n `[sarmal] setRenderOptions: trailColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()), got \"${value}\"`,\n );\n }\n return;\n }\n\n if (Array.isArray(value)) {\n if (value.length < 2) {\n throw new RangeError(\n `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`,\n );\n }\n\n for (let i = 0; i < value.length; i++) {\n const entry = value[i];\n if (typeof entry !== \"string\" || parseColorToOklab(entry) === null) {\n throw new TypeError(\n `[sarmal] setRenderOptions: trailColor[${i}] must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()), got ${JSON.stringify(entry)}`,\n );\n }\n }\n return;\n }\n\n throw new TypeError(\n `[sarmal] setRenderOptions: trailColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()) or an array of color strings, got ${JSON.stringify(value)}`,\n );\n}\n\nfunction assertHeadColor(value: string | null) {\n if (value === null) {\n return;\n }\n\n if (typeof value !== \"string\" || parseColorToOklab(value) === null) {\n throw new TypeError(\n `[sarmal] setRenderOptions: headColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()) or null, got ${JSON.stringify(value)}`,\n );\n }\n}\n\nfunction assertSkeletonColor(value: string) {\n if (value === \"transparent\") {\n return;\n }\n\n if (typeof value !== \"string\" || parseColorToOklab(value) === null) {\n throw new TypeError(\n `[sarmal] setRenderOptions: skeletonColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()) or \"transparent\", got ${JSON.stringify(value)}`,\n );\n }\n}\n\nfunction assertTrailStyle(value: TrailStyle) {\n if (!TRAIL_STYLES.includes(value)) {\n // TODO: perhaps make Trail Style inferred from a variable so it can be spread here to keep it dynamic.\n throw new RangeError(\n `[sarmal] setRenderOptions: trailStyle must be one of \"default\", \"gradient-static\", \"gradient-animated\", got ${JSON.stringify(value)}`,\n );\n }\n}\n\nfunction assertHeadRadius(value: number) {\n if (typeof value !== \"number\") {\n throw new TypeError(\n `[sarmal] setRenderOptions: headRadius must be a number, got ${JSON.stringify(value)}`,\n );\n }\n if (!Number.isFinite(value) || value <= 0) {\n throw new TypeError(\n `[sarmal] setRenderOptions: headRadius must be a finite positive number, got ${value}`,\n );\n }\n}\n\nfunction assertTrailWidth(value: number) {\n if (typeof value !== \"number\") {\n throw new TypeError(\n `[sarmal] setRenderOptions: trailWidth must be a number, got ${JSON.stringify(value)}`,\n );\n }\n\n if (!Number.isFinite(value) || value <= 0) {\n throw new TypeError(\n `[sarmal] setRenderOptions: trailWidth must be a finite positive number, got ${value}`,\n );\n }\n}\n\n/**\n * Resolves the effective solid fill color used by the `\"default\"` trail style.\n * If the caller passed an array, the first entry is used.\n *\n * This is to avoid errors when an array of colors are passed for `trailStyle=\"default\"`\n */\nexport function resolveTrailMainColor(trailColor: TrailColor) {\n return typeof trailColor === \"string\" ? trailColor : trailColor[0]!;\n}\n\n/**\n * Resolves the effective palette used by gradient trail styles.\n *\n * This is to avoid errors when a single color string is passed for gradient trail styles\n */\nexport function resolveTrailPalette(trailColor: TrailColor): string[] {\n return typeof trailColor === \"string\" ? [trailColor] : trailColor;\n}\n\n/**\n * Computes the head color automatically from the current `trailColor` and `trailStyle` pair.\n *\n * Rules:\n * - `\"default\"` style: the head matches the solid trail color.\n * - Gradient styles: the head matches the *last* stop of the palette\n *\n * @returns a CSS-valid color string\n */\nexport function resolveHeadColor(trailColor: TrailColor, trailStyle: TrailStyle): string {\n if (trailStyle === \"default\") {\n return resolveTrailMainColor(trailColor);\n }\n\n const palette = resolveTrailPalette(trailColor);\n const last = palette[palette.length - 1]!;\n const { r, g, b } = colorToRgb(last);\n return `rgb(${r},${g},${b})`;\n}\n\n/**\n * Emits a console warning when `trailColor` and `trailStyle` don't *semantically* match up\n *\n * For example, an array passed with `\"default\"` style (only the first color is used) or\n * a single string passed with a gradient style (the trail renders as a solid color)\n *\n * ! This is only a warning, so the renderer will still produce a valid output\n */\nexport function warnIfTrailColorMismatch(trailColor: TrailColor, trailStyle: TrailStyle): void {\n if (trailStyle === \"default\" && Array.isArray(trailColor)) {\n // biome-ignore lint/suspicious/noConsole: advisory for developer feedback\n console.warn(\n '[sarmal] trailColor is an array but trailStyle is \"default\"; only the first color will be used. Pass a gradient trailStyle to use the whole palette.',\n );\n\n return;\n }\n\n if (trailStyle !== \"default\" && typeof trailColor === \"string\") {\n // biome-ignore lint/suspicious/noConsole: advisory for developer feedback\n console.warn(\n `[sarmal] trailColor is a single color but trailStyle is \"${trailStyle}\"; the trail will render as a solid color. Pass an array of hex colors to use a real gradient.`,\n );\n }\n}\n","import type { CurveDef } from \"./types\";\nimport type { Rgb } from \"./renderer-shared\";\n\nimport { createEngine } from \"./engine\";\nimport {\n computeBoundaries,\n lerpOklab,\n parseColorToRgb,\n rgbToOklab,\n oklabToRgb,\n} from \"./renderer-shared\";\n\nconst DEFAULT_TRAIL_HEX = \"#ec5571\"; // --color-primary\nconst DEFAULT_FPS = 30;\nconst DEFAULT_SIZE = 16; // visual-tested at 12/16/20/24 chars; 16 was the smallest that showed clear curve definition\nconst BRIGHTNESS_HEAD = 1.0;\nconst BRIGHTNESS_TAIL = 0.15;\nconst BRAILLE_BASE = 0x2800;\n\ntype BrailleBit = [number, number];\n\n/**\n * ! 0x40 and 0x80 are not unused. My LLM said `Omitting them reduces the grid to 2×3 and loses a quarter of the resolution`\n * ! Simple mode with one char per cell does not produce high enough fidelity, so Braille is the preffered approach.\n */\nconst BRAILLE_BIT: Array<BrailleBit> = [\n [0x01, 0x08],\n [0x02, 0x10],\n [0x04, 0x20],\n [0x40, 0x80],\n];\n\nexport type ColorCapability = \"truecolor\" | \"256-color\" | \"monochrome\";\ntype DotCol = 0 | 1;\ntype DotRow = 0 | 1 | 2 | 3;\ntype Boundary = { screenX: number; screenY: number };\n\nexport interface TerminalSarmalOptions {\n size?: number;\n fps?: number;\n trailColor?: string;\n headColor?: string;\n speed?: number;\n}\n\nexport function brailleChar(bits: number) {\n return String.fromCodePoint(BRAILLE_BASE + (bits & 0xff));\n}\n\nexport function brailleBit(row: number, col: number) {\n if (row < 0 || row >= BRAILLE_BIT.length || col < 0 || col >= (BRAILLE_BIT[0]?.length ?? 0)) {\n return 0;\n }\n return BRAILLE_BIT[row]![col]!;\n}\n\nexport function dotToCell(\n dotCol: number,\n dotRow: number,\n): {\n charCol: number;\n charRow: number;\n dotColInCell: DotCol;\n dotRowInCell: DotRow;\n} {\n return {\n charCol: Math.floor(dotCol / 2),\n charRow: Math.floor(dotRow / 4),\n dotColInCell: (dotCol % 2) as DotCol,\n dotRowInCell: (dotRow % 4) as DotRow,\n };\n}\n\nexport function detectColor(): ColorCapability {\n const colorterm = (process.env.COLORTERM ?? \"\").toLowerCase();\n\n if (colorterm === \"truecolor\" || colorterm === \"24bit\") {\n return \"truecolor\";\n }\n\n const term = (process.env.TERM ?? \"\").toLowerCase();\n\n if (term.includes(\"256color\")) {\n return \"256-color\";\n }\n\n if (term.includes(\"truecolor\") || term.includes(\"24bit\")) {\n return \"truecolor\";\n }\n\n if (term === \"\" || term === \"linux\" || term === \"dumb\") {\n return \"monochrome\";\n }\n\n return \"256-color\";\n}\n\nexport function rgbTo256(r: number, g: number, b: number) {\n const avg = Math.round((r + g + b) / 3);\n\n if (Math.abs(r - avg) <= 4 && Math.abs(g - avg) <= 4 && Math.abs(b - avg) <= 4) {\n if (avg <= 8) {\n return 16;\n }\n return 232 + Math.min(23, Math.round((avg - 8) / 10));\n }\n\n const ri = Math.round((r / 255) * 5);\n const gi = Math.round((g / 255) * 5);\n const bi = Math.round((b / 255) * 5);\n return 16 + 36 * ri + 6 * gi + bi;\n}\n\nexport function dimRgb(rgb: Rgb, brightness: number): Rgb {\n const t = 1 - Math.max(0, Math.min(1, brightness));\n if (t <= 0) {\n return rgb;\n }\n\n if (t >= 1) {\n return { r: 0, g: 0, b: 0 };\n }\n\n return oklabToRgb(lerpOklab(rgbToOklab(rgb), { L: 0, a: 0, b: 0 }, t));\n}\n\nexport function dimColor(color: string, brightness: number): Rgb {\n return dimRgb(parseColorToRgb(color)!, brightness);\n}\n\nconst AR = \"\\x1B[0m\";\n\nfunction ansiTruecolorFg(r: number, g: number, b: number) {\n return `\\x1B[38;2;${r};${g};${b}m`;\n}\n\nfunction ansi256Fg(code: number) {\n return `\\x1B[38;5;${code}m`;\n}\n\nfunction ansiColor(r: number, g: number, b: number, colorCap: ColorCapability) {\n if (colorCap === \"truecolor\") {\n return ansiTruecolorFg(r, g, b);\n }\n\n if (colorCap === \"256-color\") {\n return ansi256Fg(rgbTo256(r, g, b));\n }\n\n return \"\";\n}\n\ninterface BrailleCell {\n bits: number;\n brightness: number;\n isHead: boolean;\n}\n\nfunction snapCol(col: number, max: number) {\n return Math.max(0, Math.min(max - 1, Math.round(col)));\n}\n\nfunction applyBoundary(\n x: number,\n y: number,\n scale: number,\n offsetX: number,\n offsetY: number,\n): Boundary {\n return {\n screenX: x * scale + offsetX,\n screenY: y * scale + offsetY,\n };\n}\n\nfunction renderFrame(\n trail: Array<{ x: number; y: number }>,\n trailCount: number,\n charWidth: number,\n charHeight: number,\n scale: number,\n offsetX: number,\n offsetY: number,\n trailRgb: Rgb,\n headRgb: Rgb,\n colorCap: ColorCapability,\n): string {\n const dotWidth = charWidth * 2;\n const dotHeight = charHeight * 4;\n\n const grid: BrailleCell[][] = Array.from({ length: charHeight }, () =>\n Array.from(\n { length: charWidth },\n (): BrailleCell => ({ bits: 0, brightness: 0, isHead: false }),\n ),\n );\n\n for (let i = 0; i < trailCount; i++) {\n const pt = trail[i]!;\n const t = trailCount > 1 ? i / (trailCount - 1) : 1;\n const brightness = BRIGHTNESS_TAIL + (BRIGHTNESS_HEAD - BRIGHTNESS_TAIL) * t;\n const { screenX, screenY } = applyBoundary(pt.x, pt.y, scale, offsetX, offsetY);\n const dotCol = snapCol(screenX, dotWidth);\n const dotRow = snapCol(screenY, dotHeight);\n const cell = dotToCell(dotCol, dotRow);\n\n if (\n cell.charRow < 0 ||\n cell.charRow >= charHeight ||\n cell.charCol < 0 ||\n cell.charCol >= charWidth\n ) {\n continue;\n }\n\n const c = grid[cell.charRow]![cell.charCol]!;\n c.bits |= BRAILLE_BIT[cell.dotRowInCell]![cell.dotColInCell]!;\n if (brightness > c.brightness) {\n c.brightness = brightness;\n }\n\n if (i === trailCount - 1) {\n c.isHead = true;\n }\n }\n\n if (colorCap !== \"monochrome\") {\n return renderColorFrame(grid, charWidth, charHeight, trailRgb, headRgb, colorCap);\n }\n\n return renderMonochromeFrame(grid, charWidth, charHeight);\n}\n\nfunction renderColorFrame(\n grid: BrailleCell[][],\n charWidth: number,\n charHeight: number,\n trailRgb: Rgb,\n headRgb: Rgb,\n colorCap: ColorCapability,\n): string {\n const lines: string[] = [];\n\n for (let row = 0; row < charHeight; row++) {\n let line = \"\";\n for (let col = 0; col < charWidth; col++) {\n const cell = grid[row]![col]!;\n if (cell.bits === 0) {\n line += \" \";\n continue;\n }\n\n const ch = brailleChar(cell.bits);\n if (cell.isHead) {\n line += ansiColor(headRgb.r, headRgb.g, headRgb.b, colorCap) + ch + AR;\n } else {\n const dimmed = dimRgb(trailRgb, cell.brightness);\n line += ansiColor(dimmed.r, dimmed.g, dimmed.b, colorCap) + ch + AR;\n }\n }\n lines.push(line);\n }\n return lines.join(\"\\n\");\n}\n\nfunction renderMonochromeFrame(\n grid: Array<Array<BrailleCell>>,\n charWidth: number,\n charHeight: number,\n): string {\n const lines: string[] = [];\n for (let row = 0; row < charHeight; row++) {\n let line = \"\";\n\n for (let col = 0; col < charWidth; col++) {\n const cell = grid[row]![col]!;\n\n if (cell.bits === 0) {\n line += \" \";\n continue;\n }\n\n line += brailleChar(cell.bits);\n }\n lines.push(line);\n }\n return lines.join(\"\\n\");\n}\n\nexport function terminalSarmal(\n stream: NodeJS.WriteStream,\n curveDef: CurveDef,\n options?: TerminalSarmalOptions,\n) {\n if (!stream.isTTY) {\n return () => {};\n }\n\n const size = options?.size ?? DEFAULT_SIZE;\n const fps = options?.fps ?? DEFAULT_FPS;\n const trailHex = options?.trailColor ?? DEFAULT_TRAIL_HEX;\n const headHex = options?.headColor ?? trailHex;\n const userSpeed = options?.speed;\n const colorCap = detectColor();\n\n const trailRgb = parseColorToRgb(trailHex)!;\n const headRgb = parseColorToRgb(headHex)!;\n\n const engine = createEngine(curveDef);\n if (userSpeed !== undefined) {\n engine.setSpeed(userSpeed);\n }\n\n const charWidth = size;\n const charHeight = Math.ceil(size / 2);\n\n const skeleton = engine.getSarmalSkeleton();\n const b = computeBoundaries(skeleton, charWidth * 2, charHeight * 4, 1);\n if (!b) {\n return () => {};\n }\n\n const { scale, offsetX, offsetY } = b;\n\n let running = true;\n let firstFrame = true;\n\n stream.write(\"\\x1B[?25l\");\n\n function cleanup() {\n running = false;\n stream.write(\"\\x1B[?25h\");\n stream.write(\"\\n\");\n }\n\n const onSigint = () => {\n cleanup();\n process.exit(0);\n };\n process.on(\"SIGINT\", onSigint);\n\n function render() {\n const delta = 1 / fps;\n const trail = engine.tick(delta);\n const trailCount = engine.trailCount;\n\n const frame = renderFrame(\n trail,\n trailCount,\n charWidth,\n charHeight,\n scale,\n offsetX,\n offsetY,\n trailRgb,\n headRgb,\n colorCap,\n );\n\n if (firstFrame) {\n firstFrame = false;\n stream.write(frame + \"\\n\");\n return;\n }\n\n const rows = charHeight;\n stream.write(`\\x1B[${rows}A`);\n const lines = frame.split(\"\\n\");\n for (const line of lines) {\n stream.write(line + \"\\n\");\n }\n }\n\n render();\n\n const interval = setInterval(() => {\n if (!running) {\n return;\n }\n\n render();\n }, 1000 / fps);\n\n function stop() {\n clearInterval(interval);\n process.off(\"SIGINT\", onSigint);\n cleanup();\n }\n\n return stop;\n}\n"]}