@pixerate/flair 0.2.0 → 0.3.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 (44) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +44 -8
  3. package/dist/catalog/index.d.ts +5 -4
  4. package/dist/catalog/index.js +12 -4
  5. package/dist/{chunk-JWZCPMFG.js → chunk-D7NLHC7K.js} +117 -674
  6. package/dist/chunk-D7NLHC7K.js.map +1 -0
  7. package/dist/{chunk-3FAUZOGL.js → chunk-FIJOMGPG.js} +2 -2
  8. package/dist/{chunk-UQQNZSYL.js → chunk-GTIAOYOK.js} +211 -7
  9. package/dist/chunk-GTIAOYOK.js.map +1 -0
  10. package/dist/{chunk-MWVTQFV5.js → chunk-IWKM5FJK.js} +410 -59
  11. package/dist/chunk-IWKM5FJK.js.map +1 -0
  12. package/dist/{chunk-HKXOHRCD.js → chunk-VA74HBPK.js} +2 -2
  13. package/dist/chunk-WAMA45G3.js +998 -0
  14. package/dist/chunk-WAMA45G3.js.map +1 -0
  15. package/dist/{chunk-J55UBNAT.js → chunk-WC3VADZC.js} +42 -36
  16. package/dist/chunk-WC3VADZC.js.map +1 -0
  17. package/dist/cli.js +12 -11
  18. package/dist/cli.js.map +1 -1
  19. package/dist/{controls-DdXMbovi.d.ts → controls-Syr6drzO.d.ts} +4 -3
  20. package/dist/editor/index.d.ts +12 -8
  21. package/dist/editor/index.js +26 -16
  22. package/dist/editor/index.js.map +1 -1
  23. package/dist/{export-FEBPEEO7.js → export-DH2HMACO.js} +4 -3
  24. package/dist/index.d.ts +193 -17
  25. package/dist/index.js +69 -33
  26. package/dist/index.js.map +1 -1
  27. package/dist/node.d.ts +1 -1
  28. package/dist/node.js +2 -2
  29. package/dist/recorder-KEX3U35D.js +10 -0
  30. package/dist/{renderer-Oyynz-fs.d.ts → renderer-mzvhKzKa.d.ts} +1 -1
  31. package/dist/types-CsFUZZIv.d.ts +102 -0
  32. package/package.json +1 -1
  33. package/dist/chunk-36DTNSHU.js +0 -160
  34. package/dist/chunk-36DTNSHU.js.map +0 -1
  35. package/dist/chunk-J55UBNAT.js.map +0 -1
  36. package/dist/chunk-JWZCPMFG.js.map +0 -1
  37. package/dist/chunk-MWVTQFV5.js.map +0 -1
  38. package/dist/chunk-UQQNZSYL.js.map +0 -1
  39. package/dist/recorder-WJTWMEQN.js +0 -10
  40. package/dist/types-CkKkavvB.d.ts +0 -71
  41. /package/dist/{chunk-3FAUZOGL.js.map → chunk-FIJOMGPG.js.map} +0 -0
  42. /package/dist/{chunk-HKXOHRCD.js.map → chunk-VA74HBPK.js.map} +0 -0
  43. /package/dist/{export-FEBPEEO7.js.map → export-DH2HMACO.js.map} +0 -0
  44. /package/dist/{recorder-WJTWMEQN.js.map → recorder-KEX3U35D.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/time/noise.ts","../src/scene/types.ts","../src/scene/geometry.ts","../src/scene/camera.ts","../src/scene/check.ts","../src/scene/effects.ts","../src/scene/behavior.ts","../src/scene/report.ts","../src/scene/player.ts","../src/scene/cameratrack.ts"],"sourcesContent":["/**\n * Deterministic pseudo-random number generator, hash mixing, and C2-continuous smooth value noise.\n * Based on 64-bit SplitMix64 mixing and quintic Hermite interpolation.\n *\n * Produces deterministic, platform-independent outputs for wiggle, organic turbulence, and camera shake.\n */\n\n// BigInt constants for SplitMix64\nconst GOLDEN_GAMMA = 0x9e3779b97f4a7c15n;\nconst MULT1 = 0xbf58476d1ce4e5b9n;\nconst MULT2 = 0x94d049bb133111ebn;\nconst MASK64 = 0xffffffffffffffffn;\nconst BASE_MIX = 0x2545f4914f6cdd1dn;\nconst POW2_53 = 9007199254740992; // 2^53\n\n/**\n * SplitMix64 mixing finalizer: maps any 64-bit integer to a high-quality pseudorandom 64-bit integer.\n */\nexport function hash64(x: bigint): bigint {\n let v = (x + GOLDEN_GAMMA) & MASK64;\n v = ((v ^ (v >> 30n)) * MULT1) & MASK64;\n v = ((v ^ (v >> 27n)) * MULT2) & MASK64;\n return (v ^ (v >> 31n)) & MASK64;\n}\n\n/**\n * Combines multiple seed numbers/bigints into a single mixed 64-bit seed.\n */\nexport function mixSeeds(parts: (bigint | number)[]): bigint {\n let h = BASE_MIX;\n for (const p of parts) {\n const val = typeof p === 'bigint' ? p : BigInt(Math.floor(p));\n h = hash64(h ^ hash64(val & MASK64));\n }\n return h;\n}\n\n/**\n * Maps a 64-bit seed to a uniform float in [0, 1).\n */\nexport function unitFloat(seed: bigint | number): number {\n const s = typeof seed === 'bigint' ? seed : BigInt(Math.floor(seed));\n const hashed = hash64(s);\n const shifted = Number(hashed >> 11n);\n return shifted / POW2_53;\n}\n\n/**\n * Quintic Hermite interpolant fade function: 6t^5 - 15t^4 + 10t^3.\n * First and second derivatives are zero at t = 0 and t = 1, ensuring C2 continuity.\n */\nexport function quinticFade(t: number): number {\n return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);\n}\n\n/**\n * Returns a deterministic pseudo-random lattice value in [-1, 1].\n */\nfunction lattice(seed: bigint, i: bigint): number {\n const mixed = seed ^ hash64(i & MASK64);\n return unitFloat(mixed) * 2.0 - 1.0;\n}\n\n/**\n * 1D C2-continuous smooth value noise in [-1, 1].\n * Guaranteed bounded by lattice values, smooth without sudden velocity spikes.\n */\nexport function noise1d(seed: bigint | number, x: number): number {\n if (!Number.isFinite(x)) return 0.0;\n const s = typeof seed === 'bigint' ? seed : BigInt(Math.floor(seed));\n const floorX = Math.floor(x);\n const u = quinticFade(x - floorX);\n const i = BigInt(Math.floor(floorX));\n const a = lattice(s, i);\n const b = lattice(s, i + 1n);\n return a + (b - a) * u;\n}\n\n/**\n * 3D C2-continuous smooth value noise in [-1, 1].\n */\nexport function noise3d(x: number, y: number, z: number, seed: bigint | number = 0): number {\n if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {\n return 0.0;\n }\n const s = typeof seed === 'bigint' ? seed : BigInt(Math.floor(seed));\n const fx = Math.floor(x);\n const fy = Math.floor(y);\n const fz = Math.floor(z);\n\n const u = quinticFade(x - fx);\n const v = quinticFade(y - fy);\n const w = quinticFade(z - fz);\n\n const ix = BigInt(fx);\n const iy = BigInt(fy);\n const iz = BigInt(fz);\n\n const g = (dx: bigint, dy: bigint, dz: bigint): number => {\n const combined = mixSeeds([s, ix + dx, iy + dy, iz + dz]);\n return unitFloat(combined) * 2.0 - 1.0;\n };\n\n const lerp = (a: number, b: number, t: number) => a + (b - a) * t;\n\n const x00 = lerp(g(0n, 0n, 0n), g(1n, 0n, 0n), u);\n const x10 = lerp(g(0n, 1n, 0n), g(1n, 1n, 0n), u);\n const x01 = lerp(g(0n, 0n, 1n), g(1n, 0n, 1n), u);\n const x11 = lerp(g(0n, 1n, 1n), g(1n, 1n, 1n), u);\n\n const y0 = lerp(x00, x10, v);\n const y1 = lerp(x01, x11, v);\n\n return lerp(y0, y1, w);\n}\n\n/**\n * Evaluates multi-octave fractal noise wiggle:\n * sum_{k=0}^{octaves-1} amp * (ampMult^k) * noise1d(freq * (2^k) * t)\n *\n * Matches the mathematical formulation of After Effects / EffectCraft wiggle().\n *\n * @param freq Oscillation frequency in Hz (cycles per second).\n * @param amp Maximum deviation amplitude.\n * @param octaves Number of noise detail octaves (default: 3).\n * @param ampMult Amplitude multiplier per octave (default: 0.5).\n * @param t Elapsed time in seconds.\n * @param seed Random seed (default: 42).\n * @param dimension Dimension index (0, 1, 2) to ensure decorrelated multi-axis wiggle.\n */\nexport function fractalWiggle(\n freq: number,\n amp: number,\n octaves = 3,\n ampMult = 0.5,\n t = 0,\n seed = 42,\n dimension = 0,\n): number {\n const oct = Math.max(1, Math.min(16, Math.floor(octaves)));\n let currentAmp = amp;\n let currentFreq = freq;\n let sum = 0.0;\n const baseSeed = BigInt(seed);\n\n for (let k = 0; k < oct; k++) {\n const s = mixSeeds([baseSeed, BigInt(dimension), BigInt(k)]);\n const phaseOffset = unitFloat(s) * 1000.0;\n sum += currentAmp * noise1d(s, currentFreq * t + phaseOffset);\n currentAmp *= ampMult;\n currentFreq *= 2.0;\n }\n\n return sum;\n}\n","import type { Rect, SceneElement } from './geometry.js';\nimport type { CameraAngle, Stage } from './camera.js';\nimport type { Effect } from './effects.js';\nimport type { Behavior } from './behavior.js';\nimport type { SceneRig } from './rig.js';\n\n/** Shader or external visual stream in the scene. */\nexport interface SceneMedia {\n type: 'shader' | string;\n source?: string;\n values?: Record<string, unknown>;\n [field: string]: unknown;\n}\n\nexport interface ElementCommand {\n type: 'element';\n element: SceneElement;\n}\n\nexport interface UpdateCommand {\n type: 'update';\n id: string;\n props: Partial<SceneElement>;\n}\n\nexport interface DeleteCommand {\n type: 'delete';\n id: string;\n}\n\nexport interface ClearCommand {\n type: 'clear';\n}\n\nexport interface CustomCommand {\n type: string;\n [key: string]: unknown;\n}\n\n/** A command to add, move, or style elements in a shot. */\nexport type SceneCommand =\n | ElementCommand\n | UpdateCommand\n | DeleteCommand\n | ClearCommand\n | CustomCommand;\n\nexport function isElementCommand(cmd: SceneCommand): cmd is ElementCommand {\n return cmd.type === 'element' && 'element' in cmd && !!cmd.element;\n}\n\nexport function isUpdateCommand(cmd: SceneCommand): cmd is UpdateCommand {\n return cmd.type === 'update' && 'id' in cmd && typeof cmd.id === 'string';\n}\n\nexport function isDeleteCommand(cmd: SceneCommand): cmd is DeleteCommand {\n return cmd.type === 'delete' && 'id' in cmd && typeof cmd.id === 'string';\n}\n\nexport interface DynamicFraming {\n /** Element IDs to frame. */\n frame: string[];\n /** Proportional padding (default 0.15 for 15%). */\n padding?: number;\n /** Minimum width in scene units. */\n minWidth?: number;\n /** Minimum height in scene units. */\n minHeight?: number;\n}\n\n/**\n * A shot's camera beyond its framing: its 3D angle (see `CameraAngle`) and\n * how much it blurs while moving into the shot. A part left out keeps the\n * value the camera already has, so set `tilt: 0` to come back flat.\n */\nexport interface CameraLens extends CameraAngle {\n /**\n * Motion blur while the camera moves into this shot: 0 (the default) for\n * none, 1 for natural, up to 3 for a whip. The blur follows the move's\n * speed, so it peaks mid-move and is gone when the camera settles.\n */\n motionBlur?: number;\n}\n\n/**\n * Where the camera goes for a shot: a framing box, a dynamic framing, or\n * neither (only a new angle or blur, keeping the framing), with any\n * {@link CameraLens} fields alongside: `{ \"frame\": [\"chart\"], \"tilt\": 30 }`.\n */\nexport type ShotCamera = (Rect | DynamicFraming | Record<never, never>) & CameraLens;\n\n/** One beat of a scene: camera framing, title, narration/caption, commands, and procedural effects. */\nexport interface Shot {\n /** Narration / caption spoken or displayed during this beat. */\n caption?: string;\n /** High-level title or callout displayed on screen. */\n title?: string;\n /** Camera framing box in scene units or dynamic auto-framing target, with an optional 3D angle and motion blur. */\n camera?: ShotCamera;\n /** Pause/dwell duration after this shot's actions run (in milliseconds). Default: 800ms. */\n hold?: number;\n /** Drawing or state change commands played during this shot. */\n run?: SceneCommand[];\n /** Procedural visual effects started in this shot. */\n effects?: Effect[];\n /** IDs of active effects to stop/fade at the start of this shot. */\n stopEffects?: string[];\n /** Continuous procedural parameter and physics behaviors played during this shot. */\n behaviors?: Behavior[];\n}\n\n/** Complete animated scene specification. */\nexport interface Scene {\n name: string;\n title?: string;\n description?: string;\n stage?: Partial<Stage>;\n /** Embedded media (e.g. bespoke WebGL fragment shaders). */\n media?: Record<string, SceneMedia>;\n /** Target duration [minSeconds, maxSeconds]. */\n target?: [number, number];\n /** Interactive macro rigs binding high-level controls to uniforms or element properties. */\n rig?: SceneRig;\n shots: Shot[];\n}\n","export type Point = [number, number];\n\nexport interface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** An element placed or animated in a scene. */\nexport interface SceneElement {\n id: string;\n type: string;\n x: number;\n y: number;\n width: number;\n height: number;\n points?: readonly (readonly [number, number])[];\n fontSize?: number;\n text?: string;\n containerId?: string | null;\n isDeleted?: boolean;\n opacity?: number;\n}\n\n/** Bounds in scene units, ignoring rotation. */\nexport function boundsOf(e: SceneElement): Rect {\n if (e.points?.length) {\n const xs = e.points.map((p) => e.x + p[0]);\n const ys = e.points.map((p) => e.y + p[1]);\n const x = Math.min(...xs);\n const y = Math.min(...ys);\n return {\n x,\n y,\n width: Math.max(1, Math.max(...xs) - x),\n height: Math.max(1, Math.max(...ys) - y),\n };\n }\n return { x: e.x, y: e.y, width: Math.max(1, e.width), height: Math.max(1, e.height) };\n}\n\n/** Area of the overlap of two rectangles (0 when they do not meet). */\nexport function overlapArea(a: Rect, b: Rect): number {\n const w = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x);\n const h = Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y);\n return w > 0 && h > 0 ? w * h : 0;\n}\n\nexport const areaOf = (r: Rect): number => r.width * r.height;\n","import { type Rect, type SceneElement, boundsOf } from './geometry.js';\n\nexport interface Stage {\n width: number;\n height: number;\n caption?: number;\n}\n\nexport const DEFAULT_STAGE: Stage = {\n width: 1920,\n height: 1080,\n caption: 0,\n};\n\nexport const CAMERA_MS = 600;\n\n/**\n * How the camera is turned in 3D, in degrees: the board is drawn flat (see\n * {@link View}), then turned round the middle of the stage and seen in\n * perspective. Each is optional and 0 (flat) by default.\n */\nexport interface CameraAngle {\n /** Tips the board back round its horizontal axis (CSS `rotateX`): positive leans the top away, like a desk seen from above. −75 to 75. */\n tilt?: number;\n /** Swings the board round its vertical axis (CSS `rotateY`): positive brings the left side nearer. −75 to 75. */\n turn?: number;\n /** Rolls the board round the line of sight (CSS `rotateZ`), clockwise. −180 to 180. */\n roll?: number;\n /** How far the lens sits from the board, as a multiple of the stage width (default {@link DEFAULT_PERSPECTIVE}); smaller is more dramatic. */\n perspective?: number;\n}\n\n/** The camera's default distance from the board, as a multiple of the stage width. */\nexport const DEFAULT_PERSPECTIVE = 1.2;\n\n/** The largest tilt or turn, in degrees: past this the board is seen edge-on. */\nexport const MAX_TILT = 75;\n\n/**\n * Camera view into the scene: drawn at (point + scroll) × zoom, then turned\n * by its {@link CameraAngle} (see {@link cameraTransform}) and blurred by\n * `blur` while the camera moves.\n */\nexport interface View extends CameraAngle {\n zoom: number;\n scrollX: number;\n scrollY: number;\n /** Motion blur in stage pixels, set while the camera moves (see `ShotCamera.motionBlur`); 0 or absent when still. */\n blur?: number;\n /**\n * The direction the picture streaks in, in degrees on screen (0 is\n * horizontal, 90 vertical), when the move is mostly a pan; absent when it\n * blurs every way (a zoom or a turn).\n */\n blurAngle?: number;\n}\n\nexport const boardHeight = (stage: Stage): number => stage.height - (stage.caption ?? 0);\n\n/** View with `(cx, cy)` in the middle of the board at `zoom`. */\nexport function viewAt(cx: number, cy: number, zoom: number, stage: Stage): View {\n return {\n zoom,\n scrollX: stage.width / (2 * zoom) - cx,\n scrollY: boardHeight(stage) / (2 * zoom) - cy,\n };\n}\n\n/** Scene point in the middle of the board. */\nexport function centreOf(view: View, stage: Stage): [number, number] {\n return [\n stage.width / (2 * view.zoom) - view.scrollX,\n boardHeight(stage) / (2 * view.zoom) - view.scrollY,\n ];\n}\n\n/** The view that fits `rect` on the board, centred and with its aspect kept. */\nexport function frame(rect: Rect, stage: Stage): View {\n if (!(rect.width > 0 && rect.height > 0)) {\n throw new Error('A camera rectangle needs a positive width and height');\n }\n const zoom = Math.min(stage.width / rect.width, boardHeight(stage) / rect.height);\n return viewAt(rect.x + rect.width / 2, rect.y + rect.height / 2, zoom, stage);\n}\n\nexport interface FramingOptions {\n /** Proportional padding added to all sides (e.g., 0.15 for 15%). Default: 0.15 */\n padding?: number;\n /** Minimum width in scene units. */\n minWidth?: number;\n /** Minimum height in scene units. */\n minHeight?: number;\n}\n\n/**\n * Calculates a camera framing rectangle that encloses target elements\n * while preserving the stage aspect ratio and applying optional margin padding.\n */\nexport function calculateFramingRect(\n elements: readonly SceneElement[],\n stage: Stage,\n options: FramingOptions = {},\n): Rect {\n const activeElements = elements.filter((e) => !e.isDeleted);\n const targetH = boardHeight(stage);\n const stageAspect = stage.width / Math.max(1, targetH);\n\n if (activeElements.length === 0) {\n return {\n x: 0,\n y: 0,\n width: stage.width,\n height: targetH,\n };\n }\n\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (const el of activeElements) {\n const b = boundsOf(el);\n if (b.x < minX) minX = b.x;\n if (b.y < minY) minY = b.y;\n if (b.x + b.width > maxX) maxX = b.x + b.width;\n if (b.y + b.height > maxY) maxY = b.y + b.height;\n }\n\n let width = maxX - minX;\n let height = maxY - minY;\n\n // Apply padding (default 15%)\n const padding = options.padding ?? 0.15;\n if (padding > 0) {\n const padX = width * padding;\n const padY = height * padding;\n minX -= padX;\n minY -= padY;\n width += padX * 2;\n height += padY * 2;\n }\n\n // Apply min dimensions\n if (options.minWidth && width < options.minWidth) {\n const diff = options.minWidth - width;\n minX -= diff / 2;\n width = options.minWidth;\n }\n if (options.minHeight && height < options.minHeight) {\n const diff = options.minHeight - height;\n minY -= diff / 2;\n height = options.minHeight;\n }\n\n // Preserve stage aspect ratio centered around midpoint\n const currentAspect = width / Math.max(1, height);\n if (currentAspect < stageAspect) {\n // Taller than stage: expand width\n const targetWidth = height * stageAspect;\n const diff = targetWidth - width;\n minX -= diff / 2;\n width = targetWidth;\n } else if (currentAspect > stageAspect) {\n // Wider than stage: expand height\n const targetHeight = width / stageAspect;\n const diff = targetHeight - height;\n minY -= diff / 2;\n height = targetHeight;\n }\n\n return {\n x: minX,\n y: minY,\n width,\n height,\n };\n}\n\nconst ANGLES = ['tilt', 'turn', 'roll'] as const;\n\n/** Whether a view is turned in 3D at all. */\nexport function isAngled(view: CameraAngle): boolean {\n return ANGLES.some((k) => (view[k] ?? 0) !== 0);\n}\n\n/** The view's angle with each part clamped to its range (see {@link CameraAngle}); flat parts are left out. */\nexport function cameraAngle(view: CameraAngle): CameraAngle {\n const out: CameraAngle = {};\n const clamp = (v: number, max: number) => Math.max(-max, Math.min(max, v));\n if (view.tilt) out.tilt = clamp(view.tilt, MAX_TILT);\n if (view.turn) out.turn = clamp(view.turn, MAX_TILT);\n if (view.roll) out.roll = clamp(view.roll, 180);\n if (view.perspective !== undefined && view.perspective !== DEFAULT_PERSPECTIVE) out.perspective = Math.max(0.3, view.perspective);\n return out;\n}\n\n/**\n * The view part-way (`t` from 0 to 1) from one view to another.\n * Centre moves linearly and zoom scales exponentially for smooth motion;\n * angles and perspective move linearly.\n */\nexport function between(from: View, to: View, t: number, stage: Stage): View {\n const [x0, y0] = centreOf(from, stage);\n const [x1, y1] = centreOf(to, stage);\n const zoom = from.zoom * Math.pow(to.zoom / from.zoom, t);\n const view: View = viewAt(x0 + (x1 - x0) * t, y0 + (y1 - y0) * t, zoom, stage);\n if (!isAngled(from) && !isAngled(to) && from.perspective === to.perspective) return view;\n const lerp = (a = 0, b = 0) => a + (b - a) * t;\n return {\n ...view,\n ...cameraAngle({\n tilt: lerp(from.tilt, to.tilt),\n turn: lerp(from.turn, to.turn),\n roll: lerp(from.roll, to.roll),\n perspective: lerp(from.perspective ?? DEFAULT_PERSPECTIVE, to.perspective ?? DEFAULT_PERSPECTIVE),\n }),\n };\n}\n\n/**\n * The CSS `transform` that turns a board layer (a box the stage's size,\n * with `transform-origin` in its middle) to the view's angle, or \"none\" when\n * the view is flat. Zoom and scroll are not in it: draw them inside the\n * layer, as for a flat view. `width` is the layer's width on screen in CSS\n * pixels (default: the stage width).\n */\nexport function cameraTransform(view: View, stage: Stage, width = stage.width): string {\n if (!isAngled(view)) return 'none';\n const a = cameraAngle(view);\n const distance = (a.perspective ?? DEFAULT_PERSPECTIVE) * width;\n return `perspective(${round(distance)}px) rotateX(${round(a.tilt ?? 0)}deg) rotateY(${round(a.turn ?? 0)}deg) rotateZ(${round(a.roll ?? 0)}deg)`;\n}\n\n/**\n * The view's motion blur as standard deviations along the board layer's own\n * x and y axes, in CSS pixels at a layer `width` wide: for an SVG\n * `<feGaussianBlur stdDeviation=\"x y\">`, so a pan streaks along its\n * direction (CSS `blur()` can only blur every way). The layer is turned\n * after it is blurred, so the direction is taken back through its roll.\n * [0, 0] when still.\n */\nexport function cameraBlur(view: View, stage: Stage, width = stage.width): [number, number] {\n const blur = (view.blur ?? 0) * (width / stage.width);\n if (!(blur >= 0.05)) return [0, 0];\n if (view.blurAngle === undefined) return [round(blur), round(blur)];\n const a = ((view.blurAngle - (cameraAngle(view).roll ?? 0)) * Math.PI) / 180;\n // A little blur across the streak keeps it from looking like a scanline.\n const across = blur * 0.15;\n return [round(Math.max(across, blur * Math.abs(Math.cos(a)))), round(Math.max(across, blur * Math.abs(Math.sin(a))))];\n}\n\n/** The CSS `filter` for the view's motion blur at a layer `width` CSS pixels wide (the same every way; see {@link cameraBlur} for a streak), or \"none\". */\nexport function cameraFilter(view: View, stage: Stage, width = stage.width): string {\n const blur = (view.blur ?? 0) * (width / stage.width);\n return blur >= 0.05 ? `blur(${round(blur)}px)` : 'none';\n}\n\nconst round = (n: number) => Math.round(n * 1000) / 1000;\n\n/**\n * Where a scene point lands on screen, in stage pixels from the top left of\n * the board: drawn by the view's zoom and scroll, then turned and seen in\n * perspective as {@link cameraTransform} does. Null when the point is\n * behind the camera.\n */\nexport function projectPoint(view: View, stage: Stage, [x, y]: readonly [number, number]): [number, number] | null {\n const cx = stage.width / 2;\n const cy = boardHeight(stage) / 2;\n let px = (x + view.scrollX) * view.zoom - cx;\n let py = (y + view.scrollY) * view.zoom - cy;\n if (!isAngled(view)) return [px + cx, py + cy];\n const a = cameraAngle(view);\n const rad = Math.PI / 180;\n // CSS applies rotateX(tilt) rotateY(turn) rotateZ(roll) right to left: roll first.\n const [sr, cr] = [Math.sin((a.roll ?? 0) * rad), Math.cos((a.roll ?? 0) * rad)];\n [px, py] = [px * cr - py * sr, px * sr + py * cr];\n const [st, ct] = [Math.sin((a.turn ?? 0) * rad), Math.cos((a.turn ?? 0) * rad)];\n let pz = -px * st;\n px = px * ct;\n const [sx, cx2] = [Math.sin((a.tilt ?? 0) * rad), Math.cos((a.tilt ?? 0) * rad)];\n [py, pz] = [py * cx2 - pz * sx, py * sx + pz * cx2];\n const distance = (a.perspective ?? DEFAULT_PERSPECTIVE) * stage.width;\n if (pz >= distance) return null;\n const k = distance / (distance - pz);\n return [cx + px * k, cy + py * k];\n}\n\n/** How far the picture moves on screen between two views: as a pan (x, y), and as zooming and turning. */\nfunction screenMotion(from: View, to: View, stage: Stage): { dx: number; dy: number; other: number } {\n const [x0, y0] = centreOf(from, stage);\n const [x1, y1] = centreOf(to, stage);\n const zoom = Math.sqrt(from.zoom * to.zoom);\n const scale = Math.abs(Math.log(to.zoom / from.zoom)) * stage.width * 0.5;\n const turn = ANGLES.reduce((sum, k) => sum + Math.abs((to[k] ?? 0) - (from[k] ?? 0)), 0) * (stage.width / 180);\n return { dx: (x1 - x0) * zoom, dy: (y1 - y0) * zoom, other: scale + turn };\n}\n\n/**\n * Motion blur for one step of a camera move, in stage pixels: how far the\n * picture moves on screen between two views (centre, zoom and angle), times\n * `strength` (`ShotCamera.motionBlur`), capped at 40.\n */\nexport function motionBlur(from: View, to: View, stage: Stage, strength: number): number {\n if (!(strength > 0)) return 0;\n const { dx, dy, other } = screenMotion(from, to, stage);\n return round(Math.min(40, (Math.hypot(dx, dy) + other) * 0.35 * strength));\n}\n\n/**\n * The direction a step of a camera move streaks in, in degrees on screen\n * (0 to 180, 0 horizontal), when most of it is a pan; undefined when it is\n * mostly zooming or turning, which blurs every way.\n */\nexport function motionBlurAngle(from: View, to: View, stage: Stage): number | undefined {\n const { dx, dy, other } = screenMotion(from, to, stage);\n const pan = Math.hypot(dx, dy);\n if (!(pan > 0) || pan < 0.6 * (pan + other)) return undefined;\n const a = (Math.atan2(dy, dx) * 180) / Math.PI;\n return round(((a % 180) + 180) % 180);\n}\n\n/** The blur fields of a step of a camera move with this strength: `{ blur, blurAngle }`, or nothing when still. */\nexport function moveBlur(from: View, to: View, stage: Stage, strength: number): Pick<View, 'blur' | 'blurAngle'> {\n const blur = motionBlur(from, to, stage, strength);\n if (!(blur > 0)) return {};\n const blurAngle = motionBlurAngle(from, to, stage);\n return blurAngle === undefined ? { blur } : { blur, blurAngle };\n}\n\n/** The part of the scene the camera currently shows. */\nexport function visibleRect(view: View, stage: Stage): Rect {\n return {\n x: -view.scrollX,\n y: -view.scrollY,\n width: stage.width / view.zoom,\n height: boardHeight(stage) / view.zoom,\n };\n}\n\n/** Smooth cubic ease in / ease out. */\nexport const easeInOutCubic = (t: number): number =>\n t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;\n","import { boardHeight, projectPoint, type View, type Stage } from './camera.js';\nimport { boundsOf, overlapArea, areaOf, type Rect, type SceneElement } from './geometry.js';\nimport { isElementCommand, type Shot } from './types.js';\n\nexport interface ShotCheck {\n /** Elements this shot drew or changed that the camera frame leaves out (\"outside\") or cuts (\"cut\"). */\n framing: { id: string; how: 'outside' | 'cut' }[];\n /** Elements this shot drew that overlap each other significantly. */\n overlaps: [string, string][];\n /** Smallest text size on screen in CSS px. */\n smallestText?: { id: string; text: string; px: number };\n /** Any text smaller than min readable threshold (e.g. 18px). */\n smallTexts?: { id: string; text: string; px: number }[];\n /** Elements clipped by the screen edges. */\n edges: string[];\n /** Elements covered by the title bar. */\n underTitle: string[];\n}\n\nexport const NO_CHECK: ShotCheck = { framing: [], overlaps: [], edges: [], underTitle: [] };\n\nexport const SMALL_TEXT_PX = 24;\n\nexport function titleBox(title: string, stage: Stage): Rect {\n const width = Math.min(stage.width, title.length * 20 + 64);\n return { x: (stage.width - width) / 2, y: 24, width, height: 60 };\n}\n\nexport function touchedIds(shot: Shot): string[] {\n const ids = new Set<string>();\n for (const cmd of shot.run ?? []) {\n if (isElementCommand(cmd)) {\n ids.add(cmd.element.id);\n } else if ('id' in cmd && typeof (cmd as any).id === 'string') {\n ids.add((cmd as any).id);\n }\n }\n return [...ids];\n}\n\nconst IN_FRAME = 0.95;\n\n/**\n * Where a scene box lands on screen (stage pixels from the board's top\n * left), turned and in perspective as the view draws it: the bounds of its\n * four corners. Null when part of it is behind the camera.\n */\nexport function screenBox(box: Rect, view: View, stage: Stage): Rect | null {\n const corners = [\n [box.x, box.y],\n [box.x + box.width, box.y],\n [box.x, box.y + box.height],\n [box.x + box.width, box.y + box.height],\n ].map(([x, y]) => projectPoint(view, stage, [x!, y!]));\n if (corners.some((c) => !c)) return null;\n const xs = corners.map((c) => c![0]);\n const ys = corners.map((c) => c![1]);\n const x = Math.min(...xs);\n const y = Math.min(...ys);\n return { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y };\n}\n\n/**\n * Evaluates the elements on stage against the current camera view and shot\n * settings. Framing, edges, text size and the title are judged on screen,\n * where the view (its angle included) puts each element.\n */\nexport function checkShot(\n shot: Shot,\n elements: readonly SceneElement[],\n view: View,\n stage: Stage,\n title?: string,\n): ShotCheck {\n const screen: Rect = { x: 0, y: 0, width: stage.width, height: boardHeight(stage) };\n const touched = new Set(touchedIds(shot));\n const framing: { id: string; how: 'outside' | 'cut' }[] = [];\n const edges: string[] = [];\n const overlaps: [string, string][] = [];\n const activeElements = elements.filter((e) => !e.isDeleted && (e.opacity === undefined || e.opacity > 0));\n\n for (const el of activeElements) {\n const box = screenBox(boundsOf(el), view, stage);\n const overlap = box ? overlapArea(box, screen) : 0;\n const boxArea = box ? areaOf(box) : 0;\n\n if (touched.has(el.id)) {\n if (overlap === 0) {\n framing.push({ id: el.id, how: 'outside' });\n } else if (boxArea > 0 && overlap / boxArea < IN_FRAME) {\n framing.push({ id: el.id, how: 'cut' });\n }\n } else if (overlap > 0 && boxArea > 0 && overlap / boxArea < IN_FRAME) {\n edges.push(el.id);\n }\n }\n\n // Check overlaps among touched elements\n const touchedList = activeElements.filter((e) => touched.has(e.id));\n for (let i = 0; i < touchedList.length; i++) {\n for (let j = i + 1; j < touchedList.length; j++) {\n const a = touchedList[i]!;\n const b = touchedList[j]!;\n const aBox = boundsOf(a);\n const bBox = boundsOf(b);\n const ol = overlapArea(aBox, bBox);\n const minArea = Math.min(areaOf(aBox), areaOf(bBox));\n if (minArea > 0 && ol / minArea > 0.3) {\n overlaps.push([a.id, b.id]);\n }\n }\n }\n\n // Check text readability under current zoom\n const smallTexts: { id: string; text: string; px: number }[] = [];\n let smallestText: { id: string; text: string; px: number } | undefined;\n\n for (const el of activeElements) {\n if (el.text && el.fontSize) {\n // The text's height on screen where it sits: zoom, and perspective when the camera is angled.\n const b = boundsOf(el);\n const top = projectPoint(view, stage, [b.x + b.width / 2, b.y + b.height / 2]);\n const bottom = projectPoint(view, stage, [b.x + b.width / 2, b.y + b.height / 2 + el.fontSize]);\n if (!top || !bottom) continue;\n const px = Math.round(Math.hypot(bottom[0] - top[0], bottom[1] - top[1]));\n const item = { id: el.id, text: el.text, px };\n if (!smallestText || px < smallestText.px) {\n smallestText = item;\n }\n if (px < SMALL_TEXT_PX) {\n smallTexts.push(item);\n }\n }\n }\n\n // Check title obstruction\n const underTitle: string[] = [];\n if (title) {\n const tBox = titleBox(title, stage);\n for (const el of activeElements) {\n const box = screenBox(boundsOf(el), view, stage);\n if (box && overlapArea(box, tBox) > 0) {\n underTitle.push(el.id);\n }\n }\n }\n\n return {\n framing,\n overlaps,\n smallestText,\n smallTexts: smallTexts.length ? smallTexts : undefined,\n edges,\n underTitle,\n };\n}\n","import type { Rect } from './geometry.js';\n\nexport interface EffectBase {\n /** Unique identifier for the effect so it can be stopped or updated. */\n id: string;\n /** End the effect when this many subsequent shots begin. */\n shots?: number;\n /** Duration in milliseconds after which the effect stops automatically. */\n duration?: number;\n /** When to start: 'start' of shot or after commands are 'drawn'. Default: 'drawn'. */\n at?: 'start' | 'drawn';\n}\n\nexport interface GradientEffect extends EffectBase {\n type: 'gradient';\n colors: string[];\n rect?: Rect;\n ids?: string[];\n angle?: number;\n period?: number;\n opacity?: number;\n}\n\nexport interface FlowEffect extends EffectBase {\n type: 'flow';\n along: string;\n color?: string;\n dots?: number;\n period?: number;\n size?: number;\n direction?: 'forward' | 'back' | 'both';\n label?: string;\n}\n\nexport interface GlowEffect extends EffectBase {\n type: 'glow';\n ids: string[];\n color?: string;\n size?: number;\n pulse?: boolean;\n}\n\nexport interface SpotlightEffect extends EffectBase {\n type: 'spotlight';\n ids: string[];\n dim?: number;\n padding?: number;\n}\n\nexport interface ShimmerEffect extends EffectBase {\n type: 'shimmer';\n ids: string[];\n color?: string;\n period?: number;\n}\n\nexport interface RippleEffect extends EffectBase {\n type: 'ripple';\n ids: string[];\n color?: string;\n period?: number;\n size?: number;\n times?: number;\n}\n\nexport interface TimerEffect extends EffectBase {\n type: 'timer';\n ids: string[];\n time?: number;\n fill?: boolean;\n repeat?: boolean;\n color?: string;\n size?: number;\n}\n\nexport interface CounterEffect extends EffectBase {\n type: 'counter';\n to: number;\n from?: number;\n time?: number;\n text?: string;\n decimals?: number;\n near?: string;\n x?: number;\n y?: number;\n color?: string;\n size?: number;\n}\n\nexport interface HighlightEffect extends EffectBase {\n type: 'highlight';\n ids: string[];\n style?: 'marker' | 'underline';\n color?: string;\n}\n\nexport interface BlurEffect extends EffectBase {\n type: 'blur';\n ids: string[];\n amount?: number;\n padding?: number;\n}\n\nexport interface BurstEffect extends EffectBase {\n type: 'burst';\n ids: string[];\n color?: string;\n sparks?: number;\n size?: number;\n}\n\n/** Media layer effect driving a WebGL shader (or other media stream) under or over the scene. */\nexport interface MediaEffect extends EffectBase {\n type: 'media';\n /** ID in Scene.media. */\n media: string;\n rect?: Rect;\n ids?: string[];\n padding?: number;\n layer?: 'under' | 'over';\n opacity?: number;\n}\n\nexport type Effect =\n | GradientEffect\n | FlowEffect\n | GlowEffect\n | SpotlightEffect\n | ShimmerEffect\n | RippleEffect\n | TimerEffect\n | CounterEffect\n | HighlightEffect\n | BlurEffect\n | BurstEffect\n | MediaEffect;\n\nexport const EFFECT_TYPES = [\n 'gradient',\n 'flow',\n 'glow',\n 'spotlight',\n 'shimmer',\n 'ripple',\n 'timer',\n 'counter',\n 'highlight',\n 'blur',\n 'burst',\n 'media',\n] as const;\n\nexport const EFFECT_FADE_MS = 300;\n\nexport interface RunningEffect {\n effect: Effect;\n /** Timestamp when started (ms). */\n from: number;\n /** Timestamp when stopping/fading begins (ms). */\n until?: number;\n}\n\n/** Returns true if the effect has completely expired and finished fading out. */\nexport function effectGone(running: RunningEffect, now: number): boolean {\n if (running.until !== undefined) {\n return now >= running.until + EFFECT_FADE_MS;\n }\n if (running.effect.duration !== undefined) {\n return now >= running.from + running.effect.duration + EFFECT_FADE_MS;\n }\n return false;\n}\n\n/** Returns current opacity multiplier (0 to 1) accounting for fade in / fade out. */\nexport function effectOpacity(running: RunningEffect, now: number): number {\n if (now < running.from) return 0;\n // Fade in over first 150ms\n const inFade = Math.min(1, (now - running.from) / 150);\n\n if (running.until !== undefined) {\n const out = Math.max(0, 1 - (now - running.until) / EFFECT_FADE_MS);\n return inFade * out;\n }\n if (running.effect.duration !== undefined) {\n const remaining = running.from + running.effect.duration - now;\n if (remaining < 0) {\n const out = Math.max(0, 1 + remaining / EFFECT_FADE_MS);\n return inFade * out;\n }\n }\n return inFade;\n}\n","import type { EasingCurve } from '../time/retiming.js';\n\nexport interface BehaviorBase {\n id?: string;\n /** Target element ID or media ID. */\n target: string;\n /** Property on element (e.g. 'x', 'y', 'scale', 'rotation', 'opacity') or uniform on media. */\n property: string;\n}\n\nexport interface OscillateBehavior extends BehaviorBase {\n type: 'oscillate';\n wave?: 'sine' | 'square' | 'triangle' | 'sawtooth';\n amplitude: number;\n /** Frequency in Hz (cycles per second). */\n frequency: number;\n /** Phase offset in radians (default 0). */\n phase?: number;\n /** Base center value. If undefined, treated as relative offset added to current base value. */\n center?: number;\n}\n\nexport interface RampBehavior extends BehaviorBase {\n type: 'ramp';\n from?: number;\n to: number;\n ease?: EasingCurve;\n}\n\nexport interface RateBehavior extends BehaviorBase {\n type: 'rate';\n /** Rate of change per second (e.g. degrees/sec or units/sec). */\n rate: number;\n}\n\nexport interface WriggleBehavior extends BehaviorBase {\n type: 'wriggle';\n /** Maximum amplitude of deviation. */\n amount: number;\n /** Speed / frequency of variation in Hz. */\n frequency: number;\n /** Number of noise octaves for organic complexity (default: 3). */\n octaves?: number;\n /** Deterministic random seed. */\n seed?: number;\n}\n\nexport interface OvershootBehavior extends BehaviorBase {\n type: 'overshoot';\n targetValue: number;\n startValue?: number;\n /** Oscillation frequency / cycles (default: 3). */\n cycles?: number;\n /** Damping factor (higher settles faster, default: 4.0). */\n damping?: number;\n}\n\nexport interface ThrowBehavior extends BehaviorBase {\n type: 'throw';\n /** Initial speed in units per second. */\n initialVelocity: number;\n /** Friction / deceleration factor (default: 2.0). */\n friction?: number;\n}\n\nexport type Behavior =\n | OscillateBehavior\n | RampBehavior\n | RateBehavior\n | WriggleBehavior\n | OvershootBehavior\n | ThrowBehavior;\n\nimport { fractalWiggle } from '../time/noise.js';\n\n/**\n * Computes smooth organic 1D multi-octave C2 noise wander matching AE wiggle.\n */\nfunction evaluateWriggle(\n t: number,\n freq: number,\n amount: number,\n octaves = 3,\n seed = 42,\n): number {\n return fractalWiggle(freq, amount, octaves, 0.5, t, seed, 0);\n}\n\n/**\n * Evaluates the new numeric value for a behavior at a given elapsed time.\n * @param behavior The behavior specification.\n * @param elapsedSec Elapsed time within the shot/behavior duration (in seconds).\n * @param durationSec Total duration of the shot/behavior (in seconds).\n * @param baseValue Initial or fallback value before behavior was applied.\n */\nexport function evaluateBehavior(\n behavior: Behavior,\n elapsedSec: number,\n durationSec: number,\n baseValue = 0,\n): number {\n const t = Math.max(0, elapsedSec);\n\n switch (behavior.type) {\n case 'oscillate': {\n const freq = behavior.frequency;\n const phase = behavior.phase ?? 0;\n const angle = 2 * Math.PI * freq * t + phase;\n let waveVal = 0;\n\n switch (behavior.wave) {\n case 'square':\n waveVal = Math.sin(angle) >= 0 ? 1 : -1;\n break;\n case 'triangle':\n waveVal = (2 / Math.PI) * Math.asin(Math.sin(angle));\n break;\n case 'sawtooth': {\n const norm = (angle / (2 * Math.PI)) % 1;\n waveVal = 2 * (norm < 0 ? norm + 1 : norm) - 1;\n break;\n }\n case 'sine':\n default:\n waveVal = Math.sin(angle);\n break;\n }\n\n const offset = waveVal * behavior.amplitude;\n return behavior.center !== undefined ? behavior.center + offset : baseValue + offset;\n }\n\n case 'ramp': {\n const from = behavior.from ?? baseValue;\n const to = behavior.to;\n const p = durationSec > 0 ? Math.min(1, Math.max(0, t / durationSec)) : 1;\n let eased = p;\n\n switch (behavior.ease) {\n case 'easeIn':\n eased = p * p;\n break;\n case 'easeOut':\n eased = p * (2 - p);\n break;\n case 'easeInOut':\n eased = p < 0.5 ? 2 * p * p : -1 + (4 - 2 * p) * p;\n break;\n case 'linear':\n default:\n eased = p;\n break;\n }\n\n return from + (to - from) * eased;\n }\n\n case 'rate': {\n return baseValue + behavior.rate * t;\n }\n\n case 'wriggle': {\n const wander = evaluateWriggle(\n t,\n behavior.frequency,\n behavior.amount,\n behavior.octaves ?? 3,\n behavior.seed ?? 42,\n );\n return baseValue + wander;\n }\n\n case 'overshoot': {\n const from = behavior.startValue ?? baseValue;\n const target = behavior.targetValue;\n const cycles = behavior.cycles ?? 3;\n const damping = behavior.damping ?? 4.0;\n\n if (durationSec <= 0) return target;\n const p = Math.min(1, t / durationSec);\n if (p >= 1) return target;\n\n // Damped harmonic oscillation: 1 - e^(-damping * p) * cos(cycles * pi * p)\n const envelope = Math.exp(-damping * p);\n const oscillation = Math.cos(cycles * Math.PI * p);\n const progress = 1 - envelope * oscillation;\n\n return from + (target - from) * progress;\n }\n\n case 'throw': {\n const v0 = behavior.initialVelocity;\n const k = Math.max(0.001, behavior.friction ?? 2.0);\n // Continuous displacement under linear drag: x(t) = (v0 / k) * (1 - e^(-k * t))\n const displacement = (v0 / k) * (1 - Math.exp(-k * t));\n return baseValue + displacement;\n }\n }\n}\n","import type { ShotCheck } from './check.js';\nimport type { Scene } from './types.js';\n\nexport interface ShotLog extends ShotCheck {\n index: number;\n start: number;\n drawn: number;\n end: number;\n caption?: string;\n title?: string;\n failed: string[];\n effects?: string[];\n}\n\nexport interface ReviewOptions {\n target?: [number, number];\n minTextPx?: number;\n}\n\nexport interface Review {\n totalMs: number;\n warnings: string[];\n notes: string[];\n markdown: string;\n}\n\n/** Formats a structured diagnostic report of scene playback. */\nexport function reviewScene(scene: Scene, logs: readonly ShotLog[], options: ReviewOptions = {}): Review {\n const warnings: string[] = [];\n const notes: string[] = [];\n const minTextPx = options.minTextPx ?? 18;\n const totalMs = logs.length > 0 ? logs[logs.length - 1]!.end : 0;\n const totalSec = (totalMs / 1000).toFixed(1);\n\n if (scene.target) {\n const [minS, maxS] = scene.target;\n const durS = totalMs / 1000;\n if (durS < minS) {\n warnings.push(`Scene is ${totalSec}s, shorter than target minimum (${minS}s).`);\n } else if (durS > maxS) {\n warnings.push(`Scene is ${totalSec}s, longer than target maximum (${maxS}s).`);\n }\n }\n\n for (const log of logs) {\n const shotPrefix = `Shot ${log.index}:`;\n\n if (log.failed.length > 0) {\n warnings.push(`${shotPrefix} ${log.failed.length} command(s) failed: ${log.failed.join(', ')}`);\n }\n\n for (const f of log.framing) {\n warnings.push(`${shotPrefix} element \"${f.id}\" is ${f.how} of the camera frame.`);\n }\n\n for (const [a, b] of log.overlaps) {\n notes.push(`${shotPrefix} elements \"${a}\" and \"${b}\" overlap significantly.`);\n }\n\n if (log.smallTexts) {\n for (const t of log.smallTexts) {\n if (t.px < minTextPx) {\n warnings.push(`${shotPrefix} text \"${t.text}\" is only ${t.px}px on screen (less than ${minTextPx}px minimum).`);\n }\n }\n }\n\n for (const id of log.underTitle) {\n warnings.push(`${shotPrefix} element \"${id}\" is partially covered by the title bar.`);\n }\n }\n\n // Generate markdown\n const lines: string[] = [\n `# Scene Preview Review: ${scene.title || scene.name}`,\n `**Total Duration:** ${totalSec}s (${logs.length} shots)`,\n '',\n ];\n\n if (warnings.length > 0) {\n lines.push('### ⚠️ Warnings to Address:');\n for (const w of warnings) lines.push(`- ${w}`);\n lines.push('');\n } else {\n lines.push('✅ No critical layout or framing warnings.');\n lines.push('');\n }\n\n if (notes.length > 0) {\n lines.push('### ℹ️ Layout Notes:');\n for (const n of notes) lines.push(`- ${n}`);\n lines.push('');\n }\n\n lines.push('### Shot Breakdown');\n lines.push('| Shot | Start | End | Duration | Title / Caption |');\n lines.push('|:---:|:---:|:---:|:---:|:---|');\n for (const log of logs) {\n const dur = ((log.end - log.start) / 1000).toFixed(1);\n const text = log.title || log.caption || '—';\n lines.push(`| ${log.index} | ${(log.start / 1000).toFixed(1)}s | ${(log.end / 1000).toFixed(1)}s | ${dur}s | ${text.slice(0, 40)} |`);\n }\n\n return {\n totalMs,\n warnings,\n notes,\n markdown: lines.join('\\n'),\n };\n}\n","import type { Clock } from '../time/clock.js';\nimport { ManualClock } from '../time/clock.js';\nimport {\n between,\n boardHeight,\n cameraAngle,\n centreOf,\n CAMERA_MS,\n DEFAULT_STAGE,\n easeInOutCubic,\n frame,\n calculateFramingRect,\n isAngled,\n moveBlur,\n projectPoint,\n viewAt,\n type Stage,\n type View,\n} from './camera.js';\nimport { checkShot } from './check.js';\nimport { effectGone, type RunningEffect } from './effects.js';\nimport { evaluateBehavior } from './behavior.js';\nimport type { Rect, SceneElement } from './geometry.js';\nimport { reviewScene, type Review, type ShotLog } from './report.js';\nimport {\n isDeleteCommand,\n isElementCommand,\n isUpdateCommand,\n type Scene,\n type SceneCommand,\n type Shot,\n} from './types.js';\n\nexport interface SceneBoard {\n getView(): View;\n setView(view: View): void;\n elements(): readonly SceneElement[];\n runCommand(cmd: SceneCommand): void | Promise<void>;\n setTitle?(text: string, opacity: number): void;\n setCaption?(text: string): void;\n setEffects?(effects: readonly RunningEffect[]): void;\n setMediaTime?(mediaId: string, timeSeconds: number): void;\n}\n\n/** Pure in-memory board implementation suitable for headless simulation, testing, and SSR. */\nexport class MemorySceneBoard implements SceneBoard {\n private view: View;\n private elMap = new Map<string, SceneElement>();\n private currentTitle = '';\n private currentCaption = '';\n private runningEffects: readonly RunningEffect[] = [];\n\n constructor(stage: Stage = DEFAULT_STAGE) {\n this.view = viewAt(stage.width / 2, stage.height / 2, 1, stage);\n }\n\n getView(): View {\n return this.view;\n }\n\n setView(view: View): void {\n this.view = view;\n }\n\n elements(): readonly SceneElement[] {\n return Array.from(this.elMap.values());\n }\n\n runCommand(cmd: SceneCommand): void {\n if (isElementCommand(cmd)) {\n this.elMap.set(cmd.element.id, { ...cmd.element });\n } else if (isUpdateCommand(cmd)) {\n const existing = this.elMap.get(cmd.id);\n if (existing) {\n this.elMap.set(cmd.id, { ...existing, ...cmd.props });\n }\n } else if (isDeleteCommand(cmd)) {\n this.elMap.delete(cmd.id);\n } else if (cmd.type === 'clear') {\n this.elMap.clear();\n }\n }\n\n setTitle(text: string): void {\n this.currentTitle = text;\n }\n\n setCaption(text: string): void {\n this.currentCaption = text;\n }\n\n setEffects(effects: readonly RunningEffect[]): void {\n this.runningEffects = effects;\n }\n}\n\n/**\n * The view a shot's camera moves to: its framing (a box, or the elements it\n * frames, from the board or the shot's own commands) at its angle, zoomed\n * out when angled so the framed box still fits. Framing\n * or angle parts the camera leaves out keep `current`'s. Undefined when the\n * shot has no camera.\n */\nexport function shotView(\n shot: Shot,\n elements: readonly SceneElement[],\n stage: Stage,\n current: View,\n): View | undefined {\n const camera = shot.camera;\n if (!camera) return undefined;\n let view: View = { zoom: current.zoom, scrollX: current.scrollX, scrollY: current.scrollY };\n let framed: Rect | undefined;\n if ('frame' in camera) {\n const frameIds = new Set(camera.frame);\n const targetElements = elements.filter((el) => frameIds.has(el.id));\n if (targetElements.length === 0) {\n for (const cmd of shot.run ?? []) {\n if (isElementCommand(cmd) && frameIds.has(cmd.element.id)) targetElements.push(cmd.element);\n }\n }\n framed = calculateFramingRect(targetElements, stage, {\n padding: camera.padding,\n minWidth: camera.minWidth,\n minHeight: camera.minHeight,\n });\n } else if ('width' in camera) {\n framed = camera as Rect;\n }\n if (framed) view = frame(framed, stage);\n const angle = cameraAngle({\n tilt: camera.tilt ?? current.tilt,\n turn: camera.turn ?? current.turn,\n roll: camera.roll ?? current.roll,\n perspective: camera.perspective ?? current.perspective,\n });\n const angled: View = { ...view, ...angle };\n return framed && isAngled(angled) ? fitAngled(angled, framed, stage) : angled;\n}\n\n/**\n * An angled view zoomed out (round the same centre) until the framed box,\n * turned and in perspective, fits the board again: perspective makes the\n * near side bigger, so a flat framing would cut it.\n */\nfunction fitAngled(view: View, box: Rect, stage: Stage): View {\n const [cx, cy] = centreOf(view, stage);\n const halfW = stage.width / 2;\n const halfH = boardHeight(stage) / 2;\n let fitted = view;\n for (let i = 0; i < 4; i++) {\n let spread = 0;\n for (const corner of [[box.x, box.y], [box.x + box.width, box.y], [box.x, box.y + box.height], [box.x + box.width, box.y + box.height]] as const) {\n const p = projectPoint(fitted, stage, corner);\n spread = p ? Math.max(spread, Math.abs(p[0] - halfW) / halfW, Math.abs(p[1] - halfH) / halfH) : Infinity;\n if (!p) break;\n }\n if (spread <= 1.001) break;\n const zoom = fitted.zoom / (Number.isFinite(spread) ? spread : 2);\n fitted = { ...fitted, ...viewAt(cx, cy, zoom, stage) };\n }\n return fitted;\n}\n\nexport interface PlayOptions {\n check?: boolean;\n afterShot?: (log: ShotLog) => void | Promise<void>;\n}\n\nexport interface PlayResult {\n ok: boolean;\n duration: number;\n shots: ShotLog[];\n}\n\n/**\n * Plays a scene's shots sequentially on `board` driven by `clock`.\n * When using a `ManualClock`, the execution is completely deterministic and stepped.\n */\nexport async function playScene(\n scene: Scene,\n board: SceneBoard,\n clock: Clock,\n options: PlayOptions = {},\n): Promise<PlayResult> {\n const stage: Stage = {\n width: scene.stage?.width ?? DEFAULT_STAGE.width,\n height: scene.stage?.height ?? DEFAULT_STAGE.height,\n caption: scene.stage?.caption ?? DEFAULT_STAGE.caption,\n };\n\n const shots: ShotLog[] = [];\n const begin = clock.now();\n let runningEffects: RunningEffect[] = [];\n const endsAt = new Map<RunningEffect, number>();\n\n const updateEffects = (next: RunningEffect[]) => {\n runningEffects = next.filter((r) => !effectGone(r, clock.now()));\n board.setEffects?.(runningEffects);\n };\n\n for (const [idx, shot] of scene.shots.entries()) {\n const shotStart = clock.now() - begin;\n const failedCmds: string[] = [];\n\n // Stop requested effects\n const stopIds = new Set(shot.stopEffects ?? []);\n if (stopIds.size > 0 || runningEffects.some((r) => endsAt.get(r) === idx)) {\n const now = clock.now();\n updateEffects(\n runningEffects.map((r) =>\n r.until === undefined && (stopIds.has(r.effect.id) || endsAt.get(r) === idx)\n ? { ...r, until: now }\n : r,\n ),\n );\n }\n\n // Camera move\n const targetView = shotView(shot, board.elements(), stage, board.getView());\n if (targetView) {\n if (idx === 0) {\n board.setView(targetView);\n } else {\n const fromView = board.getView();\n const strength = shot.camera?.motionBlur ?? 0;\n const steps = 15;\n const stepMs = CAMERA_MS / steps;\n let last = fromView;\n for (let s = 1; s <= steps; s++) {\n const t = easeInOutCubic(s / steps);\n const view = between(fromView, targetView, t, stage);\n board.setView(s < steps ? { ...view, ...moveBlur(last, view, stage, strength) } : view);\n last = view;\n await clock.sleep(stepMs);\n }\n }\n }\n\n // Set title and caption\n if (shot.title !== undefined) {\n board.setTitle?.(shot.title, 1);\n }\n if (shot.caption !== undefined) {\n board.setCaption?.(shot.caption);\n }\n\n // Start effects scheduled at 'start'\n const startEffects = (shot.effects ?? []).filter((e) => e.at === 'start');\n if (startEffects.length > 0) {\n const now = clock.now();\n const added = startEffects.map((effect): RunningEffect => ({ effect, from: now }));\n for (const r of added) if (r.effect.shots) endsAt.set(r, idx + r.effect.shots);\n updateEffects([...runningEffects, ...added]);\n }\n\n // Run commands\n for (const cmd of shot.run ?? []) {\n try {\n await board.runCommand(cmd);\n } catch (err: any) {\n failedCmds.push(err.message || String(err));\n }\n }\n\n // Start effects scheduled after 'drawn' (default)\n const drawnEffects = (shot.effects ?? []).filter((e) => e.at !== 'start');\n if (drawnEffects.length > 0) {\n const now = clock.now();\n const added = drawnEffects.map((effect): RunningEffect => ({ effect, from: now }));\n for (const r of added) if (r.effect.shots) endsAt.set(r, idx + r.effect.shots);\n updateEffects([...runningEffects, ...added]);\n }\n\n const drawnTime = clock.now() - begin;\n\n // Check shot layout & framing\n const check = options.check\n ? checkShot(shot, board.elements(), board.getView(), stage, shot.title)\n : { framing: [], overlaps: [], edges: [], underTitle: [] };\n\n // Hold dwell time and active behaviors\n const holdMs = shot.hold ?? 800;\n const behaviors = shot.behaviors ?? [];\n if (behaviors.length > 0) {\n const initialProps = new Map<string, number>();\n for (const b of behaviors) {\n const key = `${b.target}:${b.property}`;\n const el = board.elements().find((e) => e.id === b.target);\n if (el && typeof (el as any)[b.property] === 'number') {\n initialProps.set(key, (el as any)[b.property]);\n } else {\n initialProps.set(key, 0);\n }\n }\n\n const stepMs = 50;\n const steps = Math.max(1, Math.round(holdMs / stepMs));\n const actualStepMs = holdMs / steps;\n const durationSec = holdMs / 1000;\n\n for (let s = 1; s <= steps; s++) {\n await clock.sleep(actualStepMs);\n const elapsedSec = (s * actualStepMs) / 1000;\n for (const b of behaviors) {\n const baseVal = initialProps.get(`${b.target}:${b.property}`) ?? 0;\n const nextVal = evaluateBehavior(b, elapsedSec, durationSec, baseVal);\n board.runCommand({\n type: 'update',\n id: b.target,\n props: { [b.property]: nextVal },\n });\n }\n }\n } else {\n await clock.sleep(holdMs);\n }\n\n const shotEnd = clock.now() - begin;\n\n const log: ShotLog = {\n ...check,\n index: idx + 1,\n start: shotStart,\n drawn: drawnTime,\n end: shotEnd,\n caption: shot.caption,\n title: shot.title,\n failed: failedCmds,\n effects: runningEffects.map((r) => r.effect.id),\n };\n\n shots.push(log);\n if (options.afterShot) {\n await options.afterShot(log);\n }\n }\n\n const duration = clock.now() - begin;\n const ok = shots.every((s) => s.failed.length === 0 && s.framing.every((f) => f.how !== 'outside'));\n\n return { ok, duration, shots };\n}\n\n/**\n * Simulates a scene instantaneously using a ManualClock and MemorySceneBoard,\n * returning full timing details, layout checks, and diagnostic report.\n */\nexport async function simulateScene(\n scene: Scene,\n options: { minTextPx?: number } = {},\n): Promise<{ result: PlayResult; review: Review }> {\n const clock = new ManualClock();\n const board = new MemorySceneBoard();\n\n let done = false;\n let playError: any = null;\n const playPromise = playScene(scene, board, clock, { check: true })\n .then((res) => {\n done = true;\n return res;\n })\n .catch((err) => {\n done = true;\n playError = err;\n throw err;\n });\n\n // Advance manual clock until simulation finishes\n for (let i = 0; i < 5000 && !done; i++) {\n await clock.advance(50);\n }\n\n if (playError) throw playError;\n const result = await playPromise;\n const review = reviewScene(scene, result.shots, {\n target: scene.target,\n minTextPx: options.minTextPx,\n });\n\n return { result, review };\n}\n","import { between, CAMERA_MS, DEFAULT_STAGE, easeInOutCubic, moveBlur, viewAt, type Stage, type View } from './camera.js';\nimport type { SceneElement } from './geometry.js';\nimport { MemorySceneBoard, shotView } from './player.js';\nimport type { Scene, Shot } from './types.js';\n\n/** The camera through one shot, timed as `playScene` plays it: a move from `from` to `to`, then a hold. */\nexport interface CameraShot {\n shot: Shot;\n index: number;\n /** When the shot starts on the scene's timeline, in ms. */\n startMs: number;\n /** How long the camera moves at the start of the shot (0 when it cuts or stays). */\n moveMs: number;\n /** How long the shot holds after the move. */\n holdMs: number;\n from: View;\n to: View;\n /** The shot's `camera.motionBlur` (0 for none). */\n motionBlur: number;\n /** The elements on stage once the shot's commands have run. */\n elements: SceneElement[];\n}\n\n/** The scene's stage with defaults filled in. */\nexport function sceneStage(scene: Scene): Stage {\n return {\n width: scene.stage?.width ?? DEFAULT_STAGE.width,\n height: scene.stage?.height ?? DEFAULT_STAGE.height,\n caption: scene.stage?.caption ?? DEFAULT_STAGE.caption,\n };\n}\n\n/**\n * The camera through every shot of a scene, worked out the way `playScene`\n * moves it, for exporters and players that lay a scene out on a timeline:\n * each shot's move (framing, angle and blur strength) and hold.\n */\nexport function cameraTrack(scene: Scene): CameraShot[] {\n const stage = sceneStage(scene);\n const board = new MemorySceneBoard(stage);\n let view: View = viewAt(stage.width / 2, (stage.height - (stage.caption ?? 0)) / 2, 1, stage);\n let startMs = 0;\n return scene.shots.map((shot, index) => {\n const target = shotView(shot, board.elements(), stage, view);\n const moveMs = index > 0 && shot.camera ? CAMERA_MS : 0;\n const from = index === 0 && target ? target : view;\n const to = target ?? view;\n const holdMs = shot.hold ?? 800;\n for (const cmd of shot.run ?? []) board.runCommand(cmd);\n const entry: CameraShot = {\n shot,\n index,\n startMs,\n moveMs,\n holdMs,\n from,\n to,\n motionBlur: shot.camera?.motionBlur ?? 0,\n elements: board.elements().map((e) => ({ ...e })),\n };\n view = to;\n startMs += moveMs + holdMs;\n return entry;\n });\n}\n\n/** The time the player waits between steps of a camera move, in ms (its blur is measured over one). */\nexport const CAMERA_STEP_MS = CAMERA_MS / 15;\n\n/**\n * The camera `ms` into a shot: part-way through its move (eased, with its\n * motion blur) or settled.\n */\nexport function cameraAt(entry: CameraShot, ms: number, stage: Stage): View {\n if (!(entry.moveMs > 0) || ms >= entry.moveMs) return entry.to;\n const u = Math.max(0, ms) / entry.moveMs;\n const view = between(entry.from, entry.to, easeInOutCubic(u), stage);\n if (!(entry.motionBlur > 0)) return view;\n const before = between(entry.from, entry.to, easeInOutCubic(Math.max(0, u - CAMERA_STEP_MS / entry.moveMs)), stage);\n return { ...view, ...moveBlur(before, view, stage, entry.motionBlur) };\n}\n\n/** The camera at one frame of a scene's timeline. */\nexport interface CameraSample {\n frame: number;\n view: View;\n /** The shot (0-based) the frame is in. */\n shot: number;\n}\n\n/**\n * The camera sampled for a timeline at `frameRate`: every `every` frames\n * during each move (so an editor's straight-line interpolation follows the\n * eased path and perspective), and once where each move settles. Views\n * include the move's motion blur.\n */\nexport function cameraSamples(scene: Scene, frameRate: number, every = 2): CameraSample[] {\n const stage = sceneStage(scene);\n const samples: CameraSample[] = [];\n // A later sample on the same frame (a shot with no hold) replaces the earlier one.\n const add = (sample: CameraSample) => {\n if (samples.at(-1)?.frame === sample.frame) samples.pop();\n samples.push(sample);\n };\n for (const entry of cameraTrack(scene)) {\n const start = Math.round((entry.startMs / 1000) * frameRate);\n const moveFrames = Math.round((entry.moveMs / 1000) * frameRate);\n for (let f = 0; f < moveFrames; f += every) {\n add({ frame: start + f, view: cameraAt(entry, (f / frameRate) * 1000, stage), shot: entry.index });\n }\n add({ frame: start + moveFrames, view: entry.to, shot: entry.index });\n }\n return samples;\n}\n"],"mappings":"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","names":["x","y"]}
@@ -1,6 +1,6 @@
1
1
  // src/shader/controls.ts
2
2
  var DECLARATION = /^\s*uniform\s+(float|int|bool|vec2|vec3|vec4|sampler2D)\s+([A-Za-z_]\w*)\s*;\s*\/\/\s*@control\b(.*)$/;
3
- var ATTRIBUTE = /([a-z]+)(?:=("[^"]*"|\S+))?/gi;
3
+ var ATTRIBUTE = /([a-z]+)(?:=("[^"]*"|'[^']*'|\S+))?/gi;
4
4
  var COMPONENTS = {
5
5
  float: 1,
6
6
  int: 1,
@@ -8,7 +8,8 @@ var COMPONENTS = {
8
8
  vec2: 2,
9
9
  vec3: 3,
10
10
  vec4: 4,
11
- sampler2D: 1
11
+ sampler2D: 1,
12
+ text: 1
12
13
  };
13
14
  function componentCount(type) {
14
15
  return COMPONENTS[type];
@@ -32,7 +33,7 @@ function rgbToHex(rgb) {
32
33
  function parseDefault(raw, type, color) {
33
34
  const n = COMPONENTS[type];
34
35
  if (type === "bool") return raw === "true" || raw === "1";
35
- if (type === "sampler2D") return raw ?? "";
36
+ if (type === "sampler2D" || type === "text") return raw ?? "";
36
37
  if (raw === void 0) return n === 1 ? 0 : Array.from({ length: n }, () => color ? 1 : 0);
37
38
  if (color && raw.startsWith("#")) {
38
39
  const rgb = hexToRgb(raw) ?? [1, 1, 1];
@@ -42,43 +43,44 @@ function parseDefault(raw, type, color) {
42
43
  if (n === 1) return parts[0] ?? 0;
43
44
  return Array.from({ length: n }, (_, i) => parts[i] ?? parts[parts.length - 1] ?? 0);
44
45
  }
46
+ function controlFromDeclaration(name, type, attributes, line) {
47
+ const attrs = {};
48
+ for (const a of attributes.matchAll(ATTRIBUTE)) {
49
+ attrs[a[1].toLowerCase()] = a[2]?.replace(/^(["'])(.*)\1$/s, "$2");
50
+ }
51
+ const color = "color" in attrs && (type === "vec3" || type === "vec4");
52
+ const num = (v, fallback) => v !== void 0 && Number.isFinite(Number(v)) ? Number(v) : fallback;
53
+ const min = num(attrs.min, 0);
54
+ const rawMax = num(attrs.max, type === "int" ? 10 : 1);
55
+ const max = rawMax > min ? rawMax : min + 1;
56
+ const control = {
57
+ name,
58
+ type,
59
+ label: attrs.label ?? humanise(name),
60
+ color,
61
+ min,
62
+ max,
63
+ step: num(attrs.step, type === "int" ? 1 : (max - min) / 200),
64
+ default: parseDefault(attrs.default, type, color),
65
+ line
66
+ };
67
+ if (type === "sampler2D") {
68
+ const pick = (v, options) => options.find((o) => o === v?.toLowerCase()) ?? options[0];
69
+ control.texture = {
70
+ filter: pick(attrs.filter, ["mipmap", "linear", "nearest"]),
71
+ wrap: pick(attrs.wrap, ["repeat", "clamp", "mirror"])
72
+ };
73
+ }
74
+ return control;
75
+ }
45
76
  function parseControls(source) {
46
77
  const controls = [];
47
78
  const seen = /* @__PURE__ */ new Set();
48
79
  source.split("\n").forEach((text, index) => {
49
80
  const m = DECLARATION.exec(text);
50
81
  if (!m || seen.has(m[2])) return;
51
- const type = m[1];
52
- const name = m[2];
53
- seen.add(name);
54
- const attrs = {};
55
- for (const a of m[3].matchAll(ATTRIBUTE)) {
56
- attrs[a[1].toLowerCase()] = a[2]?.replace(/^"|"$/g, "");
57
- }
58
- const color = "color" in attrs && (type === "vec3" || type === "vec4");
59
- const num = (v, fallback) => v !== void 0 && Number.isFinite(Number(v)) ? Number(v) : fallback;
60
- const min = num(attrs.min, 0);
61
- const rawMax = num(attrs.max, type === "int" ? 10 : 1);
62
- const max = rawMax > min ? rawMax : min + 1;
63
- const control = {
64
- name,
65
- type,
66
- label: attrs.label ?? humanise(name),
67
- color,
68
- min,
69
- max,
70
- step: num(attrs.step, type === "int" ? 1 : (max - min) / 200),
71
- default: parseDefault(attrs.default, type, color),
72
- line: index + 1
73
- };
74
- if (type === "sampler2D") {
75
- const pick = (v, options) => options.find((o) => o === v?.toLowerCase()) ?? options[0];
76
- control.texture = {
77
- filter: pick(attrs.filter, ["mipmap", "linear", "nearest"]),
78
- wrap: pick(attrs.wrap, ["repeat", "clamp", "mirror"])
79
- };
80
- }
81
- controls.push(control);
82
+ seen.add(m[2]);
83
+ controls.push(controlFromDeclaration(m[2], m[1], m[3], index + 1));
82
84
  });
83
85
  return controls;
84
86
  }
@@ -86,7 +88,7 @@ function controlValue(control, values) {
86
88
  const v = values?.[control.name];
87
89
  if (v === void 0) return control.default;
88
90
  if (control.type === "bool") return typeof v === "boolean" ? v : control.default;
89
- if (control.type === "sampler2D") return typeof v === "string" ? v : control.default;
91
+ if (control.type === "sampler2D" || control.type === "text") return typeof v === "string" ? v : control.default;
90
92
  const n = COMPONENTS[control.type];
91
93
  if (n === 1) return typeof v === "number" ? v : control.default;
92
94
  return Array.isArray(v) && v.length === n ? v : control.default;
@@ -100,6 +102,9 @@ function checkControlValue(control, raw) {
100
102
  if (control.type === "sampler2D") {
101
103
  return typeof raw === "string" && (raw === "" || isImageUrl(raw)) ? { ok: true, value: raw } : fail(`${control.name} takes an image URL: https://\u2026, data:image/\u2026 or blob:\u2026.`);
102
104
  }
105
+ if (control.type === "text") {
106
+ return typeof raw === "string" ? { ok: true, value: raw } : fail(`${control.name} takes text.`);
107
+ }
103
108
  if (control.color && typeof raw === "string") {
104
109
  const rgb = hexToRgb(raw);
105
110
  if (!rgb) return fail(`${control.name} takes a colour like "#77C16A".`);
@@ -153,6 +158,7 @@ export {
153
158
  humanise,
154
159
  hexToRgb,
155
160
  rgbToHex,
161
+ controlFromDeclaration,
156
162
  parseControls,
157
163
  controlValue,
158
164
  checkControlValue,
@@ -161,4 +167,4 @@ export {
161
167
  imageUrls,
162
168
  describeValue
163
169
  };
164
- //# sourceMappingURL=chunk-J55UBNAT.js.map
170
+ //# sourceMappingURL=chunk-WC3VADZC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shader/controls.ts"],"sourcesContent":["/**\n * Controls declared in a shader's source. A uniform becomes a control when\n * its declaration carries an `@control` comment on the same line:\n *\n * uniform float uSpeed; // @control min=0 max=4 default=1 label=\"Speed\"\n * uniform int uSteps; // @control min=1 max=64 default=16\n * uniform bool uGrain; // @control default=true label=\"Film grain\"\n * uniform vec2 uCenter; // @control min=-1 max=1 default=0,0\n * uniform vec3 uTint; // @control color default=#77C16A label=\"Tint\"\n * uniform sampler2D uPhoto; // @control filter=linear wrap=clamp label=\"Photo\"\n *\n * The source is the single source of truth for which controls exist and\n * their defaults; values someone picks are stored next to it.\n */\n\n/** The uniform types a control can have. */\nexport type ControlType = \"float\" | \"int\" | \"bool\" | \"vec2\" | \"vec3\" | \"vec4\" | \"sampler2D\" | \"text\";\n\n/**\n * A control's value: a number (`float`, `int`), a boolean (`bool`), the\n * components of a vector, an image URL (`sampler2D`; \"\" for none), or a\n * string (`text`, vector effects only: shaders cannot take text).\n */\nexport type ControlValue = number | boolean | number[] | string;\n\n/** How an image is sampled: Shadertoy's channel settings. */\nexport interface TextureSettings {\n /** `mipmap` (the default), `linear` or `nearest` (pixelated). */\n filter: \"mipmap\" | \"linear\" | \"nearest\";\n /** Outside 0–1: `repeat` (the default), `clamp` to the edge, or `mirror`. */\n wrap: \"repeat\" | \"clamp\" | \"mirror\";\n}\n\n/** A control parsed from an annotated uniform. */\nexport interface ControlDef {\n /** The uniform's name. */\n name: string;\n type: ControlType;\n /** `label=\"…\"`, or the name made readable (\"uFlowSpeed\" → \"Flow speed\"). */\n label: string;\n /** A `vec3` or `vec4` shown as a colour picker. */\n color: boolean;\n min: number;\n /** Always greater than `min`. */\n max: number;\n step: number;\n default: ControlValue;\n /** 1-based line of the declaration. */\n line: number;\n /** Sampling settings, for `sampler2D` controls only. */\n texture?: TextureSettings;\n}\n\nconst DECLARATION =\n /^\\s*uniform\\s+(float|int|bool|vec2|vec3|vec4|sampler2D)\\s+([A-Za-z_]\\w*)\\s*;\\s*\\/\\/\\s*@control\\b(.*)$/;\nconst ATTRIBUTE = /([a-z]+)(?:=(\"[^\"]*\"|'[^']*'|\\S+))?/gi;\n\nconst COMPONENTS: Record<ControlType, number> = {\n float: 1,\n int: 1,\n bool: 1,\n vec2: 2,\n vec3: 3,\n vec4: 4,\n sampler2D: 1,\n text: 1,\n};\n\n/** The number of components a control type has: 1 for scalars, `bool`, `sampler2D` and `text`, else the vector size. */\nexport function componentCount(type: ControlType): number {\n return COMPONENTS[type];\n}\n\n/** \"uFlowSpeed\" → \"Flow speed\". */\nexport function humanise(name: string): string {\n const words = name\n .replace(/^u(?=[A-Z])/, \"\")\n .replace(/_/g, \" \")\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .trim()\n .toLowerCase();\n return words.charAt(0).toUpperCase() + words.slice(1);\n}\n\n/** \"#77C16A\" (or \"#7c6\") → [0.467, 0.757, 0.416]; null when it is not a hex colour. */\nexport function hexToRgb(hex: string): number[] | null {\n const m = /^#?([0-9a-f]{6}|[0-9a-f]{3})$/i.exec(hex.trim());\n if (!m) return null;\n const digits = m[1]!;\n const h =\n digits.length === 3\n ? digits\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : digits;\n return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16) / 255);\n}\n\n/** [0.467, 0.757, 0.416] → \"#77c16a\". Components are clamped to 0–1; a fourth (alpha) is ignored. */\nexport function rgbToHex(rgb: number[]): string {\n return (\n \"#\" +\n rgb\n .slice(0, 3)\n .map((c) =>\n Math.round(Math.min(1, Math.max(0, c)) * 255)\n .toString(16)\n .padStart(2, \"0\"),\n )\n .join(\"\")\n );\n}\n\nfunction parseDefault(raw: string | undefined, type: ControlType, color: boolean): ControlValue {\n const n = COMPONENTS[type];\n if (type === \"bool\") return raw === \"true\" || raw === \"1\";\n if (type === \"sampler2D\" || type === \"text\") return raw ?? \"\";\n // Without a default, numbers start at 0 and colours at white.\n if (raw === undefined) return n === 1 ? 0 : Array.from({ length: n }, () => (color ? 1 : 0));\n if (color && raw.startsWith(\"#\")) {\n const rgb = hexToRgb(raw) ?? [1, 1, 1];\n return type === \"vec4\" ? [...rgb, 1] : rgb;\n }\n const parts = raw\n .split(\",\")\n .map(Number)\n .map((v) => (Number.isFinite(v) ? v : 0));\n if (n === 1) return parts[0] ?? 0;\n // A short list repeats its last component: `default=0.5` on a vec3 is (0.5, 0.5, 0.5).\n return Array.from({ length: n }, (_, i) => parts[i] ?? parts[parts.length - 1] ?? 0);\n}\n\n/**\n * A control from its declaration's attributes (the text after `@control`):\n * `min`, `max`, `step`, `default`, `label`, `color`, and for images `filter`\n * and `wrap`. Values with spaces are quoted, in double or single quotes.\n * Shared by shader sources and vector effects' `{{name: type …}}` placeholders.\n */\nexport function controlFromDeclaration(name: string, type: ControlType, attributes: string, line: number): ControlDef {\n const attrs: Record<string, string | undefined> = {};\n for (const a of attributes.matchAll(ATTRIBUTE)) {\n attrs[a[1]!.toLowerCase()] = a[2]?.replace(/^([\"'])(.*)\\1$/s, \"$2\");\n }\n const color = \"color\" in attrs && (type === \"vec3\" || type === \"vec4\");\n const num = (v: string | undefined, fallback: number) =>\n v !== undefined && Number.isFinite(Number(v)) ? Number(v) : fallback;\n const min = num(attrs.min, 0);\n const rawMax = num(attrs.max, type === \"int\" ? 10 : 1);\n const max = rawMax > min ? rawMax : min + 1;\n const control: ControlDef = {\n name,\n type,\n label: attrs.label ?? humanise(name),\n color,\n min,\n max,\n step: num(attrs.step, type === \"int\" ? 1 : (max - min) / 200),\n default: parseDefault(attrs.default, type, color),\n line,\n };\n if (type === \"sampler2D\") {\n const pick = <T extends string>(v: string | undefined, options: readonly T[]): T =>\n options.find((o) => o === v?.toLowerCase()) ?? options[0]!;\n control.texture = {\n filter: pick(attrs.filter, [\"mipmap\", \"linear\", \"nearest\"] as const),\n wrap: pick(attrs.wrap, [\"repeat\", \"clamp\", \"mirror\"] as const),\n };\n }\n return control;\n}\n\n/**\n * The controls a source declares, in source order. A uniform declared twice\n * counts once (the first wins). Unknown attributes are ignored, and missing\n * or malformed ones fall back to defaults: `min=0`, `max=1` (10 for `int`),\n * a step of 1/200 of the range (1 for `int`).\n */\nexport function parseControls(source: string): ControlDef[] {\n const controls: ControlDef[] = [];\n const seen = new Set<string>();\n source.split(\"\\n\").forEach((text, index) => {\n const m = DECLARATION.exec(text);\n if (!m || seen.has(m[2]!)) return;\n seen.add(m[2]!);\n controls.push(controlFromDeclaration(m[2]!, m[1] as ControlType, m[3]!, index + 1));\n });\n return controls;\n}\n\n/** The value a control has: the stored one when it still fits the declaration, else the default. */\nexport function controlValue(\n control: ControlDef,\n values: Record<string, ControlValue> | undefined,\n): ControlValue {\n const v = values?.[control.name];\n if (v === undefined) return control.default;\n if (control.type === \"bool\") return typeof v === \"boolean\" ? v : control.default;\n if (control.type === \"sampler2D\" || control.type === \"text\") return typeof v === \"string\" ? v : control.default;\n const n = COMPONENTS[control.type];\n if (n === 1) return typeof v === \"number\" ? v : control.default;\n return Array.isArray(v) && v.length === n ? v : control.default;\n}\n\n/** What {@link checkControlValue} decides: the value to store, or a sentence saying what the control takes. */\nexport type ControlCheck = { ok: true; value: ControlValue } | { ok: false; message: string };\n\n/**\n * Checks a value from outside (an agent's tool call, a URL, a form) against a\n * control. Colours also accept \"#RRGGBB\"; `int` values are rounded. Images\n * take a URL: `https:`, `http:`, `data:image/…`, `blob:` or a relative path\n * (\"\" for none).\n */\nexport function checkControlValue(control: ControlDef, raw: unknown): ControlCheck {\n const fail = (message: string): ControlCheck => ({ ok: false, message });\n const n = COMPONENTS[control.type];\n if (control.type === \"bool\") {\n return typeof raw === \"boolean\"\n ? { ok: true, value: raw }\n : fail(`${control.name} takes true or false.`);\n }\n if (control.type === \"sampler2D\") {\n return typeof raw === \"string\" && (raw === \"\" || isImageUrl(raw))\n ? { ok: true, value: raw }\n : fail(`${control.name} takes an image URL: https://…, data:image/… or blob:….`);\n }\n if (control.type === \"text\") {\n return typeof raw === \"string\" ? { ok: true, value: raw } : fail(`${control.name} takes text.`);\n }\n if (control.color && typeof raw === \"string\") {\n const rgb = hexToRgb(raw);\n if (!rgb) return fail(`${control.name} takes a colour like \"#77C16A\".`);\n return { ok: true, value: control.type === \"vec4\" ? [...rgb, 1] : rgb };\n }\n if (n === 1) {\n if (typeof raw !== \"number\" || !Number.isFinite(raw)) {\n return fail(`${control.name} takes a number.`);\n }\n return { ok: true, value: control.type === \"int\" ? Math.round(raw) : raw };\n }\n if (\n !Array.isArray(raw) ||\n raw.length !== n ||\n !raw.every((x) => typeof x === \"number\" && Number.isFinite(x))\n ) {\n return fail(\n `${control.name} takes ${n} numbers${control.color ? ' (or a colour like \"#77C16A\")' : \"\"}.`,\n );\n }\n return { ok: true, value: raw as number[] };\n}\n\n/**\n * {@link checkControlValue} for controls whose values are never strings:\n * returns the value to store, or the message. An image control's value is a\n * string too, so use `checkControlValue` where images can appear.\n */\nexport function coerceControlValue(control: ControlDef, raw: unknown): ControlValue | string {\n const check = checkControlValue(control, raw);\n return check.ok ? check.value : check.message;\n}\n\n/** Whether a string is a URL an image control can load: `http(s):`, `data:image/`, `blob:` or a relative path. */\nexport function isImageUrl(url: string): boolean {\n const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(url.trim())?.[1]?.toLowerCase();\n if (scheme === undefined) return url.trim() !== \"\";\n if (scheme === \"data\") return /^data:image\\//i.test(url.trim());\n return scheme === \"http\" || scheme === \"https\" || scheme === \"blob\";\n}\n\n/** The image URLs the controls show with these values, without duplicates or empty ones. */\nexport function imageUrls(\n controls: ControlDef[],\n values: Record<string, ControlValue> | undefined,\n): string[] {\n const urls = new Set<string>();\n for (const control of controls) {\n if (control.type !== \"sampler2D\") continue;\n const url = controlValue(control, values) as string;\n if (url) urls.add(url);\n }\n return [...urls];\n}\n\n/**\n * A value as compact, JSON-friendly data: colours as hex, numbers to three\n * decimals. A long `data:` URL is shortened to its type and size, which\n * keeps it out of an agent's context but cannot be stored back.\n */\nexport function describeValue(control: ControlDef, value: ControlValue): unknown {\n if (typeof value === \"string\" && value.startsWith(\"data:\") && value.length > 100) {\n const kb = Math.round((value.length * 3) / 4 / 1024);\n return `${value.slice(0, value.indexOf(\",\") + 1)}… (about ${kb} kB)`;\n }\n if (control.color && Array.isArray(value)) return rgbToHex(value);\n if (Array.isArray(value)) return value.map((v) => Math.round(v * 1000) / 1000);\n if (typeof value === \"number\") return Math.round(value * 1000) / 1000;\n return value;\n}\n"],"mappings":";AAqDA,IAAM,cACJ;AACF,IAAM,YAAY;AAElB,IAAM,aAA0C;AAAA,EAC9C,OAAO;AAAA,EACP,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,WAAW;AAAA,EACX,MAAM;AACR;AAGO,SAAS,eAAe,MAA2B;AACxD,SAAO,WAAW,IAAI;AACxB;AAGO,SAAS,SAAS,MAAsB;AAC7C,QAAM,QAAQ,KACX,QAAQ,eAAe,EAAE,EACzB,QAAQ,MAAM,GAAG,EACjB,QAAQ,sBAAsB,OAAO,EACrC,KAAK,EACL,YAAY;AACf,SAAO,MAAM,OAAO,CAAC,EAAE,YAAY,IAAI,MAAM,MAAM,CAAC;AACtD;AAGO,SAAS,SAAS,KAA8B;AACrD,QAAM,IAAI,iCAAiC,KAAK,IAAI,KAAK,CAAC;AAC1D,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,SAAS,EAAE,CAAC;AAClB,QAAM,IACJ,OAAO,WAAW,IACd,OACG,MAAM,EAAE,EACR,IAAI,CAAC,MAAM,IAAI,CAAC,EAChB,KAAK,EAAE,IACV;AACN,SAAO,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,GAAG;AACnE;AAGO,SAAS,SAAS,KAAuB;AAC9C,SACE,MACA,IACG,MAAM,GAAG,CAAC,EACV;AAAA,IAAI,CAAC,MACJ,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC,IAAI,GAAG,EACzC,SAAS,EAAE,EACX,SAAS,GAAG,GAAG;AAAA,EACpB,EACC,KAAK,EAAE;AAEd;AAEA,SAAS,aAAa,KAAyB,MAAmB,OAA8B;AAC9F,QAAM,IAAI,WAAW,IAAI;AACzB,MAAI,SAAS,OAAQ,QAAO,QAAQ,UAAU,QAAQ;AACtD,MAAI,SAAS,eAAe,SAAS,OAAQ,QAAO,OAAO;AAE3D,MAAI,QAAQ,OAAW,QAAO,MAAM,IAAI,IAAI,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAO,QAAQ,IAAI,CAAE;AAC3F,MAAI,SAAS,IAAI,WAAW,GAAG,GAAG;AAChC,UAAM,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC;AACrC,WAAO,SAAS,SAAS,CAAC,GAAG,KAAK,CAAC,IAAI;AAAA,EACzC;AACA,QAAM,QAAQ,IACX,MAAM,GAAG,EACT,IAAI,MAAM,EACV,IAAI,CAAC,MAAO,OAAO,SAAS,CAAC,IAAI,IAAI,CAAE;AAC1C,MAAI,MAAM,EAAG,QAAO,MAAM,CAAC,KAAK;AAEhC,SAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,MAAM,MAAM,CAAC,KAAK,MAAM,MAAM,SAAS,CAAC,KAAK,CAAC;AACrF;AAQO,SAAS,uBAAuB,MAAc,MAAmB,YAAoB,MAA0B;AACpH,QAAM,QAA4C,CAAC;AACnD,aAAW,KAAK,WAAW,SAAS,SAAS,GAAG;AAC9C,UAAM,EAAE,CAAC,EAAG,YAAY,CAAC,IAAI,EAAE,CAAC,GAAG,QAAQ,mBAAmB,IAAI;AAAA,EACpE;AACA,QAAM,QAAQ,WAAW,UAAU,SAAS,UAAU,SAAS;AAC/D,QAAM,MAAM,CAAC,GAAuB,aAClC,MAAM,UAAa,OAAO,SAAS,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI;AAC9D,QAAM,MAAM,IAAI,MAAM,KAAK,CAAC;AAC5B,QAAM,SAAS,IAAI,MAAM,KAAK,SAAS,QAAQ,KAAK,CAAC;AACrD,QAAM,MAAM,SAAS,MAAM,SAAS,MAAM;AAC1C,QAAM,UAAsB;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,OAAO,MAAM,SAAS,SAAS,IAAI;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,IAAI,MAAM,MAAM,SAAS,QAAQ,KAAK,MAAM,OAAO,GAAG;AAAA,IAC5D,SAAS,aAAa,MAAM,SAAS,MAAM,KAAK;AAAA,IAChD;AAAA,EACF;AACA,MAAI,SAAS,aAAa;AACxB,UAAM,OAAO,CAAmB,GAAuB,YACrD,QAAQ,KAAK,CAAC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,QAAQ,CAAC;AAC1D,YAAQ,UAAU;AAAA,MAChB,QAAQ,KAAK,MAAM,QAAQ,CAAC,UAAU,UAAU,SAAS,CAAU;AAAA,MACnE,MAAM,KAAK,MAAM,MAAM,CAAC,UAAU,SAAS,QAAQ,CAAU;AAAA,IAC/D;AAAA,EACF;AACA,SAAO;AACT;AAQO,SAAS,cAAc,QAA8B;AAC1D,QAAM,WAAyB,CAAC;AAChC,QAAM,OAAO,oBAAI,IAAY;AAC7B,SAAO,MAAM,IAAI,EAAE,QAAQ,CAAC,MAAM,UAAU;AAC1C,UAAM,IAAI,YAAY,KAAK,IAAI;AAC/B,QAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC,CAAE,EAAG;AAC3B,SAAK,IAAI,EAAE,CAAC,CAAE;AACd,aAAS,KAAK,uBAAuB,EAAE,CAAC,GAAI,EAAE,CAAC,GAAkB,EAAE,CAAC,GAAI,QAAQ,CAAC,CAAC;AAAA,EACpF,CAAC;AACD,SAAO;AACT;AAGO,SAAS,aACd,SACA,QACc;AACd,QAAM,IAAI,SAAS,QAAQ,IAAI;AAC/B,MAAI,MAAM,OAAW,QAAO,QAAQ;AACpC,MAAI,QAAQ,SAAS,OAAQ,QAAO,OAAO,MAAM,YAAY,IAAI,QAAQ;AACzE,MAAI,QAAQ,SAAS,eAAe,QAAQ,SAAS,OAAQ,QAAO,OAAO,MAAM,WAAW,IAAI,QAAQ;AACxG,QAAM,IAAI,WAAW,QAAQ,IAAI;AACjC,MAAI,MAAM,EAAG,QAAO,OAAO,MAAM,WAAW,IAAI,QAAQ;AACxD,SAAO,MAAM,QAAQ,CAAC,KAAK,EAAE,WAAW,IAAI,IAAI,QAAQ;AAC1D;AAWO,SAAS,kBAAkB,SAAqB,KAA4B;AACjF,QAAM,OAAO,CAAC,aAAmC,EAAE,IAAI,OAAO,QAAQ;AACtE,QAAM,IAAI,WAAW,QAAQ,IAAI;AACjC,MAAI,QAAQ,SAAS,QAAQ;AAC3B,WAAO,OAAO,QAAQ,YAClB,EAAE,IAAI,MAAM,OAAO,IAAI,IACvB,KAAK,GAAG,QAAQ,IAAI,uBAAuB;AAAA,EACjD;AACA,MAAI,QAAQ,SAAS,aAAa;AAChC,WAAO,OAAO,QAAQ,aAAa,QAAQ,MAAM,WAAW,GAAG,KAC3D,EAAE,IAAI,MAAM,OAAO,IAAI,IACvB,KAAK,GAAG,QAAQ,IAAI,wEAAyD;AAAA,EACnF;AACA,MAAI,QAAQ,SAAS,QAAQ;AAC3B,WAAO,OAAO,QAAQ,WAAW,EAAE,IAAI,MAAM,OAAO,IAAI,IAAI,KAAK,GAAG,QAAQ,IAAI,cAAc;AAAA,EAChG;AACA,MAAI,QAAQ,SAAS,OAAO,QAAQ,UAAU;AAC5C,UAAM,MAAM,SAAS,GAAG;AACxB,QAAI,CAAC,IAAK,QAAO,KAAK,GAAG,QAAQ,IAAI,iCAAiC;AACtE,WAAO,EAAE,IAAI,MAAM,OAAO,QAAQ,SAAS,SAAS,CAAC,GAAG,KAAK,CAAC,IAAI,IAAI;AAAA,EACxE;AACA,MAAI,MAAM,GAAG;AACX,QAAI,OAAO,QAAQ,YAAY,CAAC,OAAO,SAAS,GAAG,GAAG;AACpD,aAAO,KAAK,GAAG,QAAQ,IAAI,kBAAkB;AAAA,IAC/C;AACA,WAAO,EAAE,IAAI,MAAM,OAAO,QAAQ,SAAS,QAAQ,KAAK,MAAM,GAAG,IAAI,IAAI;AAAA,EAC3E;AACA,MACE,CAAC,MAAM,QAAQ,GAAG,KAClB,IAAI,WAAW,KACf,CAAC,IAAI,MAAM,CAAC,MAAM,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,CAAC,GAC7D;AACA,WAAO;AAAA,MACL,GAAG,QAAQ,IAAI,UAAU,CAAC,WAAW,QAAQ,QAAQ,kCAAkC,EAAE;AAAA,IAC3F;AAAA,EACF;AACA,SAAO,EAAE,IAAI,MAAM,OAAO,IAAgB;AAC5C;AAOO,SAAS,mBAAmB,SAAqB,KAAqC;AAC3F,QAAM,QAAQ,kBAAkB,SAAS,GAAG;AAC5C,SAAO,MAAM,KAAK,MAAM,QAAQ,MAAM;AACxC;AAGO,SAAS,WAAW,KAAsB;AAC/C,QAAM,SAAS,yBAAyB,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY;AAC3E,MAAI,WAAW,OAAW,QAAO,IAAI,KAAK,MAAM;AAChD,MAAI,WAAW,OAAQ,QAAO,iBAAiB,KAAK,IAAI,KAAK,CAAC;AAC9D,SAAO,WAAW,UAAU,WAAW,WAAW,WAAW;AAC/D;AAGO,SAAS,UACd,UACA,QACU;AACV,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,SAAS,YAAa;AAClC,UAAM,MAAM,aAAa,SAAS,MAAM;AACxC,QAAI,IAAK,MAAK,IAAI,GAAG;AAAA,EACvB;AACA,SAAO,CAAC,GAAG,IAAI;AACjB;AAOO,SAAS,cAAc,SAAqB,OAA8B;AAC/E,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,OAAO,KAAK,MAAM,SAAS,KAAK;AAChF,UAAM,KAAK,KAAK,MAAO,MAAM,SAAS,IAAK,IAAI,IAAI;AACnD,WAAO,GAAG,MAAM,MAAM,GAAG,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,iBAAY,EAAE;AAAA,EAChE;AACA,MAAI,QAAQ,SAAS,MAAM,QAAQ,KAAK,EAAG,QAAO,SAAS,KAAK;AAChE,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,GAAI,IAAI,GAAI;AAC7E,MAAI,OAAO,UAAU,SAAU,QAAO,KAAK,MAAM,QAAQ,GAAI,IAAI;AACjE,SAAO;AACT;","names":[]}
package/dist/cli.js CHANGED
@@ -1,17 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- simulateScene,
4
3
  standaloneHtml,
5
4
  standaloneSceneHtml,
6
5
  validateScene
7
- } from "./chunk-JWZCPMFG.js";
8
- import "./chunk-HKXOHRCD.js";
6
+ } from "./chunk-D7NLHC7K.js";
7
+ import "./chunk-VA74HBPK.js";
9
8
  import "./chunk-EHDJ3CPJ.js";
9
+ import {
10
+ simulateScene
11
+ } from "./chunk-WAMA45G3.js";
10
12
  import "./chunk-WMZQCUFJ.js";
11
- import "./chunk-36DTNSHU.js";
12
13
  import {
13
14
  parseControls
14
- } from "./chunk-J55UBNAT.js";
15
+ } from "./chunk-WC3VADZC.js";
15
16
 
16
17
  // src/cli.ts
17
18
  import { Command } from "commander";
@@ -91,7 +92,7 @@ program.command("export <targetOrShader> [extraPath] [outputPath]").description(
91
92
  }
92
93
  try {
93
94
  const scene = JSON.parse(fs.readFileSync(fullPath2, "utf-8"));
94
- const { exportMotn, exportLottie, exportFcp7Xml, exportCmx3600Edl } = await import("./export-FEBPEEO7.js");
95
+ const { exportMotn, exportLottie, exportFcp7Xml, exportCmx3600Edl } = await import("./export-DH2HMACO.js");
95
96
  const fps2 = parseInt(options.fps, 10);
96
97
  let content;
97
98
  let ext;
@@ -151,7 +152,7 @@ program.command("export <targetOrShader> [extraPath] [outputPath]").description(
151
152
  const duration = parseFloat(options.duration);
152
153
  console.log(`Rendering ${duration}s (${duration * fps} frames at ${width}x${height}, ${fps}fps) to ${options.out}...`);
153
154
  try {
154
- const { exportMotionGraphic } = await import("./recorder-WJTWMEQN.js");
155
+ const { exportMotionGraphic } = await import("./recorder-KEX3U35D.js");
155
156
  const savedPath = await exportMotionGraphic({
156
157
  source,
157
158
  values,
@@ -244,7 +245,7 @@ sceneCmd.command("motn <sceneFile> [outFile]").description("Export scene to Appl
244
245
  }
245
246
  try {
246
247
  const scene = JSON.parse(fs.readFileSync(fullPath, "utf-8"));
247
- const { exportMotn } = await import("./export-FEBPEEO7.js");
248
+ const { exportMotn } = await import("./export-DH2HMACO.js");
248
249
  const xml = exportMotn(scene, { frameRate: parseInt(options.fps, 10) });
249
250
  const destination = path.resolve(
250
251
  process.cwd(),
@@ -266,7 +267,7 @@ sceneCmd.command("lottie <sceneFile> [outFile]").description("Export scene vecto
266
267
  }
267
268
  try {
268
269
  const scene = JSON.parse(fs.readFileSync(fullPath, "utf-8"));
269
- const { exportLottie } = await import("./export-FEBPEEO7.js");
270
+ const { exportLottie } = await import("./export-DH2HMACO.js");
270
271
  const animation = exportLottie(scene, { frameRate: parseInt(options.fps, 10) });
271
272
  const destination = path.resolve(
272
273
  process.cwd(),
@@ -288,7 +289,7 @@ sceneCmd.command("fcp7 <sceneFile> [outFile]").description("Export scene timelin
288
289
  }
289
290
  try {
290
291
  const scene = JSON.parse(fs.readFileSync(fullPath, "utf-8"));
291
- const { exportFcp7Xml } = await import("./export-FEBPEEO7.js");
292
+ const { exportFcp7Xml } = await import("./export-DH2HMACO.js");
292
293
  const xml = exportFcp7Xml(scene, { frameRate: parseInt(options.fps, 10) });
293
294
  const destination = path.resolve(
294
295
  process.cwd(),
@@ -310,7 +311,7 @@ sceneCmd.command("edl <sceneFile> [outFile]").description("Export scene edit dec
310
311
  }
311
312
  try {
312
313
  const scene = JSON.parse(fs.readFileSync(fullPath, "utf-8"));
313
- const { exportCmx3600Edl } = await import("./export-FEBPEEO7.js");
314
+ const { exportCmx3600Edl } = await import("./export-DH2HMACO.js");
314
315
  const edl = exportCmx3600Edl(scene, {
315
316
  frameRate: parseInt(options.fps, 10),
316
317
  reel: options.reel
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport path from 'path';\nimport fs from 'fs';\nimport { exec } from 'child_process';\nimport { parseControls, standaloneHtml, type ControlValue } from './shader/index.js';\nimport { validateScene, simulateScene, standaloneSceneHtml, type Scene } from './scene/index.js';\n\nconst program = new Command();\n\nprogram\n .name('flair')\n .description('Generative motion graphics, animation directing & bespoke WebGL shader engine')\n .version('2.0.0');\n\n// ==========================================\n// Shader Commands\n// ==========================================\nprogram\n .command('controls <shaderPath>')\n .description('Inspect and parse @control declarations in a WebGL shader file')\n .option('--json', 'Output controls as JSON', false)\n .action((shaderPath, options) => {\n const fullPath = path.resolve(process.cwd(), shaderPath);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Shader file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n const source = fs.readFileSync(fullPath, 'utf-8');\n const controls = parseControls(source);\n\n if (options.json) {\n console.log(JSON.stringify(controls, null, 2));\n return;\n }\n\n if (controls.length === 0) {\n console.log(`No @control annotations found in ${path.basename(shaderPath)}.`);\n return;\n }\n\n console.log(`Controls in ${path.basename(shaderPath)} (${controls.length}):`);\n for (const c of controls) {\n const defVal = Array.isArray(c.default) ? `[${c.default.join(', ')}]` : String(c.default);\n console.log(\n ` • ${c.name} (${c.type}): \"${c.label}\" | default=${defVal} | range=[${c.min}, ${c.max}] (line ${c.line})`\n );\n }\n });\n\nprogram\n .command('html <shaderPath>')\n .description('Generate a self-contained standalone HTML page running the shader')\n .option('-t, --title <title>', 'Page title')\n .option('-o, --out <outputPath>', 'Output HTML path')\n .option('-v, --values <jsonOrFile>', 'Control values as JSON string or path to JSON file')\n .option('--open', 'Open generated HTML page in default browser', false)\n .action((shaderPath, options) => {\n const fullPath = path.resolve(process.cwd(), shaderPath);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Shader file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n const source = fs.readFileSync(fullPath, 'utf-8');\n const title = options.title || path.basename(shaderPath, path.extname(shaderPath));\n\n let values: Record<string, ControlValue> = {};\n if (options.values) {\n try {\n if (fs.existsSync(options.values)) {\n values = JSON.parse(fs.readFileSync(options.values, 'utf-8'));\n } else {\n values = JSON.parse(options.values);\n }\n } catch (err: any) {\n console.error(`Error parsing values: ${err.message}`);\n process.exit(1);\n }\n }\n\n const htmlContent = standaloneHtml(title, source, values);\n const outputPath = path.resolve(\n process.cwd(),\n options.out || `${path.basename(shaderPath, path.extname(shaderPath))}.html`\n );\n\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, htmlContent, 'utf-8');\n console.log(`Generated standalone shader page at: ${outputPath}`);\n\n if (options.open) {\n exec(`open \"${outputPath}\"`);\n }\n });\n\nprogram\n .command('export <targetOrShader> [extraPath] [outputPath]')\n .description('Export shader animation (GIF/MP4) or scene to Apple Motion ozml 4.0 (.motn)')\n .option('-o, --out <outputPath>', 'Output file path')\n .option('-w, --width <width>', 'Canvas width', '720')\n .option('-h, --height <height>', 'Canvas height', '720')\n .option('--fps <fps>', 'Frames per second', '30')\n .option('-d, --duration <duration>', 'Duration in seconds', '3')\n .option('-v, --values <jsonOrFile>', 'Control values as JSON string or file')\n .action(async (targetOrShader, extraPath, outputPath, options) => {\n if (targetOrShader === 'motn' || targetOrShader === 'lottie' || targetOrShader === 'fcp7' || targetOrShader === 'edl') {\n const sceneFile = extraPath;\n if (!sceneFile) {\n console.error(`Error: Please provide a scene JSON file to export: flair export ${targetOrShader} <scene.json> [output]`);\n process.exit(1);\n }\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const { exportMotn, exportLottie, exportFcp7Xml, exportCmx3600Edl } = await import('./export/index.js');\n const fps = parseInt(options.fps, 10);\n\n let content: string;\n let ext: string;\n if (targetOrShader === 'motn') {\n content = exportMotn(scene, { frameRate: fps });\n ext = 'motn';\n } else if (targetOrShader === 'lottie') {\n content = JSON.stringify(exportLottie(scene, { frameRate: fps }), null, 2);\n ext = 'json';\n } else if (targetOrShader === 'fcp7') {\n content = exportFcp7Xml(scene, { frameRate: fps });\n ext = 'xml';\n } else {\n content = exportCmx3600Edl(scene, { frameRate: fps });\n ext = 'edl';\n }\n\n const destination = path.resolve(\n process.cwd(),\n options.out || outputPath || `${scene.name || 'scene'}.${ext}`\n );\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.writeFileSync(destination, content, 'utf-8');\n console.log(`✅ Successfully exported ${targetOrShader.toUpperCase()} to: ${destination}`);\n } catch (err: any) {\n console.error(`Error exporting scene to ${targetOrShader}: ${err.message}`);\n process.exit(1);\n }\n return;\n }\n\n const shaderPath = targetOrShader;\n const fullPath = path.resolve(process.cwd(), shaderPath);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Shader file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n if (!options.out) {\n console.error('Error: Output file path required (-o, --out <outputPath>)');\n process.exit(1);\n }\n\n const source = fs.readFileSync(fullPath, 'utf-8');\n\n let values: Record<string, ControlValue> = {};\n if (options.values) {\n try {\n if (fs.existsSync(options.values)) {\n values = JSON.parse(fs.readFileSync(options.values, 'utf-8'));\n } else {\n values = JSON.parse(options.values);\n }\n } catch (err: any) {\n console.error(`Error parsing values: ${err.message}`);\n process.exit(1);\n }\n }\n\n const width = parseInt(options.width, 10);\n const height = parseInt(options.height, 10);\n const fps = parseInt(options.fps, 10);\n const duration = parseFloat(options.duration);\n\n console.log(`Rendering ${duration}s (${duration * fps} frames at ${width}x${height}, ${fps}fps) to ${options.out}...`);\n\n try {\n const { exportMotionGraphic } = await import('./export/recorder.js');\n const savedPath = await exportMotionGraphic({\n source,\n values,\n outputPath: options.out,\n width,\n height,\n fps,\n duration,\n onProgress: (frame, total) => {\n process.stdout.write(`\\rRendering frame ${frame}/${total} (${Math.round((frame / total) * 100)}%)`);\n },\n });\n console.log(`\\n✅ Successfully exported motion graphic to: ${savedPath}`);\n } catch (err: any) {\n console.error(`\\nExport failed: ${err.message}`);\n process.exit(1);\n }\n });\n\n// ==========================================\n// Scene Directing Commands\n// ==========================================\nconst sceneCmd = program\n .command('scene')\n .description('Direct, validate, simulate, and render multi-shot animated scenes');\n\nsceneCmd\n .command('validate <sceneFile>')\n .description('Validate scene JSON structure, camera framing, and effect IDs')\n .action((sceneFile) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const issues = validateScene(scene);\n if (issues.length === 0) {\n console.log(`✅ Scene \"${scene.name}\" is valid (${scene.shots.length} shots).`);\n } else {\n console.warn(`Found ${issues.length} issue(s) in \"${scene.name}\":`);\n for (const iss of issues) {\n const loc = iss.shot === 0 ? 'Scene' : `Shot ${iss.shot}`;\n console.warn(` • [${loc}] ${iss.message}`);\n }\n process.exit(1);\n }\n } catch (err: any) {\n console.error(`Error reading scene: ${err.message}`);\n process.exit(1);\n }\n });\n\nsceneCmd\n .command('review <sceneFile>')\n .alias('preview')\n .description('Simulate scene off-screen and generate timing breakdown & diagnostic review')\n .action(async (sceneFile) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const { review } = await simulateScene(scene);\n console.log(review.markdown);\n } catch (err: any) {\n console.error(`Error reviewing scene: ${err.message}`);\n process.exit(1);\n }\n });\n\nsceneCmd\n .command('html <sceneFile>')\n .description('Generate standalone interactive web player for the scene')\n .option('-o, --out <outputPath>', 'Output HTML path')\n .option('--open', 'Open generated HTML in browser', false)\n .action((sceneFile, options) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const htmlContent = standaloneSceneHtml(scene);\n const outputPath = path.resolve(\n process.cwd(),\n options.out || `${scene.name || 'scene'}.html`\n );\n\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, htmlContent, 'utf-8');\n console.log(`Generated standalone scene player at: ${outputPath}`);\n\n if (options.open) {\n exec(`open \"${outputPath}\"`);\n }\n } catch (err: any) {\n console.error(`Error generating scene HTML: ${err.message}`);\n process.exit(1);\n }\n });\n\nsceneCmd\n .command('motn <sceneFile> [outFile]')\n .description('Export scene to Apple Motion ozml 4.0 XML (.motn) document')\n .option('--fps <fps>', 'Target frame rate', '30')\n .action(async (sceneFile, outFile, options) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const { exportMotn } = await import('./export/index.js');\n const xml = exportMotn(scene, { frameRate: parseInt(options.fps, 10) });\n const destination = path.resolve(\n process.cwd(),\n outFile || `${scene.name || 'scene'}.motn`\n );\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.writeFileSync(destination, xml, 'utf-8');\n console.log(`✅ Successfully exported Apple Motion scene to: ${destination}`);\n } catch (err: any) {\n console.error(`Error exporting scene to .motn: ${err.message}`);\n process.exit(1);\n }\n });\n\nsceneCmd\n .command('lottie <sceneFile> [outFile]')\n .description('Export scene vector shapes and animations to Lottie JSON')\n .option('--fps <fps>', 'Target frame rate', '30')\n .action(async (sceneFile, outFile, options) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const { exportLottie } = await import('./export/index.js');\n const animation = exportLottie(scene, { frameRate: parseInt(options.fps, 10) });\n const destination = path.resolve(\n process.cwd(),\n outFile || `${scene.name || 'scene'}.json`\n );\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.writeFileSync(destination, JSON.stringify(animation, null, 2), 'utf-8');\n console.log(`✅ Successfully exported Lottie animation to: ${destination}`);\n } catch (err: any) {\n console.error(`Error exporting scene to Lottie: ${err.message}`);\n process.exit(1);\n }\n });\n\nsceneCmd\n .command('fcp7 <sceneFile> [outFile]')\n .description('Export scene timeline and markers to Final Cut Pro 7 XML (xmeml v4)')\n .option('--fps <fps>', 'Target frame rate', '30')\n .action(async (sceneFile, outFile, options) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const { exportFcp7Xml } = await import('./export/index.js');\n const xml = exportFcp7Xml(scene, { frameRate: parseInt(options.fps, 10) });\n const destination = path.resolve(\n process.cwd(),\n outFile || `${scene.name || 'scene'}.xml`\n );\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.writeFileSync(destination, xml, 'utf-8');\n console.log(`✅ Successfully exported FCP7 XML to: ${destination}`);\n } catch (err: any) {\n console.error(`Error exporting scene to FCP7 XML: ${err.message}`);\n process.exit(1);\n }\n });\n\nsceneCmd\n .command('edl <sceneFile> [outFile]')\n .description('Export scene edit decision list to standard CMX 3600 EDL')\n .option('--fps <fps>', 'Target frame rate', '30')\n .option('--reel <reel>', 'Reel name identifier', 'AX')\n .action(async (sceneFile, outFile, options) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const { exportCmx3600Edl } = await import('./export/index.js');\n const edl = exportCmx3600Edl(scene, {\n frameRate: parseInt(options.fps, 10),\n reel: options.reel,\n });\n const destination = path.resolve(\n process.cwd(),\n outFile || `${scene.name || 'scene'}.edl`\n );\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.writeFileSync(destination, edl, 'utf-8');\n console.log(`✅ Successfully exported CMX 3600 EDL to: ${destination}`);\n } catch (err: any) {\n console.error(`Error exporting scene to CMX 3600 EDL: ${err.message}`);\n process.exit(1);\n }\n });\n\n\nprogram.parse(process.argv);\n"],"mappings":"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","names":["fullPath","fps"]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport path from 'path';\nimport fs from 'fs';\nimport { exec } from 'child_process';\nimport { parseControls, standaloneHtml, type ControlValue } from './shader/index.js';\nimport { validateScene, simulateScene, standaloneSceneHtml, type Scene } from './scene/index.js';\n\nconst program = new Command();\n\nprogram\n .name('flair')\n .description('Generative motion graphics, animation directing & bespoke WebGL shader engine')\n .version('2.0.0');\n\n// ==========================================\n// Shader Commands\n// ==========================================\nprogram\n .command('controls <shaderPath>')\n .description('Inspect and parse @control declarations in a WebGL shader file')\n .option('--json', 'Output controls as JSON', false)\n .action((shaderPath, options) => {\n const fullPath = path.resolve(process.cwd(), shaderPath);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Shader file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n const source = fs.readFileSync(fullPath, 'utf-8');\n const controls = parseControls(source);\n\n if (options.json) {\n console.log(JSON.stringify(controls, null, 2));\n return;\n }\n\n if (controls.length === 0) {\n console.log(`No @control annotations found in ${path.basename(shaderPath)}.`);\n return;\n }\n\n console.log(`Controls in ${path.basename(shaderPath)} (${controls.length}):`);\n for (const c of controls) {\n const defVal = Array.isArray(c.default) ? `[${c.default.join(', ')}]` : String(c.default);\n console.log(\n ` • ${c.name} (${c.type}): \"${c.label}\" | default=${defVal} | range=[${c.min}, ${c.max}] (line ${c.line})`\n );\n }\n });\n\nprogram\n .command('html <shaderPath>')\n .description('Generate a self-contained standalone HTML page running the shader')\n .option('-t, --title <title>', 'Page title')\n .option('-o, --out <outputPath>', 'Output HTML path')\n .option('-v, --values <jsonOrFile>', 'Control values as JSON string or path to JSON file')\n .option('--open', 'Open generated HTML page in default browser', false)\n .action((shaderPath, options) => {\n const fullPath = path.resolve(process.cwd(), shaderPath);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Shader file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n const source = fs.readFileSync(fullPath, 'utf-8');\n const title = options.title || path.basename(shaderPath, path.extname(shaderPath));\n\n let values: Record<string, ControlValue> = {};\n if (options.values) {\n try {\n if (fs.existsSync(options.values)) {\n values = JSON.parse(fs.readFileSync(options.values, 'utf-8'));\n } else {\n values = JSON.parse(options.values);\n }\n } catch (err: any) {\n console.error(`Error parsing values: ${err.message}`);\n process.exit(1);\n }\n }\n\n const htmlContent = standaloneHtml(title, source, values);\n const outputPath = path.resolve(\n process.cwd(),\n options.out || `${path.basename(shaderPath, path.extname(shaderPath))}.html`\n );\n\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, htmlContent, 'utf-8');\n console.log(`Generated standalone shader page at: ${outputPath}`);\n\n if (options.open) {\n exec(`open \"${outputPath}\"`);\n }\n });\n\nprogram\n .command('export <targetOrShader> [extraPath] [outputPath]')\n .description('Export shader animation (GIF/MP4) or scene to Apple Motion ozml 4.0 (.motn)')\n .option('-o, --out <outputPath>', 'Output file path')\n .option('-w, --width <width>', 'Canvas width', '720')\n .option('-h, --height <height>', 'Canvas height', '720')\n .option('--fps <fps>', 'Frames per second', '30')\n .option('-d, --duration <duration>', 'Duration in seconds', '3')\n .option('-v, --values <jsonOrFile>', 'Control values as JSON string or file')\n .action(async (targetOrShader, extraPath, outputPath, options) => {\n if (targetOrShader === 'motn' || targetOrShader === 'lottie' || targetOrShader === 'fcp7' || targetOrShader === 'edl') {\n const sceneFile = extraPath;\n if (!sceneFile) {\n console.error(`Error: Please provide a scene JSON file to export: flair export ${targetOrShader} <scene.json> [output]`);\n process.exit(1);\n }\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const { exportMotn, exportLottie, exportFcp7Xml, exportCmx3600Edl } = await import('./export/index.js');\n const fps = parseInt(options.fps, 10);\n\n let content: string;\n let ext: string;\n if (targetOrShader === 'motn') {\n content = exportMotn(scene, { frameRate: fps });\n ext = 'motn';\n } else if (targetOrShader === 'lottie') {\n content = JSON.stringify(exportLottie(scene, { frameRate: fps }), null, 2);\n ext = 'json';\n } else if (targetOrShader === 'fcp7') {\n content = exportFcp7Xml(scene, { frameRate: fps });\n ext = 'xml';\n } else {\n content = exportCmx3600Edl(scene, { frameRate: fps });\n ext = 'edl';\n }\n\n const destination = path.resolve(\n process.cwd(),\n options.out || outputPath || `${scene.name || 'scene'}.${ext}`\n );\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.writeFileSync(destination, content, 'utf-8');\n console.log(`✅ Successfully exported ${targetOrShader.toUpperCase()} to: ${destination}`);\n } catch (err: any) {\n console.error(`Error exporting scene to ${targetOrShader}: ${err.message}`);\n process.exit(1);\n }\n return;\n }\n\n const shaderPath = targetOrShader;\n const fullPath = path.resolve(process.cwd(), shaderPath);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Shader file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n if (!options.out) {\n console.error('Error: Output file path required (-o, --out <outputPath>)');\n process.exit(1);\n }\n\n const source = fs.readFileSync(fullPath, 'utf-8');\n\n let values: Record<string, ControlValue> = {};\n if (options.values) {\n try {\n if (fs.existsSync(options.values)) {\n values = JSON.parse(fs.readFileSync(options.values, 'utf-8'));\n } else {\n values = JSON.parse(options.values);\n }\n } catch (err: any) {\n console.error(`Error parsing values: ${err.message}`);\n process.exit(1);\n }\n }\n\n const width = parseInt(options.width, 10);\n const height = parseInt(options.height, 10);\n const fps = parseInt(options.fps, 10);\n const duration = parseFloat(options.duration);\n\n console.log(`Rendering ${duration}s (${duration * fps} frames at ${width}x${height}, ${fps}fps) to ${options.out}...`);\n\n try {\n const { exportMotionGraphic } = await import('./export/recorder.js');\n const savedPath = await exportMotionGraphic({\n source,\n values,\n outputPath: options.out,\n width,\n height,\n fps,\n duration,\n onProgress: (frame, total) => {\n process.stdout.write(`\\rRendering frame ${frame}/${total} (${Math.round((frame / total) * 100)}%)`);\n },\n });\n console.log(`\\n✅ Successfully exported motion graphic to: ${savedPath}`);\n } catch (err: any) {\n console.error(`\\nExport failed: ${err.message}`);\n process.exit(1);\n }\n });\n\n// ==========================================\n// Scene Directing Commands\n// ==========================================\nconst sceneCmd = program\n .command('scene')\n .description('Direct, validate, simulate, and render multi-shot animated scenes');\n\nsceneCmd\n .command('validate <sceneFile>')\n .description('Validate scene JSON structure, camera framing, and effect IDs')\n .action((sceneFile) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const issues = validateScene(scene);\n if (issues.length === 0) {\n console.log(`✅ Scene \"${scene.name}\" is valid (${scene.shots.length} shots).`);\n } else {\n console.warn(`Found ${issues.length} issue(s) in \"${scene.name}\":`);\n for (const iss of issues) {\n const loc = iss.shot === 0 ? 'Scene' : `Shot ${iss.shot}`;\n console.warn(` • [${loc}] ${iss.message}`);\n }\n process.exit(1);\n }\n } catch (err: any) {\n console.error(`Error reading scene: ${err.message}`);\n process.exit(1);\n }\n });\n\nsceneCmd\n .command('review <sceneFile>')\n .alias('preview')\n .description('Simulate scene off-screen and generate timing breakdown & diagnostic review')\n .action(async (sceneFile) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const { review } = await simulateScene(scene);\n console.log(review.markdown);\n } catch (err: any) {\n console.error(`Error reviewing scene: ${err.message}`);\n process.exit(1);\n }\n });\n\nsceneCmd\n .command('html <sceneFile>')\n .description('Generate standalone interactive web player for the scene')\n .option('-o, --out <outputPath>', 'Output HTML path')\n .option('--open', 'Open generated HTML in browser', false)\n .action((sceneFile, options) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const htmlContent = standaloneSceneHtml(scene);\n const outputPath = path.resolve(\n process.cwd(),\n options.out || `${scene.name || 'scene'}.html`\n );\n\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, htmlContent, 'utf-8');\n console.log(`Generated standalone scene player at: ${outputPath}`);\n\n if (options.open) {\n exec(`open \"${outputPath}\"`);\n }\n } catch (err: any) {\n console.error(`Error generating scene HTML: ${err.message}`);\n process.exit(1);\n }\n });\n\nsceneCmd\n .command('motn <sceneFile> [outFile]')\n .description('Export scene to Apple Motion ozml 4.0 XML (.motn) document')\n .option('--fps <fps>', 'Target frame rate', '30')\n .action(async (sceneFile, outFile, options) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const { exportMotn } = await import('./export/index.js');\n const xml = exportMotn(scene, { frameRate: parseInt(options.fps, 10) });\n const destination = path.resolve(\n process.cwd(),\n outFile || `${scene.name || 'scene'}.motn`\n );\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.writeFileSync(destination, xml, 'utf-8');\n console.log(`✅ Successfully exported Apple Motion scene to: ${destination}`);\n } catch (err: any) {\n console.error(`Error exporting scene to .motn: ${err.message}`);\n process.exit(1);\n }\n });\n\nsceneCmd\n .command('lottie <sceneFile> [outFile]')\n .description('Export scene vector shapes and animations to Lottie JSON')\n .option('--fps <fps>', 'Target frame rate', '30')\n .action(async (sceneFile, outFile, options) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const { exportLottie } = await import('./export/index.js');\n const animation = exportLottie(scene, { frameRate: parseInt(options.fps, 10) });\n const destination = path.resolve(\n process.cwd(),\n outFile || `${scene.name || 'scene'}.json`\n );\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.writeFileSync(destination, JSON.stringify(animation, null, 2), 'utf-8');\n console.log(`✅ Successfully exported Lottie animation to: ${destination}`);\n } catch (err: any) {\n console.error(`Error exporting scene to Lottie: ${err.message}`);\n process.exit(1);\n }\n });\n\nsceneCmd\n .command('fcp7 <sceneFile> [outFile]')\n .description('Export scene timeline and markers to Final Cut Pro 7 XML (xmeml v4)')\n .option('--fps <fps>', 'Target frame rate', '30')\n .action(async (sceneFile, outFile, options) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const { exportFcp7Xml } = await import('./export/index.js');\n const xml = exportFcp7Xml(scene, { frameRate: parseInt(options.fps, 10) });\n const destination = path.resolve(\n process.cwd(),\n outFile || `${scene.name || 'scene'}.xml`\n );\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.writeFileSync(destination, xml, 'utf-8');\n console.log(`✅ Successfully exported FCP7 XML to: ${destination}`);\n } catch (err: any) {\n console.error(`Error exporting scene to FCP7 XML: ${err.message}`);\n process.exit(1);\n }\n });\n\nsceneCmd\n .command('edl <sceneFile> [outFile]')\n .description('Export scene edit decision list to standard CMX 3600 EDL')\n .option('--fps <fps>', 'Target frame rate', '30')\n .option('--reel <reel>', 'Reel name identifier', 'AX')\n .action(async (sceneFile, outFile, options) => {\n const fullPath = path.resolve(process.cwd(), sceneFile);\n if (!fs.existsSync(fullPath)) {\n console.error(`Error: Scene file not found at: ${fullPath}`);\n process.exit(1);\n }\n\n try {\n const scene: Scene = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));\n const { exportCmx3600Edl } = await import('./export/index.js');\n const edl = exportCmx3600Edl(scene, {\n frameRate: parseInt(options.fps, 10),\n reel: options.reel,\n });\n const destination = path.resolve(\n process.cwd(),\n outFile || `${scene.name || 'scene'}.edl`\n );\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.writeFileSync(destination, edl, 'utf-8');\n console.log(`✅ Successfully exported CMX 3600 EDL to: ${destination}`);\n } catch (err: any) {\n console.error(`Error exporting scene to CMX 3600 EDL: ${err.message}`);\n process.exit(1);\n }\n });\n\n\nprogram.parse(process.argv);\n"],"mappings":"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","names":["fullPath","fps"]}
@@ -13,10 +13,11 @@
13
13
  * their defaults; values someone picks are stored next to it.
14
14
  */
15
15
  /** The uniform types a control can have. */
16
- type ControlType = "float" | "int" | "bool" | "vec2" | "vec3" | "vec4" | "sampler2D";
16
+ type ControlType = "float" | "int" | "bool" | "vec2" | "vec3" | "vec4" | "sampler2D" | "text";
17
17
  /**
18
18
  * A control's value: a number (`float`, `int`), a boolean (`bool`), the
19
- * components of a vector, or an image URL (`sampler2D`; "" for none).
19
+ * components of a vector, an image URL (`sampler2D`; "" for none), or a
20
+ * string (`text`, vector effects only: shaders cannot take text).
20
21
  */
21
22
  type ControlValue = number | boolean | number[] | string;
22
23
  /** How an image is sampled: Shadertoy's channel settings. */
@@ -45,7 +46,7 @@ interface ControlDef {
45
46
  /** Sampling settings, for `sampler2D` controls only. */
46
47
  texture?: TextureSettings;
47
48
  }
48
- /** The number of components a control type has: 1 for scalars, `bool` and `sampler2D`, else the vector size. */
49
+ /** The number of components a control type has: 1 for scalars, `bool`, `sampler2D` and `text`, else the vector size. */
49
50
  declare function componentCount(type: ControlType): number;
50
51
  /** "uFlowSpeed" → "Flow speed". */
51
52
  declare function humanise(name: string): string;