@idle-screens/schema 0.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/validate.ts","../src/simulate.ts","../src/compile.ts","../src/examples.ts"],"sourcesContent":["/**\n * Declarative, agent-authorable saver format. A `SaverSpec` describes a saver as DATA\n * (no code), which `compileSaver` turns into a seeded, deterministic, flash-safe\n * SaverPlugin. It models the \"drifting sprite field\" family that covers most classic\n * savers (fish, toasters, DVD, rain, messages): a static background plus one or more\n * layers of entities that move.\n *\n * SAFETY INVARIANT: there is deliberately NO full-field strobe/flash primitive — the\n * background is static and entities are bounded sprites — so a compiled spec cannot\n * produce photosensitive flashing by construction (proven by sampling a compiled spec\n * through @idle-screens/validator).\n */\nexport const SCHEMA_VERSION = 1 as const;\n\nexport interface SaverSpec {\n schemaVersion: 1;\n id: string;\n label: string;\n /** Deterministic seed; falls back to the host's SaverContext.seed. */\n seed?: number;\n background?: BackgroundSpec;\n layers: LayerSpec[];\n motionIntensity?: 'calm' | 'moderate' | 'energetic';\n}\n\nexport type BackgroundSpec =\n | { type: 'solid'; color: string }\n | { type: 'gradient'; stops: GradientStop[]; band?: BandSpec };\n\n/** A vertical gradient stop (`at` 0 = top, 1 = bottom). */\nexport interface GradientStop {\n at: number;\n color: string;\n}\n\n/** An optional solid band at the bottom (e.g. an aquarium seafloor). */\nexport interface BandSpec {\n color: string;\n /** Band height in px. */\n height: number;\n}\n\nexport interface LayerSpec {\n count: number;\n sprite: SpriteSpec;\n /** Sprite size range in px (font size for emoji/text; ignored for circle). */\n size?: [number, number];\n motion: MotionSpec;\n /** Wrap around the opposite edge when leaving the viewport. Default true. */\n wrap?: boolean;\n /** Flip the sprite horizontally to face its heading. Default false. */\n flip?: boolean;\n}\n\nexport type SpriteSpec =\n | { kind: 'emoji'; glyphs: string[] }\n | { kind: 'text'; strings: string[]; color?: string; font?: string }\n | { kind: 'circle'; radius: [number, number]; color: string };\n\nexport type MotionSpec =\n /**\n * Drift at a per-entity speed picked from `speed` (px/sec) along `angle` degrees\n * (0 = right, 90 = down; default 0). `bidirectional` randomly flips horizontal\n * direction per entity (e.g. fish swimming both ways); `bob` adds a small vertical\n * wobble amplitude (px). Covers horizontal fields, diagonals (toasters) and rain\n * (angle 90).\n */\n | { type: 'drift'; speed: [number, number]; angle?: number; bidirectional?: boolean; bob?: number }\n /** Rise upward (px/sec) with an optional horizontal sway amplitude (px) — bubbles. */\n | { type: 'rise'; speed: [number, number]; sway?: number }\n /** Bounce diagonally at a per-entity speed, reflecting off the edges (px/sec). */\n | { type: 'bounce'; speed: [number, number] };\n\n/** A validation problem, pointing at a JSON path within the spec. */\nexport interface SpecError {\n path: string;\n message: string;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: SpecError[];\n}\n\n/** Perf/safety caps enforced by `validateSpec`. */\nexport const LIMITS = {\n maxPerLayer: 400,\n maxTotal: 800,\n maxLayers: 8,\n maxSpeed: 4000, // px/sec — bounds motion so nothing teleports\n} as const;\n","import { LIMITS, SCHEMA_VERSION, type SaverSpec, type SpecError, type ValidationResult } from './types';\n\nconst HEX = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;\nconst isObj = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);\nconst isStr = (v: unknown): v is string => typeof v === 'string';\nconst isNum = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v);\nconst isRange = (v: unknown): v is [number, number] =>\n Array.isArray(v) && v.length === 2 && isNum(v[0]) && isNum(v[1]) && v[0] <= v[1];\n\n/**\n * Validate an untrusted (agent- or JSON-authored) spec structurally + semantically.\n * Returns typed errors; never throws. `compileSaver` refuses to run an invalid spec.\n */\nexport function validateSpec(spec: unknown): ValidationResult {\n const errors: SpecError[] = [];\n const err = (path: string, message: string): void => void errors.push({ path, message });\n\n if (!isObj(spec)) return { valid: false, errors: [{ path: '', message: 'spec must be an object' }] };\n\n if (spec.schemaVersion !== SCHEMA_VERSION) err('schemaVersion', `must be ${SCHEMA_VERSION}`);\n if (!isStr(spec.id) || spec.id.trim() === '') err('id', 'must be a non-empty string');\n if (!isStr(spec.label) || spec.label.trim() === '') err('label', 'must be a non-empty string');\n if (spec.seed !== undefined && !isNum(spec.seed)) err('seed', 'must be a number');\n if (spec.motionIntensity !== undefined && !['calm', 'moderate', 'energetic'].includes(spec.motionIntensity as string)) {\n err('motionIntensity', 'must be calm | moderate | energetic');\n }\n\n if (spec.background !== undefined) validateBackground(spec.background, err);\n\n if (!Array.isArray(spec.layers) || spec.layers.length === 0) {\n err('layers', 'must be a non-empty array');\n } else {\n if (spec.layers.length > LIMITS.maxLayers) err('layers', `at most ${LIMITS.maxLayers} layers`);\n let total = 0;\n spec.layers.forEach((layer, i) => {\n total += isObj(layer) && isNum(layer.count) ? layer.count : 0;\n validateLayer(layer, `layers[${i}]`, err);\n });\n if (total > LIMITS.maxTotal) err('layers', `total entities ${total} exceeds cap ${LIMITS.maxTotal}`);\n }\n\n return { valid: errors.length === 0, errors };\n}\n\nfunction color(v: unknown, path: string, err: (p: string, m: string) => void): void {\n if (!isStr(v) || !HEX.test(v)) err(path, 'must be a hex colour like #0a3a52');\n}\n\nfunction validateBackground(bg: unknown, err: (p: string, m: string) => void): void {\n if (!isObj(bg)) return err('background', 'must be an object');\n if (bg.type === 'solid') {\n color(bg.color, 'background.color', err);\n } else if (bg.type === 'gradient') {\n if (!Array.isArray(bg.stops) || bg.stops.length < 2) {\n err('background.stops', 'gradient needs at least 2 stops');\n } else {\n bg.stops.forEach((s, i) => {\n if (!isObj(s) || !isNum(s.at) || s.at < 0 || s.at > 1) err(`background.stops[${i}].at`, 'must be 0..1');\n color(isObj(s) ? s.color : undefined, `background.stops[${i}].color`, err);\n });\n }\n if (bg.band !== undefined) {\n if (!isObj(bg.band)) err('background.band', 'must be an object');\n else {\n color(bg.band.color, 'background.band.color', err);\n if (!isNum(bg.band.height) || bg.band.height <= 0) err('background.band.height', 'must be > 0');\n }\n }\n } else {\n err('background.type', 'must be solid | gradient');\n }\n}\n\nfunction validateLayer(layer: unknown, path: string, err: (p: string, m: string) => void): void {\n if (!isObj(layer)) return err(path, 'must be an object');\n if (!isNum(layer.count) || !Number.isInteger(layer.count) || layer.count < 1) {\n err(`${path}.count`, 'must be a positive integer');\n } else if (layer.count > LIMITS.maxPerLayer) {\n err(`${path}.count`, `at most ${LIMITS.maxPerLayer} per layer`);\n }\n if (layer.size !== undefined && (!isRange(layer.size) || layer.size[0] <= 0)) {\n err(`${path}.size`, 'must be a [min,max] range of positive px');\n }\n if (layer.wrap !== undefined && typeof layer.wrap !== 'boolean') err(`${path}.wrap`, 'must be a boolean');\n if (layer.flip !== undefined && typeof layer.flip !== 'boolean') err(`${path}.flip`, 'must be a boolean');\n\n validateSprite(layer.sprite, `${path}.sprite`, err);\n validateMotion(layer.motion, `${path}.motion`, err);\n}\n\nfunction validateSprite(sprite: unknown, path: string, err: (p: string, m: string) => void): void {\n if (!isObj(sprite)) return err(path, 'must be an object');\n if (sprite.kind === 'emoji') {\n if (!Array.isArray(sprite.glyphs) || sprite.glyphs.length === 0 || !sprite.glyphs.every(isStr)) {\n err(`${path}.glyphs`, 'must be a non-empty array of strings');\n }\n } else if (sprite.kind === 'text') {\n if (!Array.isArray(sprite.strings) || sprite.strings.length === 0 || !sprite.strings.every(isStr)) {\n err(`${path}.strings`, 'must be a non-empty array of strings');\n }\n if (sprite.color !== undefined) color(sprite.color, `${path}.color`, err);\n } else if (sprite.kind === 'circle') {\n if (!isRange(sprite.radius) || sprite.radius[0] <= 0) err(`${path}.radius`, 'must be a [min,max] range of positive px');\n color(sprite.color, `${path}.color`, err);\n } else {\n err(`${path}.kind`, 'must be emoji | text | circle');\n }\n}\n\nfunction validateMotion(motion: unknown, path: string, err: (p: string, m: string) => void): void {\n if (!isObj(motion)) return err(path, 'must be an object');\n const speedOk = (v: unknown, p: string): void => {\n if (!isRange(v)) err(p, 'must be a [min,max] range');\n else if (v[1] > LIMITS.maxSpeed) err(p, `speed exceeds cap ${LIMITS.maxSpeed} px/sec`);\n };\n if (motion.type === 'drift') {\n speedOk(motion.speed, `${path}.speed`);\n if (motion.angle !== undefined && !isNum(motion.angle)) err(`${path}.angle`, 'must be a number (degrees)');\n if (motion.bidirectional !== undefined && typeof motion.bidirectional !== 'boolean') err(`${path}.bidirectional`, 'must be a boolean');\n if (motion.bob !== undefined && !isNum(motion.bob)) err(`${path}.bob`, 'must be a number');\n } else if (motion.type === 'rise') {\n speedOk(motion.speed, `${path}.speed`);\n if (motion.sway !== undefined && !isNum(motion.sway)) err(`${path}.sway`, 'must be a number');\n } else if (motion.type === 'bounce') {\n speedOk(motion.speed, `${path}.speed`);\n } else {\n err(`${path}.type`, 'must be drift | rise | bounce');\n }\n}\n\n/** Narrowing helper: validate + cast. Throws with a joined message when invalid. */\nexport function assertValidSpec(spec: unknown): SaverSpec {\n const r = validateSpec(spec);\n if (!r.valid) {\n throw new Error(`invalid saver spec:\\n${r.errors.map((e) => ` ${e.path || '<root>'}: ${e.message}`).join('\\n')}`);\n }\n return spec as SaverSpec;\n}\n","import type { Rng } from '@idle-screens/core';\nimport type { LayerSpec, SpriteSpec } from './types';\n\n/** A seeded entity: initial state + resolved velocity. `positionAt` is a pure function\n * of this + time, so the rAF loop and renderFrame(t) produce identical results. */\nexport interface Entity {\n x0: number;\n y0: number;\n size: number;\n spriteIndex: number;\n phase: number;\n vx: number; // px/sec\n vy: number; // px/sec\n bob: number; // vertical bob / horizontal sway amplitude (px)\n motion: 'drift' | 'rise' | 'bounce';\n headingLeft: boolean;\n}\n\nexport interface Placed {\n x: number;\n y: number;\n flip: boolean;\n}\n\nexport function spriteVariants(sprite: SpriteSpec): number {\n if (sprite.kind === 'emoji') return sprite.glyphs.length;\n if (sprite.kind === 'text') return sprite.strings.length;\n return 1;\n}\n\n/** Deterministically place a layer's entities using the seeded RNG (never Math.random). */\nexport function buildEntities(layer: LayerSpec, rng: Rng, w: number, h: number): Entity[] {\n const [smin, smax] = layer.size ?? [20, 40];\n const variants = spriteVariants(layer.sprite);\n const out: Entity[] = [];\n for (let i = 0; i < layer.count; i++) {\n const size =\n layer.sprite.kind === 'circle'\n ? rng.range(layer.sprite.radius[0], layer.sprite.radius[1]) * 2\n : rng.range(smin, smax);\n\n let vx = 0;\n let vy = 0;\n let bob = 0;\n let motion: Entity['motion'] = 'drift';\n const m = layer.motion;\n if (m.type === 'drift') {\n const s = rng.range(m.speed[0], m.speed[1]);\n let angle = ((m.angle ?? 0) * Math.PI) / 180;\n if (m.bidirectional && rng.next() < 0.5) angle = Math.PI - angle;\n vx = s * Math.cos(angle);\n vy = s * Math.sin(angle);\n bob = m.bob ?? 0;\n } else if (m.type === 'rise') {\n motion = 'rise';\n vy = -rng.range(m.speed[0], m.speed[1]); // upward\n bob = m.sway ?? 0;\n } else {\n motion = 'bounce';\n const s = rng.range(m.speed[0], m.speed[1]);\n const a = rng.range(0, Math.PI * 2);\n vx = s * Math.cos(a);\n vy = s * Math.sin(a);\n }\n\n out.push({\n x0: rng.range(0, w),\n y0: rng.range(0, h),\n size,\n spriteIndex: variants > 1 ? rng.int(0, variants - 1) : 0,\n phase: rng.range(0, Math.PI * 2),\n vx,\n vy,\n bob,\n motion,\n headingLeft: vx < 0,\n });\n }\n return out;\n}\n\nfunction wrap(v: number, min: number, max: number): number {\n const range = max - min;\n return ((((v - min) % range) + range) % range) + min;\n}\n\nfunction reflect(v: number, min: number, max: number): number {\n const range = max - min;\n if (range <= 0) return min;\n const period = 2 * range;\n const p = (((v - min) % period) + period) % period;\n return min + (p < range ? p : period - p);\n}\n\n/** Analytic position of an entity at logical time `t` (ms). Pure & deterministic. */\nexport function positionAt(e: Entity, t: number, w: number, h: number): Placed {\n const dt = t / 1000;\n const m = e.size;\n if (e.motion === 'bounce') {\n return {\n x: reflect(e.x0 + e.vx * dt, m / 2, w - m / 2),\n y: reflect(e.y0 + e.vy * dt, m / 2, h - m / 2),\n flip: false,\n };\n }\n if (e.motion === 'rise') {\n return {\n x: e.x0 + (e.bob ? e.bob * Math.sin(t / 700 + e.phase) : 0),\n y: wrap(e.y0 + e.vy * dt, -m, h + m),\n flip: false,\n };\n }\n // drift\n const x = wrap(e.x0 + e.vx * dt, -m, w + m);\n let y = e.vy !== 0 ? wrap(e.y0 + e.vy * dt, -m, h + m) : e.y0;\n if (e.bob) y += e.bob * Math.sin(t / 500 + e.phase);\n return { x, y, flip: e.headingLeft };\n}\n","import {\n createRng,\n type SaverContext,\n type SaverInstance,\n type SaverManifest,\n type SaverPlugin,\n} from '@idle-screens/core';\nimport { assertValidSpec } from './validate';\nimport { buildEntities, positionAt, type Entity } from './simulate';\nimport type { LayerSpec, SaverSpec } from './types';\n\n/** Derive a manifest so a compiled spec composes with @idle-screens/capabilities. */\nexport function manifestFor(spec: SaverSpec): SaverManifest {\n const total = spec.layers.reduce((n, l) => n + l.count, 0);\n const costTier = total < 30 ? 'idle' : total < 150 ? 'low' : total < 400 ? 'medium' : 'high';\n return {\n id: spec.id,\n label: spec.label,\n passthrough: false,\n minBackend: 'canvas2d',\n costTier,\n motionIntensity: spec.motionIntensity ?? 'moderate',\n reducedMotionFallback: 'static',\n // Flash-safe by construction: static background + bounded sprites, no strobe\n // primitive. Proven by sampling a compiled spec through @idle-screens/validator.\n a11y: { flashSafe: true },\n };\n}\n\ninterface Built {\n layer: LayerSpec;\n entities: Entity[];\n}\n\nclass SpecInstance implements SaverInstance {\n private readonly canvas: HTMLCanvasElement;\n private readonly ctx: CanvasRenderingContext2D;\n private readonly seed: number;\n private w: number;\n private h: number;\n private layers: Built[] = [];\n private frameId: number | null = null;\n private paused = false;\n private startT = 0;\n\n constructor(\n private readonly spec: SaverSpec,\n ctx: SaverContext,\n ) {\n this.seed = ((spec.seed ?? ctx.seed) >>> 0) || 1;\n const canvas = document.createElement('canvas');\n canvas.style.cssText = 'display:block;width:100%;height:100%';\n canvas.setAttribute('aria-hidden', 'true');\n ctx.host.appendChild(canvas);\n this.canvas = canvas;\n const c2d = canvas.getContext('2d', { alpha: false });\n if (!c2d) throw new Error('schema saver: no 2d context');\n this.ctx = c2d;\n\n this.w = ctx.width;\n this.h = ctx.height;\n this.sizeCanvas();\n this.rebuild();\n\n this.paused = ctx.reducedMotion;\n if (this.paused) this.renderFrame(0, this.seed);\n else this.start();\n }\n\n private sizeCanvas(): void {\n const dpr = Math.min(typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1, 2);\n this.canvas.width = Math.max(1, Math.round(this.w * dpr));\n this.canvas.height = Math.max(1, Math.round(this.h * dpr));\n this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n }\n\n /** (Re)seed and place all entities — deterministic for the seed + size. */\n private rebuild(): void {\n const rng = createRng(this.seed);\n this.layers = this.spec.layers.map((layer) => ({ layer, entities: buildEntities(layer, rng, this.w, this.h) }));\n }\n\n private start(): void {\n if (this.frameId !== null || typeof requestAnimationFrame === 'undefined') return;\n this.startT = 0;\n this.frameId = requestAnimationFrame((now) => this.loop(now));\n }\n\n private stop(): void {\n if (this.frameId !== null) {\n cancelAnimationFrame(this.frameId);\n this.frameId = null;\n }\n }\n\n private loop(now: number): void {\n this.frameId = requestAnimationFrame((n) => this.loop(n));\n if (this.startT === 0) this.startT = now;\n this.renderFrame(now - this.startT, this.seed);\n }\n\n private drawBackground(): void {\n const { ctx, w, h } = this;\n const bg = this.spec.background;\n if (!bg || bg.type === 'solid') {\n ctx.fillStyle = bg?.color ?? '#05050a';\n ctx.fillRect(0, 0, w, h);\n return;\n }\n const g = ctx.createLinearGradient(0, 0, 0, h);\n for (const s of bg.stops) g.addColorStop(Math.max(0, Math.min(1, s.at)), s.color);\n ctx.fillStyle = g;\n ctx.fillRect(0, 0, w, h);\n if (bg.band) {\n ctx.fillStyle = bg.band.color;\n ctx.fillRect(0, h - bg.band.height, w, bg.band.height);\n }\n }\n\n private drawEntity(built: Built, e: Entity, t: number): void {\n const { ctx } = this;\n const p = positionAt(e, t, this.w, this.h);\n const sprite = built.layer.sprite;\n if (sprite.kind === 'circle') {\n ctx.fillStyle = sprite.color;\n ctx.beginPath();\n ctx.arc(p.x, p.y, e.size / 2, 0, Math.PI * 2);\n ctx.fill();\n return;\n }\n ctx.save();\n ctx.translate(p.x, p.y);\n if (p.flip && built.layer.flip) ctx.scale(-1, 1);\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n if (sprite.kind === 'emoji') {\n ctx.font = `${e.size}px serif`;\n ctx.fillText(sprite.glyphs[e.spriteIndex] ?? sprite.glyphs[0]!, 0, 0);\n } else {\n ctx.font = sprite.font ?? `${e.size}px system-ui, sans-serif`;\n ctx.fillStyle = sprite.color ?? '#e6e8ef';\n ctx.fillText(sprite.strings[e.spriteIndex] ?? sprite.strings[0]!, 0, 0);\n }\n ctx.restore();\n }\n\n /** Deterministic, frame-addressable render (shared by the rAF loop). */\n renderFrame(t: number, _seed: number): void {\n this.drawBackground();\n for (const built of this.layers) {\n for (const e of built.entities) this.drawEntity(built, e, t);\n }\n }\n\n setPaused(paused: boolean): void {\n this.paused = paused;\n if (paused) {\n this.stop();\n } else {\n this.start();\n }\n }\n\n resize(width: number, height: number): void {\n this.w = width;\n this.h = height;\n this.sizeCanvas();\n this.rebuild();\n if (this.paused) this.renderFrame(0, this.seed);\n }\n\n dispose(): void {\n this.stop();\n this.canvas.remove();\n }\n}\n\n/**\n * Compile a declarative spec into a runnable SaverPlugin. Throws if the spec is invalid\n * (so an agent-authored spec is validated before it can run). The result is seeded,\n * deterministic, `renderFrame(t,seed)`-addressable, and flash-safe by construction.\n */\nexport function compileSaver(spec: unknown): SaverPlugin {\n const valid = assertValidSpec(spec);\n return {\n manifest: manifestFor(valid),\n mount: (ctx: SaverContext) => new SpecInstance(valid, ctx),\n };\n}\n","import type { SaverSpec } from './types';\n\n/**\n * Reproduces the hand-coded Fish Aquarium saver as pure DATA — the discriminating test\n * that the schema is actually expressive: two layers (fish drifting both ways at varied\n * speeds with a bob + flip, over a gradient with a seafloor band; bubbles rising with sway).\n */\nexport const AQUARIUM_SPEC: SaverSpec = {\n schemaVersion: 1,\n id: 'aquarium',\n label: 'Aquarium (declarative)',\n seed: 42,\n motionIntensity: 'calm',\n background: {\n type: 'gradient',\n stops: [\n { at: 0, color: '#0a3a52' },\n { at: 0.6, color: '#062534' },\n { at: 1, color: '#02141d' },\n ],\n band: { color: '#0b1a12', height: 24 },\n },\n layers: [\n {\n count: 14,\n sprite: { kind: 'emoji', glyphs: ['🐟', '🐠', '🐡', '🦈', '🐙'] },\n size: [34, 72],\n motion: { type: 'drift', speed: [30, 110], bidirectional: true, bob: 6 },\n flip: true,\n },\n {\n count: 22,\n sprite: { kind: 'circle', radius: [2, 7], color: '#c8ebff' },\n motion: { type: 'rise', speed: [24, 78], sway: 8 },\n },\n ],\n};\n\n/** Rain: dense text streaks falling straight down over a near-black sky. */\nexport const RAIN_SPEC: SaverSpec = {\n schemaVersion: 1,\n id: 'rain',\n label: 'Rain (declarative)',\n seed: 7,\n motionIntensity: 'moderate',\n background: { type: 'solid', color: '#05070a' },\n layers: [\n {\n count: 140,\n sprite: { kind: 'text', strings: ['│'], color: '#9fd0ff' },\n size: [12, 26],\n motion: { type: 'drift', angle: 90, speed: [520, 900] },\n },\n ],\n};\n\nexport const EXAMPLE_SPECS: SaverSpec[] = [AQUARIUM_SPEC, RAIN_SPEC];\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/types.ts","../src/validate.ts","../src/simulate.ts","../src/compile.ts","../src/steer.ts","../src/examples/aquarium.ts","../src/examples/dev-dashboard.ts","../src/examples/lanterns.ts","../src/examples/rain.ts","../src/examples/sakura.ts","../src/examples/snowfall.ts","../src/examples/index.ts"],"sourcesContent":["/**\n * Declarative, agent-authorable saver format. A `SaverSpec` describes a saver as DATA\n * (no code), which `compileSaver` turns into a seeded, deterministic, flash-safe\n * SaverPlugin. It models the \"drifting sprite field\" family that covers most classic\n * savers (fish, toasters, DVD, rain, messages): a static background plus one or more\n * layers of entities that move.\n *\n * SAFETY INVARIANT: there is deliberately NO full-field strobe/flash primitive — the\n * background is static and entities are bounded sprites — so a compiled spec cannot\n * produce photosensitive flashing by construction (proven by sampling a compiled spec\n * through @idle-screens/validator).\n */\nexport const SCHEMA_VERSION = 1 as const;\n\nexport interface SaverSpec {\n schemaVersion: 1;\n id: string;\n label: string;\n /** Deterministic seed; falls back to the host's SaverContext.seed. */\n seed?: number;\n background?: BackgroundSpec;\n layers: LayerSpec[];\n motionIntensity?: 'calm' | 'moderate' | 'energetic';\n}\n\nexport type BackgroundSpec =\n | { type: 'solid'; color: string }\n | { type: 'gradient'; stops: GradientStop[]; band?: BandSpec };\n\n/** A vertical gradient stop (`at` 0 = top, 1 = bottom). */\nexport interface GradientStop {\n at: number;\n color: string;\n}\n\n/** An optional solid band at the bottom (e.g. an aquarium seafloor). */\nexport interface BandSpec {\n color: string;\n /** Band height in px. */\n height: number;\n}\n\nexport interface LayerSpec {\n count: number;\n sprite: SpriteSpec;\n /** Sprite size range in px (font size for emoji/text; ignored for circle). */\n size?: [number, number];\n motion: MotionSpec;\n /** Wrap around the opposite edge when leaving the viewport. Default true. */\n wrap?: boolean;\n /** Flip the sprite horizontally to face its heading. Default false. */\n flip?: boolean;\n /** Per-entity opacity range, both 0..1. Default [1,1]. */\n alpha?: [number, number];\n /** Compositing for this layer. 'lighter' = additive (glow stacking). Default source-over. */\n blend?: 'lighter';\n /**\n * Fractional spawn window (0 = left/top, 1 = right/bottom). Constrains where\n * entities are PLACED, not where they may travel. Default full viewport.\n */\n region?: { x?: [number, number]; y?: [number, number] };\n /**\n * Sinusoidal opacity breathing. SAFETY: amp is capped (LIMITS.maxPulseAmp),\n * period has a floor (LIMITS.minPulsePeriod — max 2 Hz), and every entity gets\n * its own seeded phase, so a layer can never strobe in unison. Effective alpha\n * is clamped to 0..1.\n */\n pulse?: { amp: number; period: number };\n /**\n * Addressable name for this layer. Enables `setParam` to use `key.field` paths\n * instead of `layers.N.field` indices. Also makes specs self-documenting.\n */\n key?: string;\n /**\n * Exact fractional position {x, y} for a single entity (0 = left/top, 1 = right/bottom).\n * Only valid when `count` is 1. Overrides `region` scatter placement.\n */\n position?: { x: number; y: number };\n}\n\nexport type SpriteSpec =\n | { kind: 'emoji'; glyphs: string[] }\n | {\n kind: 'text';\n strings: string[];\n color?: string;\n font?: string;\n align?: 'left' | 'center' | 'right';\n baseline?: 'top' | 'middle' | 'bottom';\n maxWidth?: number;\n }\n /** `soft` renders a radial falloff (glow orb) instead of a hard disc. */\n | { kind: 'circle'; radius: [number, number]; color: string; soft?: boolean };\n\nexport type MotionSpec =\n /**\n * Drift at a per-entity speed picked from `speed` (px/sec) along `angle` degrees\n * (0 = right, 90 = down; default 0). `bidirectional` randomly flips horizontal\n * direction per entity (e.g. fish swimming both ways); `bob` adds a small vertical\n * wobble amplitude (px). Covers horizontal fields, diagonals (toasters) and rain\n * (angle 90).\n */\n | { type: 'drift'; speed: [number, number]; angle?: number; bidirectional?: boolean; bob?: number }\n /** Rise upward (px/sec) with an optional horizontal sway amplitude (px) — bubbles. */\n | { type: 'rise'; speed: [number, number]; sway?: number }\n /** Bounce diagonally at a per-entity speed, reflecting off the edges (px/sec). */\n | { type: 'bounce'; speed: [number, number] }\n /** Entity stays exactly where placed. No movement. Use with `position` for pinned elements. */\n | { type: 'static' };\n\n/** A validation problem, pointing at a JSON path within the spec. */\nexport interface SpecError {\n path: string;\n message: string;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: SpecError[];\n}\n\n/** Perf/safety caps enforced by `validateSpec`. */\nexport const LIMITS = {\n maxPerLayer: 400,\n maxTotal: 800,\n maxLayers: 36,\n maxSpeed: 4000, // px/sec — bounds motion so nothing teleports\n maxPulseAmp: 0.5, // opacity breathing amplitude cap\n minPulsePeriod: 500, // ms — caps pulse at 2 Hz (WCAG flash threshold is 3 Hz)\n} as const;\n","import { LIMITS, SCHEMA_VERSION, type SaverSpec, type SpecError, type ValidationResult } from './types';\n\nconst HEX = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;\nconst isObj = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);\nconst isStr = (v: unknown): v is string => typeof v === 'string';\nconst isNum = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v);\nconst isRange = (v: unknown): v is [number, number] =>\n Array.isArray(v) && v.length === 2 && isNum(v[0]) && isNum(v[1]) && v[0] <= v[1];\n\n/**\n * Validate an untrusted (agent- or JSON-authored) spec structurally + semantically.\n * Returns typed errors; never throws. `compileSaver` refuses to run an invalid spec.\n */\nexport function validateSpec(spec: unknown): ValidationResult {\n const errors: SpecError[] = [];\n const err = (path: string, message: string): void => void errors.push({ path, message });\n\n if (!isObj(spec)) return { valid: false, errors: [{ path: '', message: 'spec must be an object' }] };\n\n if (spec.schemaVersion !== SCHEMA_VERSION) err('schemaVersion', `must be ${SCHEMA_VERSION}`);\n if (!isStr(spec.id) || spec.id.trim() === '') err('id', 'must be a non-empty string');\n if (!isStr(spec.label) || spec.label.trim() === '') err('label', 'must be a non-empty string');\n if (spec.seed !== undefined && !isNum(spec.seed)) err('seed', 'must be a number');\n if (spec.motionIntensity !== undefined && !['calm', 'moderate', 'energetic'].includes(spec.motionIntensity as string)) {\n err('motionIntensity', 'must be calm | moderate | energetic');\n }\n\n if (spec.background !== undefined) validateBackground(spec.background, err);\n\n if (!Array.isArray(spec.layers) || spec.layers.length === 0) {\n err('layers', 'must be a non-empty array');\n } else {\n if (spec.layers.length > LIMITS.maxLayers) err('layers', `at most ${LIMITS.maxLayers} layers`);\n let total = 0;\n spec.layers.forEach((layer, i) => {\n total += isObj(layer) && isNum(layer.count) ? layer.count : 0;\n validateLayer(layer, `layers[${i}]`, err);\n });\n if (total > LIMITS.maxTotal) err('layers', `total entities ${total} exceeds cap ${LIMITS.maxTotal}`);\n }\n\n return { valid: errors.length === 0, errors };\n}\n\nfunction color(v: unknown, path: string, err: (p: string, m: string) => void): void {\n if (!isStr(v) || !HEX.test(v)) err(path, 'must be a hex colour like #0a3a52');\n}\n\nfunction validateBackground(bg: unknown, err: (p: string, m: string) => void): void {\n if (!isObj(bg)) return err('background', 'must be an object');\n if (bg.type === 'solid') {\n color(bg.color, 'background.color', err);\n } else if (bg.type === 'gradient') {\n if (!Array.isArray(bg.stops) || bg.stops.length < 2) {\n err('background.stops', 'gradient needs at least 2 stops');\n } else {\n bg.stops.forEach((s, i) => {\n if (!isObj(s) || !isNum(s.at) || s.at < 0 || s.at > 1) err(`background.stops[${i}].at`, 'must be 0..1');\n color(isObj(s) ? s.color : undefined, `background.stops[${i}].color`, err);\n });\n }\n if (bg.band !== undefined) {\n if (!isObj(bg.band)) err('background.band', 'must be an object');\n else {\n color(bg.band.color, 'background.band.color', err);\n if (!isNum(bg.band.height) || bg.band.height <= 0) err('background.band.height', 'must be > 0');\n }\n }\n } else {\n err('background.type', 'must be solid | gradient');\n }\n}\n\nfunction validateLayer(layer: unknown, path: string, err: (p: string, m: string) => void): void {\n if (!isObj(layer)) return err(path, 'must be an object');\n if (!isNum(layer.count) || !Number.isInteger(layer.count) || layer.count < 1) {\n err(`${path}.count`, 'must be a positive integer');\n } else if (layer.count > LIMITS.maxPerLayer) {\n err(`${path}.count`, `at most ${LIMITS.maxPerLayer} per layer`);\n }\n if (layer.size !== undefined && (!isRange(layer.size) || layer.size[0] <= 0)) {\n err(`${path}.size`, 'must be a [min,max] range of positive px');\n }\n if (layer.wrap !== undefined && typeof layer.wrap !== 'boolean') err(`${path}.wrap`, 'must be a boolean');\n if (layer.flip !== undefined && typeof layer.flip !== 'boolean') err(`${path}.flip`, 'must be a boolean');\n\n if (layer.alpha !== undefined && (!isRange(layer.alpha) || layer.alpha[0] < 0 || layer.alpha[1] > 1)) {\n err(`${path}.alpha`, 'must be a [min,max] range within 0..1');\n }\n if (layer.blend !== undefined && layer.blend !== 'lighter') err(`${path}.blend`, \"must be 'lighter' when set\");\n if (layer.key !== undefined && (!isStr(layer.key) || layer.key.trim() === '')) {\n err(`${path}.key`, 'must be a non-empty string');\n }\n if (layer.position !== undefined) {\n if (!isObj(layer.position) || !isNum(layer.position.x) || !isNum(layer.position.y)) {\n err(`${path}.position`, 'must be {x, y} with numbers 0..1');\n } else {\n if (layer.position.x < 0 || layer.position.x > 1) err(`${path}.position.x`, 'must be 0..1');\n if (layer.position.y < 0 || layer.position.y > 1) err(`${path}.position.y`, 'must be 0..1');\n if (isNum(layer.count) && layer.count !== 1) err(`${path}.position`, 'position requires count: 1');\n }\n }\n if (layer.region !== undefined) {\n if (!isObj(layer.region)) err(`${path}.region`, 'must be an object');\n else {\n for (const axis of ['x', 'y'] as const) {\n const r = layer.region[axis];\n if (r !== undefined && (!isRange(r) || r[0] < 0 || r[1] > 1)) {\n err(`${path}.region.${axis}`, 'must be a [min,max] range within 0..1');\n }\n }\n }\n }\n if (layer.pulse !== undefined) {\n if (!isObj(layer.pulse)) err(`${path}.pulse`, 'must be an object');\n else {\n if (!isNum(layer.pulse.amp) || layer.pulse.amp <= 0 || layer.pulse.amp > LIMITS.maxPulseAmp) {\n err(`${path}.pulse.amp`, `must be within 0..${LIMITS.maxPulseAmp}`);\n }\n if (!isNum(layer.pulse.period) || layer.pulse.period < LIMITS.minPulsePeriod) {\n err(`${path}.pulse.period`, `must be >= ${LIMITS.minPulsePeriod} ms (flash-safety floor)`);\n }\n }\n }\n\n validateSprite(layer.sprite, `${path}.sprite`, err);\n validateMotion(layer.motion, `${path}.motion`, err);\n}\n\nfunction validateSprite(sprite: unknown, path: string, err: (p: string, m: string) => void): void {\n if (!isObj(sprite)) return err(path, 'must be an object');\n if (sprite.kind === 'emoji') {\n if (!Array.isArray(sprite.glyphs) || sprite.glyphs.length === 0 || !sprite.glyphs.every(isStr)) {\n err(`${path}.glyphs`, 'must be a non-empty array of strings');\n }\n } else if (sprite.kind === 'text') {\n if (!Array.isArray(sprite.strings) || sprite.strings.length === 0 || !sprite.strings.every(isStr)) {\n err(`${path}.strings`, 'must be a non-empty array of strings');\n }\n if (sprite.color !== undefined) color(sprite.color, `${path}.color`, err);\n if (sprite.align !== undefined && !['left', 'center', 'right'].includes(sprite.align as string)) {\n err(`${path}.align`, 'must be left | center | right');\n }\n if (sprite.baseline !== undefined && !['top', 'middle', 'bottom'].includes(sprite.baseline as string)) {\n err(`${path}.baseline`, 'must be top | middle | bottom');\n }\n if (sprite.maxWidth !== undefined && (!isNum(sprite.maxWidth) || sprite.maxWidth <= 0)) {\n err(`${path}.maxWidth`, 'must be a positive number');\n }\n } else if (sprite.kind === 'circle') {\n if (!isRange(sprite.radius) || sprite.radius[0] <= 0) err(`${path}.radius`, 'must be a [min,max] range of positive px');\n color(sprite.color, `${path}.color`, err);\n if (sprite.soft !== undefined && typeof sprite.soft !== 'boolean') err(`${path}.soft`, 'must be a boolean');\n } else {\n err(`${path}.kind`, 'must be emoji | text | circle');\n }\n}\n\nfunction validateMotion(motion: unknown, path: string, err: (p: string, m: string) => void): void {\n if (!isObj(motion)) return err(path, 'must be an object');\n const speedOk = (v: unknown, p: string): void => {\n if (!isRange(v)) err(p, 'must be a [min,max] range');\n else if (v[1] > LIMITS.maxSpeed) err(p, `speed exceeds cap ${LIMITS.maxSpeed} px/sec`);\n };\n if (motion.type === 'drift') {\n speedOk(motion.speed, `${path}.speed`);\n if (motion.angle !== undefined && !isNum(motion.angle)) err(`${path}.angle`, 'must be a number (degrees)');\n if (motion.bidirectional !== undefined && typeof motion.bidirectional !== 'boolean') err(`${path}.bidirectional`, 'must be a boolean');\n if (motion.bob !== undefined && !isNum(motion.bob)) err(`${path}.bob`, 'must be a number');\n } else if (motion.type === 'rise') {\n speedOk(motion.speed, `${path}.speed`);\n if (motion.sway !== undefined && !isNum(motion.sway)) err(`${path}.sway`, 'must be a number');\n } else if (motion.type === 'bounce') {\n speedOk(motion.speed, `${path}.speed`);\n } else if (motion.type === 'static') {\n // No fields to validate — static motion has no parameters.\n } else {\n err(`${path}.type`, 'must be drift | rise | bounce | static');\n }\n}\n\n/** Narrowing helper: validate + cast. Throws with a joined message when invalid. */\nexport function assertValidSpec(spec: unknown): SaverSpec {\n const r = validateSpec(spec);\n if (!r.valid) {\n throw new Error(`invalid saver spec:\\n${r.errors.map((e) => ` ${e.path || '<root>'}: ${e.message}`).join('\\n')}`);\n }\n return spec as SaverSpec;\n}\n","import type { Rng } from '@idle-screens/core';\nimport type { LayerSpec, SpriteSpec } from './types';\n\n/** A seeded entity: initial state + resolved velocity. `positionAt` is a pure function\n * of this + time, so the rAF loop and renderFrame(t) produce identical results. */\nexport interface Entity {\n x0: number;\n y0: number;\n size: number;\n spriteIndex: number;\n phase: number;\n vx: number; // px/sec\n vy: number; // px/sec\n bob: number; // vertical bob / horizontal sway amplitude (px)\n motion: 'drift' | 'rise' | 'bounce' | 'static';\n headingLeft: boolean;\n alpha: number; // resolved base opacity (default 1)\n pulseAmp: number; // opacity breathing amplitude (0 = none)\n pulsePeriod: number; // ms\n pulsePhase: number;\n}\n\nexport interface Placed {\n x: number;\n y: number;\n flip: boolean;\n}\n\nexport function spriteVariants(sprite: SpriteSpec): number {\n if (sprite.kind === 'emoji') return sprite.glyphs.length;\n if (sprite.kind === 'text') return sprite.strings.length;\n return 1;\n}\n\n/** Deterministically place a layer's entities using the seeded RNG (never Math.random). */\nexport function buildEntities(layer: LayerSpec, rng: Rng, w: number, h: number): Entity[] {\n const [smin, smax] = layer.size ?? [20, 40];\n const variants = spriteVariants(layer.sprite);\n const out: Entity[] = [];\n for (let i = 0; i < layer.count; i++) {\n const size =\n layer.sprite.kind === 'circle'\n ? rng.range(layer.sprite.radius[0], layer.sprite.radius[1]) * 2\n : rng.range(smin, smax);\n\n let vx = 0;\n let vy = 0;\n let bob = 0;\n let motion: Entity['motion'] = 'drift';\n const m = layer.motion;\n if (m.type === 'static') {\n motion = 'static';\n } else if (m.type === 'drift') {\n const s = rng.range(m.speed[0], m.speed[1]);\n let angle = ((m.angle ?? 0) * Math.PI) / 180;\n if (m.bidirectional && rng.next() < 0.5) angle = Math.PI - angle;\n vx = s * Math.cos(angle);\n vy = s * Math.sin(angle);\n bob = m.bob ?? 0;\n } else if (m.type === 'rise') {\n motion = 'rise';\n vy = -rng.range(m.speed[0], m.speed[1]); // upward\n bob = m.sway ?? 0;\n } else {\n motion = 'bounce';\n const s = rng.range(m.speed[0], m.speed[1]);\n const a = rng.range(0, Math.PI * 2);\n vx = s * Math.cos(a);\n vy = s * Math.sin(a);\n }\n\n let x0: number;\n let y0: number;\n if (layer.position && layer.count === 1) {\n x0 = layer.position.x * w;\n y0 = layer.position.y * h;\n } else {\n // Spawn window defaults to the full viewport. IMPORTANT (determinism/compat):\n // optional features must only consume EXTRA rng draws when declared, so specs\n // written before a feature existed keep bit-identical entity streams.\n const [rx0, rx1] = layer.region?.x ?? [0, 1];\n const [ry0, ry1] = layer.region?.y ?? [0, 1];\n x0 = rng.range(rx0 * w, rx1 * w);\n y0 = rng.range(ry0 * h, ry1 * h);\n }\n\n out.push({\n x0,\n y0,\n size,\n spriteIndex: variants > 1 ? rng.int(0, variants - 1) : 0,\n phase: rng.range(0, Math.PI * 2),\n vx,\n vy,\n bob,\n motion,\n headingLeft: vx < 0,\n alpha: layer.alpha ? rng.range(layer.alpha[0], layer.alpha[1]) : 1,\n pulseAmp: layer.pulse?.amp ?? 0,\n pulsePeriod: layer.pulse?.period ?? 1000,\n pulsePhase: layer.pulse ? rng.range(0, Math.PI * 2) : 0,\n });\n }\n return out;\n}\n\n/** Analytic opacity of an entity at logical time `t` (ms). Pure, clamped to 0..1. */\nexport function alphaAt(e: Entity, t: number): number {\n if (!e.pulseAmp) return e.alpha;\n const a = e.alpha + e.pulseAmp * Math.sin((t * 2 * Math.PI) / e.pulsePeriod + e.pulsePhase);\n return a < 0 ? 0 : a > 1 ? 1 : a;\n}\n\nfunction wrap(v: number, min: number, max: number): number {\n const range = max - min;\n return ((((v - min) % range) + range) % range) + min;\n}\n\nfunction reflect(v: number, min: number, max: number): number {\n const range = max - min;\n if (range <= 0) return min;\n const period = 2 * range;\n const p = (((v - min) % period) + period) % period;\n return min + (p < range ? p : period - p);\n}\n\n/** Analytic position of an entity at logical time `t` (ms). Pure & deterministic. */\nexport function positionAt(e: Entity, t: number, w: number, h: number): Placed {\n if (e.motion === 'static') return { x: e.x0, y: e.y0, flip: false };\n const dt = t / 1000;\n const m = e.size;\n if (e.motion === 'bounce') {\n return {\n x: reflect(e.x0 + e.vx * dt, m / 2, w - m / 2),\n y: reflect(e.y0 + e.vy * dt, m / 2, h - m / 2),\n flip: false,\n };\n }\n if (e.motion === 'rise') {\n return {\n x: e.x0 + (e.bob ? e.bob * Math.sin(t / 700 + e.phase) : 0),\n y: wrap(e.y0 + e.vy * dt, -m, h + m),\n flip: false,\n };\n }\n // drift\n const x = wrap(e.x0 + e.vx * dt, -m, w + m);\n let y = e.vy !== 0 ? wrap(e.y0 + e.vy * dt, -m, h + m) : e.y0;\n if (e.bob) y += e.bob * Math.sin(t / 500 + e.phase);\n return { x, y, flip: e.headingLeft };\n}\n","import {\n createRng,\n type ControlTrack,\n type SaverContext,\n type SaverInstance,\n type SaverManifest,\n type SaverPlugin,\n} from '@idle-screens/core';\nimport { assertValidSpec, validateSpec } from './validate';\nimport { alphaAt, buildEntities, positionAt, type Entity } from './simulate';\nimport {\n applyDeltasToSpec,\n easeSmooth,\n lerpSpec,\n structuralSignature,\n type SteerDelta,\n} from './steer';\nimport type { LayerSpec, SaverSpec } from './types';\n\nconst DEFAULT_STEER_DUR = 1000;\n\n/** Expand #rgb/#rrggbb to an rgba() string — needed for gradient stops with alpha. */\nfunction hexToRgba(hex: string, alpha: number): string {\n const h = hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;\n const n = parseInt(h.slice(1), 16);\n return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${alpha})`;\n}\n\n/** Derive a manifest so a compiled spec composes with @idle-screens/capabilities. */\nexport function manifestFor(spec: SaverSpec): SaverManifest {\n const total = spec.layers.reduce((n, l) => n + l.count, 0);\n const costTier = total < 30 ? 'idle' : total < 150 ? 'low' : total < 400 ? 'medium' : 'high';\n return {\n id: spec.id,\n label: spec.label,\n passthrough: false,\n minBackend: 'canvas2d',\n costTier,\n motionIntensity: spec.motionIntensity ?? 'moderate',\n reducedMotionFallback: 'static',\n // Flash-safe by construction: static background + bounded sprites, no strobe\n // primitive. Proven by sampling a compiled spec through @idle-screens/validator.\n a11y: { flashSafe: true },\n workerReady: true,\n };\n}\n\ninterface Built {\n layer: LayerSpec;\n entities: Entity[];\n}\n\nclass SpecInstance implements SaverInstance {\n private readonly canvas: HTMLCanvasElement | OffscreenCanvas;\n private readonly ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n private readonly saverCtx: SaverContext;\n private readonly seed: number;\n private w: number;\n private h: number;\n private layers: Built[] = [];\n private frameId: number | null = null;\n private paused = false;\n private startT = 0;\n private baseT = 0; // elapsed logical time carried across pause/resume\n private lastT = 0;\n\n /** The spec currently being rendered (base spec + any applied steering). */\n private effSpec: SaverSpec;\n /** Active glide between two resolved specs (live setParam/applyTrack). */\n private transition: { from: SaverSpec; to: SaverSpec; startT: number; dur: number } | null = null;\n private lastStructural = '';\n\n constructor(\n private readonly spec: SaverSpec,\n ctx: SaverContext,\n ) {\n this.effSpec = spec;\n this.saverCtx = ctx;\n this.seed = ((spec.seed ?? ctx.seed) >>> 0) || 1;\n let canvas: HTMLCanvasElement | OffscreenCanvas;\n if (ctx.surface) {\n canvas = ctx.surface;\n } else {\n const el = document.createElement('canvas');\n el.style.cssText = 'display:block;width:100%;height:100%';\n el.setAttribute('aria-hidden', 'true');\n ctx.host.appendChild(el);\n canvas = el;\n }\n this.canvas = canvas;\n const c2d = canvas.getContext('2d', { alpha: false }) as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | null;\n if (!c2d) throw new Error('schema saver: no 2d context');\n this.ctx = c2d;\n\n this.w = ctx.width;\n this.h = ctx.height;\n this.sizeCanvas();\n this.rebuild();\n\n this.paused = ctx.reducedMotion;\n if (this.paused) this.renderFrame(0, this.seed);\n else this.start();\n }\n\n private sizeCanvas(): void {\n const dpr = Math.min(this.saverCtx.dpr, 2);\n this.canvas.width = Math.max(1, Math.round(this.w * dpr));\n this.canvas.height = Math.max(1, Math.round(this.h * dpr));\n this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n }\n\n /** (Re)seed and place all entities — deterministic for the seed + size. */\n private rebuild(): void {\n const rng = createRng(this.seed);\n this.layers = this.effSpec.layers.map((layer) => ({ layer, entities: buildEntities(layer, rng, this.w, this.h) }));\n this.lastStructural = structuralSignature(this.effSpec);\n }\n\n private start(): void {\n if (this.frameId !== null || typeof requestAnimationFrame === 'undefined') return;\n this.startT = 0;\n this.frameId = requestAnimationFrame((now) => this.loop(now));\n }\n\n private stop(): void {\n if (this.frameId !== null) {\n cancelAnimationFrame(this.frameId);\n this.frameId = null;\n }\n // Freeze elapsed time so resume continues the scene instead of restarting at t=0.\n this.baseT = this.lastT;\n }\n\n private loop(now: number): void {\n this.frameId = requestAnimationFrame((n) => this.loop(n));\n if (this.startT === 0) this.startT = now;\n this.lastT = now - this.startT + this.baseT;\n this.renderFrame(this.lastT, this.seed);\n }\n\n private drawBackground(): void {\n const { ctx, w, h } = this;\n const bg = this.effSpec.background;\n if (!bg || bg.type === 'solid') {\n ctx.fillStyle = bg?.color ?? '#05050a';\n ctx.fillRect(0, 0, w, h);\n return;\n }\n const g = ctx.createLinearGradient(0, 0, 0, h);\n for (const s of bg.stops) g.addColorStop(Math.max(0, Math.min(1, s.at)), s.color);\n ctx.fillStyle = g;\n ctx.fillRect(0, 0, w, h);\n if (bg.band) {\n ctx.fillStyle = bg.band.color;\n ctx.fillRect(0, h - bg.band.height, w, bg.band.height);\n }\n }\n\n private drawEntity(built: Built, e: Entity, t: number): void {\n const { ctx } = this;\n const p = positionAt(e, t, this.w, this.h);\n const sprite = built.layer.sprite;\n ctx.globalAlpha = alphaAt(e, t);\n if (sprite.kind === 'circle') {\n const r = e.size / 2;\n if (sprite.soft) {\n // Glow orb: solid core fading radially to transparent (pairs with blend:'lighter').\n const g = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, r);\n g.addColorStop(0, sprite.color);\n g.addColorStop(0.35, hexToRgba(sprite.color, 0.75));\n g.addColorStop(1, hexToRgba(sprite.color, 0));\n ctx.fillStyle = g;\n } else {\n ctx.fillStyle = sprite.color;\n }\n ctx.beginPath();\n ctx.arc(p.x, p.y, r, 0, Math.PI * 2);\n ctx.fill();\n return;\n }\n ctx.save();\n ctx.translate(p.x, p.y);\n if (p.flip && built.layer.flip) ctx.scale(-1, 1);\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n if (sprite.kind === 'emoji') {\n ctx.font = `${e.size}px serif`;\n ctx.fillText(sprite.glyphs[e.spriteIndex] ?? sprite.glyphs[0]!, 0, 0);\n } else {\n ctx.textAlign = sprite.align ?? 'center';\n ctx.textBaseline = sprite.baseline ?? 'middle';\n ctx.font = sprite.font ?? `${e.size}px system-ui, sans-serif`;\n ctx.fillStyle = sprite.color ?? '#e6e8ef';\n const text = sprite.strings[e.spriteIndex] ?? sprite.strings[0]!;\n if (sprite.maxWidth) ctx.fillText(text, 0, 0, sprite.maxWidth);\n else ctx.fillText(text, 0, 0);\n }\n ctx.restore();\n }\n\n /** Advance any live steering glide; rebuild entities on structural change. */\n private stepTransition(t: number): void {\n const tr = this.transition;\n if (!tr) return;\n const k = tr.dur <= 0 ? 1 : (t - tr.startT) / tr.dur;\n this.effSpec = k >= 1 ? tr.to : lerpSpec(tr.from, tr.to, easeSmooth(k));\n if (k >= 1) this.transition = null;\n // Placement/motion fields are baked into entities at build time; rebuild\n // (deterministic — same seed → same stream) only when those change.\n const sig = structuralSignature(this.effSpec);\n if (sig !== this.lastStructural) this.rebuild();\n else this.layers.forEach((b, i) => { b.layer = this.effSpec.layers[i] ?? b.layer; });\n }\n\n /**\n * Live steering: glide from what's currently rendered to the track's target\n * state (deltas applied last-wins). Duration = the longest delta `dur`.\n */\n applyTrack(track: ControlTrack): void {\n const deltas = (track?.deltas ?? []) as unknown as SteerDelta[];\n const target = applyDeltasToSpec(this.effSpec, deltas);\n if (!validateSpec(target).valid) return;\n const dur = deltas.length\n ? deltas.reduce((m, d) => Math.max(m, d.dur ?? DEFAULT_STEER_DUR), 0)\n : DEFAULT_STEER_DUR;\n this.transition = {\n from: JSON.parse(JSON.stringify(this.effSpec)) as SaverSpec,\n to: target,\n startT: this.lastT,\n dur,\n };\n if (this.paused) {\n // No frames will run the glide — jump straight to the target.\n this.transition = null;\n this.effSpec = target;\n if (structuralSignature(this.effSpec) !== this.lastStructural) this.rebuild();\n else this.layers.forEach((b, i) => { b.layer = this.effSpec.layers[i] ?? b.layer; });\n this.renderFrame(this.lastT, this.seed);\n }\n }\n\n /** Deterministic, frame-addressable render (shared by the rAF loop). */\n renderFrame(t: number, _seed: number): void {\n this.stepTransition(t);\n const { ctx } = this;\n ctx.globalAlpha = 1;\n ctx.globalCompositeOperation = 'source-over';\n this.drawBackground();\n for (const built of this.layers) {\n ctx.globalCompositeOperation = built.layer.blend ?? 'source-over';\n for (const e of built.entities) this.drawEntity(built, e, t);\n }\n ctx.globalAlpha = 1;\n ctx.globalCompositeOperation = 'source-over';\n }\n\n setPaused(paused: boolean): void {\n this.paused = paused;\n if (paused) {\n this.stop();\n } else {\n this.start();\n }\n }\n\n resize(width: number, height: number, dpr?: number): void {\n this.w = width;\n this.h = height;\n if (dpr !== undefined) this.saverCtx.dpr = dpr;\n this.sizeCanvas();\n this.rebuild();\n if (this.paused) this.renderFrame(0, this.seed);\n }\n\n dispose(): void {\n this.stop();\n if (typeof HTMLCanvasElement !== 'undefined' && this.canvas instanceof HTMLCanvasElement) this.canvas.remove();\n }\n}\n\n/**\n * Compile a declarative spec into a runnable SaverPlugin. Throws if the spec is invalid\n * (so an agent-authored spec is validated before it can run). The result is seeded,\n * deterministic, `renderFrame(t,seed)`-addressable, and flash-safe by construction.\n */\nexport function compileSaver(spec: unknown): SaverPlugin {\n const valid = assertValidSpec(spec);\n return {\n manifest: manifestFor(valid),\n mount: (ctx: SaverContext) => new SpecInstance(valid, ctx),\n spec: valid,\n };\n}\n","/**\n * Pure helpers for LIVE steering of compiled specs (setParam / applyTrack).\n * A track's deltas address spec fields by dot-path (\"layers.0.count\",\n * \"background.stops.1.color\") or layer key (\"fireflies.count\"). Steering\n * changes existing values only — unknown paths are ignored (the server\n * validates and rejects them; the runtime stays lenient).\n */\nimport type { SaverSpec } from './types';\n\ninterface PathTarget {\n parent: Record<string, unknown> | unknown[];\n key: string | number;\n}\n\nconst UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/** Resolve a dot-path (key-aware) to its parent + final key; null if absent. */\nexport function resolveSpecPath(spec: unknown, path: string): PathTarget | null {\n if (!spec || typeof spec !== 'object' || !path) return null;\n const parts = path.split('.');\n if (parts.some((p) => UNSAFE_KEYS.has(p))) return null;\n const s = spec as { layers?: Array<Record<string, unknown>> };\n if (parts[0] !== 'layers' && parts[0] !== 'background' && Array.isArray(s.layers)) {\n const idx = s.layers.findIndex((l) => l && l.key === parts[0]);\n if (idx === -1) return null;\n parts.splice(0, 1, 'layers', String(idx));\n }\n let node: unknown = spec;\n for (let i = 0; i < parts.length - 1; i++) {\n if (node === null || typeof node !== 'object') return null;\n node = (node as Record<string, unknown>)[parts[i]!];\n }\n if (node === null || typeof node !== 'object') return null;\n const last = parts[parts.length - 1]!;\n const key: string | number = Array.isArray(node) ? Number(last) : last;\n const exists = Array.isArray(node)\n ? Number.isInteger(key as number) && (key as number) >= 0 && (key as number) < node.length\n : last in (node as Record<string, unknown>);\n return exists ? { parent: node as PathTarget['parent'], key } : null;\n}\n\n/** A steering delta as carried on a channel control-track. */\nexport interface SteerDelta {\n t: number;\n path: string;\n value: unknown;\n ease?: string;\n dur?: number;\n}\n\n/** Apply deltas to a deep copy of the spec (last-wins, target values). */\nexport function applyDeltasToSpec(spec: SaverSpec, deltas: SteerDelta[]): SaverSpec {\n const copy = JSON.parse(JSON.stringify(spec)) as SaverSpec;\n for (const d of deltas) {\n const loc = resolveSpecPath(copy, d.path);\n if (loc) (loc.parent as Record<string | number, unknown>)[loc.key] = d.value;\n }\n return copy;\n}\n\nconst HEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const h = hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;\n const n = parseInt(h.slice(1), 16);\n return [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n}\n\nfunction lerpHex(a: string, b: string, k: number): string {\n const ca = hexToRgb(a);\n const cb = hexToRgb(b);\n const mix = ca.map((v, i) => Math.round(v + (cb[i]! - v) * k));\n return `#${mix.map((v) => v.toString(16).padStart(2, '0')).join('')}`;\n}\n\n/**\n * Interpolate between two structurally-similar specs at progress k (0..1).\n * Numbers lerp, hex colours lerp per channel, everything else steps to the\n * target immediately (k > 0). Layer counts round to integers.\n */\nexport function lerpSpec(from: SaverSpec, to: SaverSpec, k: number): SaverSpec {\n const kk = Math.max(0, Math.min(1, k));\n const walk = (a: unknown, b: unknown, key?: string | number): unknown => {\n if (typeof a === 'number' && typeof b === 'number') {\n const v = a + (b - a) * kk;\n return key === 'count' ? Math.max(1, Math.round(v)) : v;\n }\n if (typeof a === 'string' && typeof b === 'string' && HEX.test(a) && HEX.test(b)) {\n return lerpHex(a, b, kk);\n }\n if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {\n return b.map((bv, i) => walk(a[i], bv, i));\n }\n if (a && b && typeof a === 'object' && typeof b === 'object' && !Array.isArray(a) && !Array.isArray(b)) {\n const out: Record<string, unknown> = {};\n for (const kName of Object.keys(b as Record<string, unknown>)) {\n out[kName] = walk((a as Record<string, unknown>)[kName], (b as Record<string, unknown>)[kName], kName);\n }\n return out;\n }\n return kk > 0 ? b : a; // non-interpolable → step to target\n };\n return walk(from, to) as SaverSpec;\n}\n\n/** Smooth (ease-in-out) progress curve used for glides. */\nexport function easeSmooth(k: number): number {\n const c = Math.max(0, Math.min(1, k));\n return c * c * (3 - 2 * c);\n}\n\n/**\n * Fields that require re-seeding entities when they change (placement/motion\n * are baked at build time). Colour/alpha/pulse-only changes redraw in place.\n */\nexport function structuralSignature(spec: SaverSpec): string {\n return JSON.stringify(\n spec.layers.map((l) => [\n l.count,\n l.size,\n l.region,\n l.position,\n l.motion,\n l.wrap,\n l.flip,\n l.alpha,\n l.pulse,\n l.sprite.kind,\n l.sprite.kind === 'circle' ? l.sprite.radius : undefined,\n l.sprite.kind === 'emoji'\n ? l.sprite.glyphs.length\n : l.sprite.kind === 'text'\n ? l.sprite.strings.length\n : undefined,\n ]),\n );\n}\n","import type { SaverSpec } from '../types';\n\n/**\n * Reproduces the hand-coded Fish Aquarium saver as pure DATA — the discriminating test\n * that the schema is actually expressive: two layers (fish drifting both ways at varied\n * speeds with a bob + flip, over a gradient with a seafloor band; bubbles rising with sway).\n */\nexport const AQUARIUM_SPEC: SaverSpec = {\n schemaVersion: 1,\n id: 'aquarium',\n label: 'Aquarium',\n seed: 42,\n motionIntensity: 'calm',\n background: {\n type: 'gradient',\n stops: [\n { at: 0, color: '#0a3a52' },\n { at: 0.6, color: '#062534' },\n { at: 1, color: '#02141d' },\n ],\n band: { color: '#0b1a12', height: 24 },\n },\n layers: [\n {\n count: 14,\n sprite: { kind: 'emoji', glyphs: ['🐟', '🐠', '🐡', '🦈', '🐙'] },\n size: [34, 72],\n motion: { type: 'drift', speed: [30, 110], bidirectional: true, bob: 6 },\n flip: true,\n },\n {\n count: 22,\n sprite: { kind: 'circle', radius: [2, 7], color: '#c8ebff' },\n motion: { type: 'rise', speed: [24, 78], sway: 8 },\n },\n ],\n};\n","import type { SaverSpec } from '../types';\n\n/**\n * Dev Dashboard: a dense ops-style HUD exercising static positioning, keyed layers,\n * and text alignment at scale. Two ambient particle layers provide depth behind 28\n * pinned text elements arranged in a three-column layout with header, metrics,\n * file list, and scrolling event log.\n */\nexport const DASHBOARD_SPEC: SaverSpec = {\n schemaVersion: 1,\n id: 'dev-dashboard',\n label: 'Dev Dashboard',\n seed: 256,\n motionIntensity: 'calm',\n background: {\n type: 'gradient',\n stops: [\n { at: 0, color: '#06060c' },\n { at: 0.5, color: '#0a0a14' },\n { at: 1, color: '#0c0c18' },\n ],\n },\n layers: [\n // --- ambient depth ---\n {\n count: 50,\n sprite: { kind: 'circle', radius: [0.2, 0.8], color: '#1a2538' },\n alpha: [0.1, 0.25],\n motion: { type: 'drift', speed: [0.3, 1.2], bob: 0.3 },\n },\n {\n count: 12,\n sprite: { kind: 'circle', radius: [1, 2.5], color: '#1a3050', soft: true },\n alpha: [0.08, 0.18],\n blend: 'lighter',\n pulse: { amp: 0.08, period: 4000 },\n motion: { type: 'drift', speed: [0.5, 1.5], bob: 0.5 },\n },\n // --- header ---\n {\n key: 'title', count: 1,\n position: { x: 0.5, y: 0.04 },\n sprite: { kind: 'text', strings: ['DEV PULSE'], color: '#556677',\n font: \"600 13px 'SF Mono', monospace\", align: 'center' },\n motion: { type: 'static' },\n },\n {\n key: 'status', count: 1,\n position: { x: 0.92, y: 0.04 },\n sprite: { kind: 'text', strings: ['● ACTIVE'], color: '#44cc66',\n font: \"bold 10px monospace\", align: 'right' },\n motion: { type: 'static' },\n },\n {\n key: 'uptime', count: 1,\n position: { x: 0.08, y: 0.04 },\n sprite: { kind: 'text', strings: ['up 2h 14m'], color: '#445566',\n font: \"10px monospace\", align: 'left' },\n motion: { type: 'static' },\n },\n // --- left column: metrics ---\n {\n key: 'tools-label', count: 1,\n position: { x: 0.06, y: 0.12 },\n sprite: { kind: 'text', strings: ['TOOL CALLS'], color: '#445566',\n font: '9px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n {\n key: 'tools-value', count: 1,\n position: { x: 0.06, y: 0.16 },\n sprite: { kind: 'text', strings: ['147'], color: '#44aaff',\n font: 'bold 28px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n {\n key: 'reads-label', count: 1,\n position: { x: 0.06, y: 0.26 },\n sprite: { kind: 'text', strings: ['READS'], color: '#445566',\n font: '9px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n {\n key: 'reads-value', count: 1,\n position: { x: 0.06, y: 0.30 },\n sprite: { kind: 'text', strings: ['89'], color: '#44ddcc',\n font: 'bold 28px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n {\n key: 'edits-label', count: 1,\n position: { x: 0.06, y: 0.40 },\n sprite: { kind: 'text', strings: ['EDITS'], color: '#445566',\n font: '9px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n {\n key: 'edits-value', count: 1,\n position: { x: 0.06, y: 0.44 },\n sprite: { kind: 'text', strings: ['34'], color: '#ff8844',\n font: 'bold 28px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n {\n key: 'errors-label', count: 1,\n position: { x: 0.06, y: 0.54 },\n sprite: { kind: 'text', strings: ['ERRORS'], color: '#445566',\n font: '9px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n {\n key: 'errors-value', count: 1,\n position: { x: 0.06, y: 0.58 },\n sprite: { kind: 'text', strings: ['2'], color: '#ff4455',\n font: 'bold 28px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n // --- center column: system gauges ---\n {\n key: 'cpu-label', count: 1,\n position: { x: 0.38, y: 0.12 },\n sprite: { kind: 'text', strings: ['CPU'], color: '#445566',\n font: '9px monospace', align: 'center' },\n motion: { type: 'static' },\n },\n {\n key: 'cpu-value', count: 1,\n position: { x: 0.38, y: 0.22 },\n sprite: { kind: 'text', strings: ['73%'], color: '#44ff88',\n font: 'bold 36px monospace', align: 'center' },\n motion: { type: 'static' },\n },\n {\n key: 'mem-label', count: 1,\n position: { x: 0.38, y: 0.36 },\n sprite: { kind: 'text', strings: ['MEMORY'], color: '#445566',\n font: '9px monospace', align: 'center' },\n motion: { type: 'static' },\n },\n {\n key: 'mem-value', count: 1,\n position: { x: 0.38, y: 0.46 },\n sprite: { kind: 'text', strings: ['4.2 GB'], color: '#ddcc44',\n font: 'bold 36px monospace', align: 'center' },\n motion: { type: 'static' },\n },\n {\n key: 'tokens-label', count: 1,\n position: { x: 0.38, y: 0.58 },\n sprite: { kind: 'text', strings: ['TOKENS'], color: '#445566',\n font: '9px monospace', align: 'center' },\n motion: { type: 'static' },\n },\n {\n key: 'tokens-value', count: 1,\n position: { x: 0.38, y: 0.66 },\n sprite: { kind: 'text', strings: ['128k'], color: '#aa88ff',\n font: 'bold 28px monospace', align: 'center' },\n motion: { type: 'static' },\n },\n // --- right column: recent files ---\n {\n key: 'files-label', count: 1,\n position: { x: 0.62, y: 0.12 },\n sprite: { kind: 'text', strings: ['RECENT FILES'], color: '#445566',\n font: '9px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n {\n key: 'file-1', count: 1,\n position: { x: 0.62, y: 0.17 },\n sprite: { kind: 'text', strings: ['src/worker.ts'], color: '#8899aa',\n font: '11px monospace', align: 'left', baseline: 'top', maxWidth: 280 },\n motion: { type: 'static' },\n },\n {\n key: 'file-2', count: 1,\n position: { x: 0.62, y: 0.21 },\n sprite: { kind: 'text', strings: ['site/src/App.tsx'], color: '#778899',\n font: '11px monospace', align: 'left', baseline: 'top', maxWidth: 280 },\n motion: { type: 'static' },\n },\n {\n key: 'file-3', count: 1,\n position: { x: 0.62, y: 0.25 },\n sprite: { kind: 'text', strings: ['packages/schema/compile.ts'], color: '#667788',\n font: '11px monospace', align: 'left', baseline: 'top', maxWidth: 280 },\n motion: { type: 'static' },\n },\n {\n key: 'file-4', count: 1,\n position: { x: 0.62, y: 0.29 },\n sprite: { kind: 'text', strings: ['docs/dashboard-spec.md'], color: '#556677',\n font: '11px monospace', align: 'left', baseline: 'top', maxWidth: 280 },\n motion: { type: 'static' },\n },\n {\n key: 'file-5', count: 1,\n position: { x: 0.62, y: 0.33 },\n sprite: { kind: 'text', strings: ['packages/core/types.ts'], color: '#445566',\n font: '11px monospace', align: 'left', baseline: 'top', maxWidth: 280 },\n motion: { type: 'static' },\n },\n // --- right column: session info ---\n {\n key: 'session-label', count: 1,\n position: { x: 0.62, y: 0.42 },\n sprite: { kind: 'text', strings: ['SESSION'], color: '#445566',\n font: '9px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n {\n key: 'session-model', count: 1,\n position: { x: 0.62, y: 0.47 },\n sprite: { kind: 'text', strings: ['claude-opus-4-6'], color: '#8888aa',\n font: '11px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n {\n key: 'session-cost', count: 1,\n position: { x: 0.62, y: 0.51 },\n sprite: { kind: 'text', strings: ['$2.47 this session'], color: '#667788',\n font: '11px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n // --- bottom: event log ---\n {\n key: 'log-label', count: 1,\n position: { x: 0.06, y: 0.76 },\n sprite: { kind: 'text', strings: ['EVENT LOG'], color: '#445566',\n font: '9px monospace', align: 'left', baseline: 'top' },\n motion: { type: 'static' },\n },\n {\n key: 'log-1', count: 1,\n position: { x: 0.06, y: 0.80 },\n sprite: { kind: 'text', strings: ['14:07 Edit compile.ts +18 -3'], color: '#667766',\n font: '10px monospace', align: 'left', baseline: 'top', maxWidth: 600 },\n motion: { type: 'static' },\n },\n {\n key: 'log-2', count: 1,\n position: { x: 0.06, y: 0.84 },\n sprite: { kind: 'text', strings: ['14:06 Bash pnpm test ✓ 125 passed'], color: '#556655',\n font: '10px monospace', align: 'left', baseline: 'top', maxWidth: 600 },\n motion: { type: 'static' },\n },\n {\n key: 'log-3', count: 1,\n position: { x: 0.06, y: 0.88 },\n sprite: { kind: 'text', strings: ['14:05 Read types.ts (121 lines)'], color: '#4d5e4d',\n font: '10px monospace', align: 'left', baseline: 'top', maxWidth: 600 },\n motion: { type: 'static' },\n },\n {\n key: 'log-4', count: 1,\n position: { x: 0.06, y: 0.92 },\n sprite: { kind: 'text', strings: ['14:03 Edit examples/dev-dashboard.ts +84 -1'], color: '#445544',\n font: '10px monospace', align: 'left', baseline: 'top', maxWidth: 600 },\n motion: { type: 'static' },\n },\n {\n key: 'log-5', count: 1,\n position: { x: 0.06, y: 0.96 },\n sprite: { kind: 'text', strings: ['14:01 Bash pnpm build ✓ 6 packages'], color: '#3d4e3d',\n font: '10px monospace', align: 'left', baseline: 'top', maxWidth: 600 },\n motion: { type: 'static' },\n },\n ],\n};\n","import type { SaverSpec } from '../types';\n\n/**\n * Night Lanterns (Yi Peng festival sky): depth from four correlated parallax cues —\n * closer layers are LARGER, FASTER, SWAY WIDER, and glow WARMER/BRIGHTER. v2 of this\n * composition exercises the expressive additions the first draft exposed as missing:\n * soft glow orbs + additive blend (lantern light that sums where it overlaps), bounded\n * alpha pulse with per-entity phases (breathing flames, strobe-proof by construction),\n * and spawn regions (stars only above the horizon; dim ember shimmer kept low near\n * the water line).\n */\nexport const LANTERNS_SPEC: SaverSpec = {\n schemaVersion: 1,\n id: 'lanterns',\n label: 'Night Lanterns',\n seed: 88,\n motionIntensity: 'calm',\n background: {\n type: 'gradient',\n stops: [\n { at: 0, color: '#04060f' },\n { at: 0.55, color: '#0b1026' },\n { at: 0.85, color: '#251731' },\n { at: 1, color: '#472518' },\n ],\n },\n layers: [\n {\n count: 60,\n sprite: { kind: 'circle', radius: [0.5, 1.4], color: '#8fa0c8' },\n alpha: [0.35, 1],\n region: { y: [0, 0.62] },\n motion: { type: 'drift', speed: [0.5, 2], bob: 1 },\n },\n {\n count: 36,\n sprite: {\n kind: 'circle',\n radius: [1.5, 3],\n color: '#b06a2a',\n soft: true,\n },\n alpha: [0.5, 0.9],\n blend: 'lighter',\n pulse: { amp: 0.18, period: 2800 },\n motion: { type: 'rise', speed: [6, 14], sway: 4 },\n },\n {\n count: 14,\n sprite: {\n kind: 'circle',\n radius: [3.5, 6.5],\n color: '#e08a34',\n soft: true,\n },\n alpha: [0.6, 1],\n blend: 'lighter',\n pulse: { amp: 0.22, period: 3400 },\n motion: { type: 'rise', speed: [16, 28], sway: 7 },\n },\n {\n count: 8,\n sprite: { kind: 'emoji', glyphs: ['🏮'] },\n size: [26, 44],\n alpha: [0.85, 1],\n motion: { type: 'rise', speed: [30, 48], sway: 10 },\n },\n {\n count: 4,\n sprite: { kind: 'emoji', glyphs: ['🏮'] },\n size: [58, 84],\n motion: { type: 'rise', speed: [52, 74], sway: 14 },\n },\n {\n count: 12,\n sprite: { kind: 'circle', radius: [2, 5], color: '#e08a34', soft: true },\n alpha: [0.12, 0.35],\n blend: 'lighter',\n region: { y: [0.93, 1] },\n pulse: { amp: 0.1, period: 2200 },\n motion: { type: 'drift', speed: [2, 6], bob: 2 },\n },\n ],\n};\n","import type { SaverSpec } from '../types';\n\n/** Rain: dense text streaks falling straight down over a near-black sky. */\nexport const RAIN_SPEC: SaverSpec = {\n schemaVersion: 1,\n id: 'rain',\n label: 'Rain',\n seed: 7,\n motionIntensity: 'moderate',\n background: { type: 'solid', color: '#05070a' },\n layers: [\n {\n count: 140,\n sprite: { kind: 'text', strings: ['│'], color: '#9fd0ff' },\n size: [12, 26],\n motion: { type: 'drift', angle: 90, speed: [520, 900] },\n },\n ],\n};\n","import type { SaverSpec } from '../types';\n\n/**\n * Sakura Drift: cherry-blossom petals carried on a spring twilight breeze. Five\n * correlated-parallax layers build the depth — the far backdrop is a barely-moving\n * field of soft blossom-glow orbs (additive, breathing out of phase), then three\n * petal layers grow LARGER, FASTER, BRIGHTER and FLUTTER WIDER as they approach\n * (the near gust), over a blue-hour gradient with a warm rose horizon and a dark\n * grass band. A final low layer of dim pink glow settles at the grass line — fallen\n * petals catching the last light. Wind direction is consistent (down-left) across\n * every moving layer; petals differ from snowfall by glyph, colour, palette and the\n * diagonal breeze, and from lanterns by falling rather than rising.\n */\nexport const SAKURA_SPEC: SaverSpec = {\n schemaVersion: 1,\n id: 'sakura',\n label: 'Sakura Drift',\n seed: 314,\n motionIntensity: 'calm',\n background: {\n type: 'gradient',\n stops: [\n { at: 0, color: '#1b1d38' },\n { at: 0.45, color: '#3a2645' },\n { at: 0.8, color: '#7d3d4e' },\n { at: 1, color: '#0f140d' },\n ],\n band: { color: '#0c140d', height: 44 },\n },\n layers: [\n {\n count: 60,\n sprite: { kind: 'circle', radius: [0.6, 1.6], color: '#ffe3ec', soft: true },\n alpha: [0.3, 0.9],\n blend: 'lighter',\n region: { y: [0, 0.72] },\n pulse: { amp: 0.22, period: 3000 },\n motion: { type: 'drift', speed: [0.3, 1.2], bob: 1 },\n },\n {\n count: 28,\n sprite: { kind: 'emoji', glyphs: ['🌸'] },\n size: [13, 20],\n alpha: [0.5, 0.8],\n motion: { type: 'drift', speed: [12, 26], angle: 100, bob: 3 },\n },\n {\n count: 18,\n sprite: { kind: 'emoji', glyphs: ['🌸'] },\n size: [20, 32],\n alpha: [0.7, 0.95],\n motion: { type: 'drift', speed: [24, 44], angle: 100, bob: 6 },\n },\n {\n count: 10,\n sprite: { kind: 'emoji', glyphs: ['🌸'] },\n size: [34, 54],\n alpha: [0.85, 1],\n motion: { type: 'drift', speed: [42, 68], angle: 100, bob: 11 },\n },\n {\n count: 16,\n sprite: { kind: 'circle', radius: [1.5, 4], color: '#ff9bb5', soft: true },\n alpha: [0.1, 0.3],\n blend: 'lighter',\n region: { y: [0.9, 1] },\n pulse: { amp: 0.15, period: 2600 },\n motion: { type: 'drift', speed: [0.3, 1.5], angle: 180, bob: 1 },\n },\n ],\n};\n","import type { SaverSpec } from '../types';\n\n/** Snowfall: three depth layers of white circles drifting down with horizontal bob. */\nexport const SNOWFALL_SPEC: SaverSpec = {\n schemaVersion: 1,\n id: 'snowfall',\n label: 'Snowfall',\n seed: 12,\n motionIntensity: 'calm',\n background: {\n type: 'gradient',\n stops: [\n { at: 0, color: '#0a1628' },\n { at: 0.7, color: '#0d1f3c' },\n { at: 1, color: '#121212' },\n ],\n },\n layers: [\n {\n count: 50,\n sprite: { kind: 'circle', radius: [1, 2.5], color: '#8899aa' },\n motion: { type: 'drift', angle: 90, speed: [15, 40], bob: 3 },\n },\n {\n count: 35,\n sprite: { kind: 'circle', radius: [2, 4.5], color: '#c0cdd8' },\n motion: { type: 'drift', angle: 85, speed: [35, 80], bob: 6 },\n },\n {\n count: 15,\n sprite: { kind: 'circle', radius: [3.5, 7], color: '#e8eff5' },\n motion: { type: 'drift', angle: 82, speed: [55, 120], bob: 10 },\n },\n ],\n};\n","import type { SaverSpec } from '../types';\nimport { AQUARIUM_SPEC } from './aquarium';\nimport { DASHBOARD_SPEC } from './dev-dashboard';\nimport { LANTERNS_SPEC } from './lanterns';\nimport { RAIN_SPEC } from './rain';\nimport { SAKURA_SPEC } from './sakura';\nimport { SNOWFALL_SPEC } from './snowfall';\n\nexport { AQUARIUM_SPEC } from './aquarium';\nexport { RAIN_SPEC } from './rain';\nexport { SNOWFALL_SPEC } from './snowfall';\nexport { LANTERNS_SPEC } from './lanterns';\nexport { SAKURA_SPEC } from './sakura';\nexport { DASHBOARD_SPEC } from './dev-dashboard';\n\n/** Catalog entry for a bundled schema example. */\nexport interface SchemaExample {\n id: string;\n label: string;\n spec: SaverSpec;\n}\n\n/** Ordered catalog of bundled schema examples. Add new specs as standalone files, then register here. */\nexport const SCHEMA_EXAMPLES: readonly SchemaExample[] = [\n { id: 'aquarium', label: 'Aquarium', spec: AQUARIUM_SPEC },\n { id: 'rain', label: 'Rain', spec: RAIN_SPEC },\n { id: 'snowfall', label: 'Snowfall', spec: SNOWFALL_SPEC },\n { id: 'lanterns', label: 'Night Lanterns', spec: LANTERNS_SPEC },\n { id: 'sakura', label: 'Sakura Drift', spec: SAKURA_SPEC },\n { id: 'dev-dashboard', label: 'Dev Dashboard', spec: DASHBOARD_SPEC },\n] as const;\n\n/** All example specs in catalog order (tests, batch compile). */\nexport const EXAMPLE_SPECS: SaverSpec[] = SCHEMA_EXAMPLES.map((e) => e.spec);\n\n/** Lookup by saver id. */\nexport const EXAMPLE_BY_ID: Readonly<Record<string, SaverSpec>> = Object.fromEntries(\n SCHEMA_EXAMPLES.map((e) => [e.id, e.spec]),\n);\n"],"mappings":";AAYO,IAAM,iBAAiB;AA8GvB,IAAM,SAAS;AAAA,EACpB,aAAa;AAAA,EACb,UAAU;AAAA,EACV,WAAW;AAAA,EACX,UAAU;AAAA;AAAA,EACV,aAAa;AAAA;AAAA,EACb,gBAAgB;AAAA;AAClB;;;AC/HA,IAAM,MAAM;AACZ,IAAM,QAAQ,CAAC,MAA6C,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC;AACnH,IAAM,QAAQ,CAAC,MAA4B,OAAO,MAAM;AACxD,IAAM,QAAQ,CAAC,MAA4B,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC;AACrF,IAAM,UAAU,CAAC,MACf,MAAM,QAAQ,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,EAAE,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC;AAM1E,SAAS,aAAa,MAAiC;AAC5D,QAAM,SAAsB,CAAC;AAC7B,QAAM,MAAM,CAAC,MAAc,YAA0B,KAAK,OAAO,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEvF,MAAI,CAAC,MAAM,IAAI,EAAG,QAAO,EAAE,OAAO,OAAO,QAAQ,CAAC,EAAE,MAAM,IAAI,SAAS,yBAAyB,CAAC,EAAE;AAEnG,MAAI,KAAK,kBAAkB,eAAgB,KAAI,iBAAiB,WAAW,cAAc,EAAE;AAC3F,MAAI,CAAC,MAAM,KAAK,EAAE,KAAK,KAAK,GAAG,KAAK,MAAM,GAAI,KAAI,MAAM,4BAA4B;AACpF,MAAI,CAAC,MAAM,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,GAAI,KAAI,SAAS,4BAA4B;AAC7F,MAAI,KAAK,SAAS,UAAa,CAAC,MAAM,KAAK,IAAI,EAAG,KAAI,QAAQ,kBAAkB;AAChF,MAAI,KAAK,oBAAoB,UAAa,CAAC,CAAC,QAAQ,YAAY,WAAW,EAAE,SAAS,KAAK,eAAyB,GAAG;AACrH,QAAI,mBAAmB,qCAAqC;AAAA,EAC9D;AAEA,MAAI,KAAK,eAAe,OAAW,oBAAmB,KAAK,YAAY,GAAG;AAE1E,MAAI,CAAC,MAAM,QAAQ,KAAK,MAAM,KAAK,KAAK,OAAO,WAAW,GAAG;AAC3D,QAAI,UAAU,2BAA2B;AAAA,EAC3C,OAAO;AACL,QAAI,KAAK,OAAO,SAAS,OAAO,UAAW,KAAI,UAAU,WAAW,OAAO,SAAS,SAAS;AAC7F,QAAI,QAAQ;AACZ,SAAK,OAAO,QAAQ,CAAC,OAAO,MAAM;AAChC,eAAS,MAAM,KAAK,KAAK,MAAM,MAAM,KAAK,IAAI,MAAM,QAAQ;AAC5D,oBAAc,OAAO,UAAU,CAAC,KAAK,GAAG;AAAA,IAC1C,CAAC;AACD,QAAI,QAAQ,OAAO,SAAU,KAAI,UAAU,kBAAkB,KAAK,gBAAgB,OAAO,QAAQ,EAAE;AAAA,EACrG;AAEA,SAAO,EAAE,OAAO,OAAO,WAAW,GAAG,OAAO;AAC9C;AAEA,SAAS,MAAM,GAAY,MAAc,KAA2C;AAClF,MAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,EAAG,KAAI,MAAM,mCAAmC;AAC9E;AAEA,SAAS,mBAAmB,IAAa,KAA2C;AAClF,MAAI,CAAC,MAAM,EAAE,EAAG,QAAO,IAAI,cAAc,mBAAmB;AAC5D,MAAI,GAAG,SAAS,SAAS;AACvB,UAAM,GAAG,OAAO,oBAAoB,GAAG;AAAA,EACzC,WAAW,GAAG,SAAS,YAAY;AACjC,QAAI,CAAC,MAAM,QAAQ,GAAG,KAAK,KAAK,GAAG,MAAM,SAAS,GAAG;AACnD,UAAI,oBAAoB,iCAAiC;AAAA,IAC3D,OAAO;AACL,SAAG,MAAM,QAAQ,CAAC,GAAG,MAAM;AACzB,YAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,EAAG,KAAI,oBAAoB,CAAC,QAAQ,cAAc;AACtG,cAAM,MAAM,CAAC,IAAI,EAAE,QAAQ,QAAW,oBAAoB,CAAC,WAAW,GAAG;AAAA,MAC3E,CAAC;AAAA,IACH;AACA,QAAI,GAAG,SAAS,QAAW;AACzB,UAAI,CAAC,MAAM,GAAG,IAAI,EAAG,KAAI,mBAAmB,mBAAmB;AAAA,WAC1D;AACH,cAAM,GAAG,KAAK,OAAO,yBAAyB,GAAG;AACjD,YAAI,CAAC,MAAM,GAAG,KAAK,MAAM,KAAK,GAAG,KAAK,UAAU,EAAG,KAAI,0BAA0B,aAAa;AAAA,MAChG;AAAA,IACF;AAAA,EACF,OAAO;AACL,QAAI,mBAAmB,0BAA0B;AAAA,EACnD;AACF;AAEA,SAAS,cAAc,OAAgB,MAAc,KAA2C;AAC9F,MAAI,CAAC,MAAM,KAAK,EAAG,QAAO,IAAI,MAAM,mBAAmB;AACvD,MAAI,CAAC,MAAM,MAAM,KAAK,KAAK,CAAC,OAAO,UAAU,MAAM,KAAK,KAAK,MAAM,QAAQ,GAAG;AAC5E,QAAI,GAAG,IAAI,UAAU,4BAA4B;AAAA,EACnD,WAAW,MAAM,QAAQ,OAAO,aAAa;AAC3C,QAAI,GAAG,IAAI,UAAU,WAAW,OAAO,WAAW,YAAY;AAAA,EAChE;AACA,MAAI,MAAM,SAAS,WAAc,CAAC,QAAQ,MAAM,IAAI,KAAK,MAAM,KAAK,CAAC,KAAK,IAAI;AAC5E,QAAI,GAAG,IAAI,SAAS,0CAA0C;AAAA,EAChE;AACA,MAAI,MAAM,SAAS,UAAa,OAAO,MAAM,SAAS,UAAW,KAAI,GAAG,IAAI,SAAS,mBAAmB;AACxG,MAAI,MAAM,SAAS,UAAa,OAAO,MAAM,SAAS,UAAW,KAAI,GAAG,IAAI,SAAS,mBAAmB;AAExG,MAAI,MAAM,UAAU,WAAc,CAAC,QAAQ,MAAM,KAAK,KAAK,MAAM,MAAM,CAAC,IAAI,KAAK,MAAM,MAAM,CAAC,IAAI,IAAI;AACpG,QAAI,GAAG,IAAI,UAAU,uCAAuC;AAAA,EAC9D;AACA,MAAI,MAAM,UAAU,UAAa,MAAM,UAAU,UAAW,KAAI,GAAG,IAAI,UAAU,4BAA4B;AAC7G,MAAI,MAAM,QAAQ,WAAc,CAAC,MAAM,MAAM,GAAG,KAAK,MAAM,IAAI,KAAK,MAAM,KAAK;AAC7E,QAAI,GAAG,IAAI,QAAQ,4BAA4B;AAAA,EACjD;AACA,MAAI,MAAM,aAAa,QAAW;AAChC,QAAI,CAAC,MAAM,MAAM,QAAQ,KAAK,CAAC,MAAM,MAAM,SAAS,CAAC,KAAK,CAAC,MAAM,MAAM,SAAS,CAAC,GAAG;AAClF,UAAI,GAAG,IAAI,aAAa,kCAAkC;AAAA,IAC5D,OAAO;AACL,UAAI,MAAM,SAAS,IAAI,KAAK,MAAM,SAAS,IAAI,EAAG,KAAI,GAAG,IAAI,eAAe,cAAc;AAC1F,UAAI,MAAM,SAAS,IAAI,KAAK,MAAM,SAAS,IAAI,EAAG,KAAI,GAAG,IAAI,eAAe,cAAc;AAC1F,UAAI,MAAM,MAAM,KAAK,KAAK,MAAM,UAAU,EAAG,KAAI,GAAG,IAAI,aAAa,4BAA4B;AAAA,IACnG;AAAA,EACF;AACA,MAAI,MAAM,WAAW,QAAW;AAC9B,QAAI,CAAC,MAAM,MAAM,MAAM,EAAG,KAAI,GAAG,IAAI,WAAW,mBAAmB;AAAA,SAC9D;AACH,iBAAW,QAAQ,CAAC,KAAK,GAAG,GAAY;AACtC,cAAM,IAAI,MAAM,OAAO,IAAI;AAC3B,YAAI,MAAM,WAAc,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,IAAI;AAC5D,cAAI,GAAG,IAAI,WAAW,IAAI,IAAI,uCAAuC;AAAA,QACvE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,MAAM,UAAU,QAAW;AAC7B,QAAI,CAAC,MAAM,MAAM,KAAK,EAAG,KAAI,GAAG,IAAI,UAAU,mBAAmB;AAAA,SAC5D;AACH,UAAI,CAAC,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,MAAM,OAAO,KAAK,MAAM,MAAM,MAAM,OAAO,aAAa;AAC3F,YAAI,GAAG,IAAI,cAAc,qBAAqB,OAAO,WAAW,EAAE;AAAA,MACpE;AACA,UAAI,CAAC,MAAM,MAAM,MAAM,MAAM,KAAK,MAAM,MAAM,SAAS,OAAO,gBAAgB;AAC5E,YAAI,GAAG,IAAI,iBAAiB,cAAc,OAAO,cAAc,0BAA0B;AAAA,MAC3F;AAAA,IACF;AAAA,EACF;AAEA,iBAAe,MAAM,QAAQ,GAAG,IAAI,WAAW,GAAG;AAClD,iBAAe,MAAM,QAAQ,GAAG,IAAI,WAAW,GAAG;AACpD;AAEA,SAAS,eAAe,QAAiB,MAAc,KAA2C;AAChG,MAAI,CAAC,MAAM,MAAM,EAAG,QAAO,IAAI,MAAM,mBAAmB;AACxD,MAAI,OAAO,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,QAAQ,OAAO,MAAM,KAAK,OAAO,OAAO,WAAW,KAAK,CAAC,OAAO,OAAO,MAAM,KAAK,GAAG;AAC9F,UAAI,GAAG,IAAI,WAAW,sCAAsC;AAAA,IAC9D;AAAA,EACF,WAAW,OAAO,SAAS,QAAQ;AACjC,QAAI,CAAC,MAAM,QAAQ,OAAO,OAAO,KAAK,OAAO,QAAQ,WAAW,KAAK,CAAC,OAAO,QAAQ,MAAM,KAAK,GAAG;AACjG,UAAI,GAAG,IAAI,YAAY,sCAAsC;AAAA,IAC/D;AACA,QAAI,OAAO,UAAU,OAAW,OAAM,OAAO,OAAO,GAAG,IAAI,UAAU,GAAG;AACxE,QAAI,OAAO,UAAU,UAAa,CAAC,CAAC,QAAQ,UAAU,OAAO,EAAE,SAAS,OAAO,KAAe,GAAG;AAC/F,UAAI,GAAG,IAAI,UAAU,+BAA+B;AAAA,IACtD;AACA,QAAI,OAAO,aAAa,UAAa,CAAC,CAAC,OAAO,UAAU,QAAQ,EAAE,SAAS,OAAO,QAAkB,GAAG;AACrG,UAAI,GAAG,IAAI,aAAa,+BAA+B;AAAA,IACzD;AACA,QAAI,OAAO,aAAa,WAAc,CAAC,MAAM,OAAO,QAAQ,KAAK,OAAO,YAAY,IAAI;AACtF,UAAI,GAAG,IAAI,aAAa,2BAA2B;AAAA,IACrD;AAAA,EACF,WAAW,OAAO,SAAS,UAAU;AACnC,QAAI,CAAC,QAAQ,OAAO,MAAM,KAAK,OAAO,OAAO,CAAC,KAAK,EAAG,KAAI,GAAG,IAAI,WAAW,0CAA0C;AACtH,UAAM,OAAO,OAAO,GAAG,IAAI,UAAU,GAAG;AACxC,QAAI,OAAO,SAAS,UAAa,OAAO,OAAO,SAAS,UAAW,KAAI,GAAG,IAAI,SAAS,mBAAmB;AAAA,EAC5G,OAAO;AACL,QAAI,GAAG,IAAI,SAAS,+BAA+B;AAAA,EACrD;AACF;AAEA,SAAS,eAAe,QAAiB,MAAc,KAA2C;AAChG,MAAI,CAAC,MAAM,MAAM,EAAG,QAAO,IAAI,MAAM,mBAAmB;AACxD,QAAM,UAAU,CAAC,GAAY,MAAoB;AAC/C,QAAI,CAAC,QAAQ,CAAC,EAAG,KAAI,GAAG,2BAA2B;AAAA,aAC1C,EAAE,CAAC,IAAI,OAAO,SAAU,KAAI,GAAG,qBAAqB,OAAO,QAAQ,SAAS;AAAA,EACvF;AACA,MAAI,OAAO,SAAS,SAAS;AAC3B,YAAQ,OAAO,OAAO,GAAG,IAAI,QAAQ;AACrC,QAAI,OAAO,UAAU,UAAa,CAAC,MAAM,OAAO,KAAK,EAAG,KAAI,GAAG,IAAI,UAAU,4BAA4B;AACzG,QAAI,OAAO,kBAAkB,UAAa,OAAO,OAAO,kBAAkB,UAAW,KAAI,GAAG,IAAI,kBAAkB,mBAAmB;AACrI,QAAI,OAAO,QAAQ,UAAa,CAAC,MAAM,OAAO,GAAG,EAAG,KAAI,GAAG,IAAI,QAAQ,kBAAkB;AAAA,EAC3F,WAAW,OAAO,SAAS,QAAQ;AACjC,YAAQ,OAAO,OAAO,GAAG,IAAI,QAAQ;AACrC,QAAI,OAAO,SAAS,UAAa,CAAC,MAAM,OAAO,IAAI,EAAG,KAAI,GAAG,IAAI,SAAS,kBAAkB;AAAA,EAC9F,WAAW,OAAO,SAAS,UAAU;AACnC,YAAQ,OAAO,OAAO,GAAG,IAAI,QAAQ;AAAA,EACvC,WAAW,OAAO,SAAS,UAAU;AAAA,EAErC,OAAO;AACL,QAAI,GAAG,IAAI,SAAS,wCAAwC;AAAA,EAC9D;AACF;AAGO,SAAS,gBAAgB,MAA0B;AACxD,QAAM,IAAI,aAAa,IAAI;AAC3B,MAAI,CAAC,EAAE,OAAO;AACZ,UAAM,IAAI,MAAM;AAAA,EAAwB,EAAE,OAAO,IAAI,CAAC,MAAM,KAAK,EAAE,QAAQ,QAAQ,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,EACnH;AACA,SAAO;AACT;;;AChKO,SAAS,eAAe,QAA4B;AACzD,MAAI,OAAO,SAAS,QAAS,QAAO,OAAO,OAAO;AAClD,MAAI,OAAO,SAAS,OAAQ,QAAO,OAAO,QAAQ;AAClD,SAAO;AACT;AAGO,SAAS,cAAc,OAAkB,KAAU,GAAW,GAAqB;AACxF,QAAM,CAAC,MAAM,IAAI,IAAI,MAAM,QAAQ,CAAC,IAAI,EAAE;AAC1C,QAAM,WAAW,eAAe,MAAM,MAAM;AAC5C,QAAM,MAAgB,CAAC;AACvB,WAAS,IAAI,GAAG,IAAI,MAAM,OAAO,KAAK;AACpC,UAAM,OACJ,MAAM,OAAO,SAAS,WAClB,IAAI,MAAM,MAAM,OAAO,OAAO,CAAC,GAAG,MAAM,OAAO,OAAO,CAAC,CAAC,IAAI,IAC5D,IAAI,MAAM,MAAM,IAAI;AAE1B,QAAI,KAAK;AACT,QAAI,KAAK;AACT,QAAI,MAAM;AACV,QAAI,SAA2B;AAC/B,UAAM,IAAI,MAAM;AAChB,QAAI,EAAE,SAAS,UAAU;AACvB,eAAS;AAAA,IACX,WAAW,EAAE,SAAS,SAAS;AAC7B,YAAM,IAAI,IAAI,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAC1C,UAAI,SAAU,EAAE,SAAS,KAAK,KAAK,KAAM;AACzC,UAAI,EAAE,iBAAiB,IAAI,KAAK,IAAI,IAAK,SAAQ,KAAK,KAAK;AAC3D,WAAK,IAAI,KAAK,IAAI,KAAK;AACvB,WAAK,IAAI,KAAK,IAAI,KAAK;AACvB,YAAM,EAAE,OAAO;AAAA,IACjB,WAAW,EAAE,SAAS,QAAQ;AAC5B,eAAS;AACT,WAAK,CAAC,IAAI,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AACtC,YAAM,EAAE,QAAQ;AAAA,IAClB,OAAO;AACL,eAAS;AACT,YAAM,IAAI,IAAI,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAC1C,YAAM,IAAI,IAAI,MAAM,GAAG,KAAK,KAAK,CAAC;AAClC,WAAK,IAAI,KAAK,IAAI,CAAC;AACnB,WAAK,IAAI,KAAK,IAAI,CAAC;AAAA,IACrB;AAEA,QAAI;AACJ,QAAI;AACJ,QAAI,MAAM,YAAY,MAAM,UAAU,GAAG;AACvC,WAAK,MAAM,SAAS,IAAI;AACxB,WAAK,MAAM,SAAS,IAAI;AAAA,IAC1B,OAAO;AAIL,YAAM,CAAC,KAAK,GAAG,IAAI,MAAM,QAAQ,KAAK,CAAC,GAAG,CAAC;AAC3C,YAAM,CAAC,KAAK,GAAG,IAAI,MAAM,QAAQ,KAAK,CAAC,GAAG,CAAC;AAC3C,WAAK,IAAI,MAAM,MAAM,GAAG,MAAM,CAAC;AAC/B,WAAK,IAAI,MAAM,MAAM,GAAG,MAAM,CAAC;AAAA,IACjC;AAEA,QAAI,KAAK;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,WAAW,IAAI,IAAI,IAAI,GAAG,WAAW,CAAC,IAAI;AAAA,MACvD,OAAO,IAAI,MAAM,GAAG,KAAK,KAAK,CAAC;AAAA,MAC/B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,KAAK;AAAA,MAClB,OAAO,MAAM,QAAQ,IAAI,MAAM,MAAM,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC,IAAI;AAAA,MACjE,UAAU,MAAM,OAAO,OAAO;AAAA,MAC9B,aAAa,MAAM,OAAO,UAAU;AAAA,MACpC,YAAY,MAAM,QAAQ,IAAI,MAAM,GAAG,KAAK,KAAK,CAAC,IAAI;AAAA,IACxD,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAGO,SAAS,QAAQ,GAAW,GAAmB;AACpD,MAAI,CAAC,EAAE,SAAU,QAAO,EAAE;AAC1B,QAAM,IAAI,EAAE,QAAQ,EAAE,WAAW,KAAK,IAAK,IAAI,IAAI,KAAK,KAAM,EAAE,cAAc,EAAE,UAAU;AAC1F,SAAO,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI;AACjC;AAEA,SAAS,KAAK,GAAW,KAAa,KAAqB;AACzD,QAAM,QAAQ,MAAM;AACpB,WAAW,IAAI,OAAO,QAAS,SAAS,QAAS;AACnD;AAEA,SAAS,QAAQ,GAAW,KAAa,KAAqB;AAC5D,QAAM,QAAQ,MAAM;AACpB,MAAI,SAAS,EAAG,QAAO;AACvB,QAAM,SAAS,IAAI;AACnB,QAAM,MAAO,IAAI,OAAO,SAAU,UAAU;AAC5C,SAAO,OAAO,IAAI,QAAQ,IAAI,SAAS;AACzC;AAGO,SAAS,WAAW,GAAW,GAAW,GAAW,GAAmB;AAC7E,MAAI,EAAE,WAAW,SAAU,QAAO,EAAE,GAAG,EAAE,IAAI,GAAG,EAAE,IAAI,MAAM,MAAM;AAClE,QAAM,KAAK,IAAI;AACf,QAAM,IAAI,EAAE;AACZ,MAAI,EAAE,WAAW,UAAU;AACzB,WAAO;AAAA,MACL,GAAG,QAAQ,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,CAAC;AAAA,MAC7C,GAAG,QAAQ,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,CAAC;AAAA,MAC7C,MAAM;AAAA,IACR;AAAA,EACF;AACA,MAAI,EAAE,WAAW,QAAQ;AACvB,WAAO;AAAA,MACL,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,IAAI,MAAM,EAAE,KAAK,IAAI;AAAA,MACzD,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;AAAA,MACnC,MAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,IAAI,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;AAC1C,MAAI,IAAI,EAAE,OAAO,IAAI,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE;AAC3D,MAAI,EAAE,IAAK,MAAK,EAAE,MAAM,KAAK,IAAI,IAAI,MAAM,EAAE,KAAK;AAClD,SAAO,EAAE,GAAG,GAAG,MAAM,EAAE,YAAY;AACrC;;;ACtJA;AAAA,EACE;AAAA,OAMK;;;ACOP,IAAM,cAAc,oBAAI,IAAI,CAAC,aAAa,eAAe,WAAW,CAAC;AAG9D,SAAS,gBAAgB,MAAe,MAAiC;AAC9E,MAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,CAAC,KAAM,QAAO;AACvD,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,MAAI,MAAM,KAAK,CAAC,MAAM,YAAY,IAAI,CAAC,CAAC,EAAG,QAAO;AAClD,QAAM,IAAI;AACV,MAAI,MAAM,CAAC,MAAM,YAAY,MAAM,CAAC,MAAM,gBAAgB,MAAM,QAAQ,EAAE,MAAM,GAAG;AACjF,UAAM,MAAM,EAAE,OAAO,UAAU,CAAC,MAAM,KAAK,EAAE,QAAQ,MAAM,CAAC,CAAC;AAC7D,QAAI,QAAQ,GAAI,QAAO;AACvB,UAAM,OAAO,GAAG,GAAG,UAAU,OAAO,GAAG,CAAC;AAAA,EAC1C;AACA,MAAI,OAAgB;AACpB,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK;AACzC,QAAI,SAAS,QAAQ,OAAO,SAAS,SAAU,QAAO;AACtD,WAAQ,KAAiC,MAAM,CAAC,CAAE;AAAA,EACpD;AACA,MAAI,SAAS,QAAQ,OAAO,SAAS,SAAU,QAAO;AACtD,QAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,QAAM,MAAuB,MAAM,QAAQ,IAAI,IAAI,OAAO,IAAI,IAAI;AAClE,QAAM,SAAS,MAAM,QAAQ,IAAI,IAC7B,OAAO,UAAU,GAAa,KAAM,OAAkB,KAAM,MAAiB,KAAK,SAClF,QAAS;AACb,SAAO,SAAS,EAAE,QAAQ,MAA8B,IAAI,IAAI;AAClE;AAYO,SAAS,kBAAkB,MAAiB,QAAiC;AAClF,QAAM,OAAO,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC;AAC5C,aAAW,KAAK,QAAQ;AACtB,UAAM,MAAM,gBAAgB,MAAM,EAAE,IAAI;AACxC,QAAI,IAAK,CAAC,IAAI,OAA4C,IAAI,GAAG,IAAI,EAAE;AAAA,EACzE;AACA,SAAO;AACT;AAEA,IAAMA,OAAM;AAEZ,SAAS,SAAS,KAAuC;AACvD,QAAM,IAAI,IAAI,WAAW,IAAI,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK;AACzF,QAAM,IAAI,SAAS,EAAE,MAAM,CAAC,GAAG,EAAE;AACjC,SAAO,CAAE,KAAK,KAAM,KAAM,KAAK,IAAK,KAAK,IAAI,GAAG;AAClD;AAEA,SAAS,QAAQ,GAAW,GAAW,GAAmB;AACxD,QAAM,KAAK,SAAS,CAAC;AACrB,QAAM,KAAK,SAAS,CAAC;AACrB,QAAM,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,KAAK,MAAM,KAAK,GAAG,CAAC,IAAK,KAAK,CAAC,CAAC;AAC7D,SAAO,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;AACrE;AAOO,SAAS,SAAS,MAAiB,IAAe,GAAsB;AAC7E,QAAM,KAAK,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;AACrC,QAAM,OAAO,CAAC,GAAY,GAAY,QAAmC;AACvE,QAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;AAClD,YAAM,IAAI,KAAK,IAAI,KAAK;AACxB,aAAO,QAAQ,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC,IAAI;AAAA,IACxD;AACA,QAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAYA,KAAI,KAAK,CAAC,KAAKA,KAAI,KAAK,CAAC,GAAG;AAChF,aAAO,QAAQ,GAAG,GAAG,EAAE;AAAA,IACzB;AACA,QAAI,MAAM,QAAQ,CAAC,KAAK,MAAM,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ;AACjE,aAAO,EAAE,IAAI,CAAC,IAAI,MAAM,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;AAAA,IAC3C;AACA,QAAI,KAAK,KAAK,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,GAAG;AACtG,YAAM,MAA+B,CAAC;AACtC,iBAAW,SAAS,OAAO,KAAK,CAA4B,GAAG;AAC7D,YAAI,KAAK,IAAI,KAAM,EAA8B,KAAK,GAAI,EAA8B,KAAK,GAAG,KAAK;AAAA,MACvG;AACA,aAAO;AAAA,IACT;AACA,WAAO,KAAK,IAAI,IAAI;AAAA,EACtB;AACA,SAAO,KAAK,MAAM,EAAE;AACtB;AAGO,SAAS,WAAW,GAAmB;AAC5C,QAAM,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;AACpC,SAAO,IAAI,KAAK,IAAI,IAAI;AAC1B;AAMO,SAAS,oBAAoB,MAAyB;AAC3D,SAAO,KAAK;AAAA,IACV,KAAK,OAAO,IAAI,CAAC,MAAM;AAAA,MACrB,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE,OAAO;AAAA,MACT,EAAE,OAAO,SAAS,WAAW,EAAE,OAAO,SAAS;AAAA,MAC/C,EAAE,OAAO,SAAS,UACd,EAAE,OAAO,OAAO,SAChB,EAAE,OAAO,SAAS,SAChB,EAAE,OAAO,QAAQ,SACjB;AAAA,IACR,CAAC;AAAA,EACH;AACF;;;ADrHA,IAAM,oBAAoB;AAG1B,SAAS,UAAU,KAAa,OAAuB;AACrD,QAAM,IAAI,IAAI,WAAW,IAAI,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK;AACzF,QAAM,IAAI,SAAS,EAAE,MAAM,CAAC,GAAG,EAAE;AACjC,SAAO,QAAS,KAAK,KAAM,GAAG,IAAK,KAAK,IAAK,GAAG,IAAI,IAAI,GAAG,IAAI,KAAK;AACtE;AAGO,SAAS,YAAY,MAAgC;AAC1D,QAAM,QAAQ,KAAK,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,OAAO,CAAC;AACzD,QAAM,WAAW,QAAQ,KAAK,SAAS,QAAQ,MAAM,QAAQ,QAAQ,MAAM,WAAW;AACtF,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,OAAO,KAAK;AAAA,IACZ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ;AAAA,IACA,iBAAiB,KAAK,mBAAmB;AAAA,IACzC,uBAAuB;AAAA;AAAA;AAAA,IAGvB,MAAM,EAAE,WAAW,KAAK;AAAA,IACxB,aAAa;AAAA,EACf;AACF;AAOA,IAAM,eAAN,MAA4C;AAAA,EAoB1C,YACmB,MACjB,KACA;AAFiB;AAGjB,SAAK,UAAU;AACf,SAAK,WAAW;AAChB,SAAK,QAAS,KAAK,QAAQ,IAAI,UAAU,KAAM;AAC/C,QAAI;AACJ,QAAI,IAAI,SAAS;AACf,eAAS,IAAI;AAAA,IACf,OAAO;AACL,YAAM,KAAK,SAAS,cAAc,QAAQ;AAC1C,SAAG,MAAM,UAAU;AACnB,SAAG,aAAa,eAAe,MAAM;AACrC,UAAI,KAAK,YAAY,EAAE;AACvB,eAAS;AAAA,IACX;AACA,SAAK,SAAS;AACd,UAAM,MAAM,OAAO,WAAW,MAAM,EAAE,OAAO,MAAM,CAAC;AACpD,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,6BAA6B;AACvD,SAAK,MAAM;AAEX,SAAK,IAAI,IAAI;AACb,SAAK,IAAI,IAAI;AACb,SAAK,WAAW;AAChB,SAAK,QAAQ;AAEb,SAAK,SAAS,IAAI;AAClB,QAAI,KAAK,OAAQ,MAAK,YAAY,GAAG,KAAK,IAAI;AAAA,QACzC,MAAK,MAAM;AAAA,EAClB;AAAA,EA7BmB;AAAA,EApBF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT;AAAA,EACA;AAAA,EACA,SAAkB,CAAC;AAAA,EACnB,UAAyB;AAAA,EACzB,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA;AAAA,EACR,QAAQ;AAAA;AAAA,EAGR;AAAA;AAAA,EAEA,aAAqF;AAAA,EACrF,iBAAiB;AAAA,EAkCjB,aAAmB;AACzB,UAAM,MAAM,KAAK,IAAI,KAAK,SAAS,KAAK,CAAC;AACzC,SAAK,OAAO,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC;AACxD,SAAK,OAAO,SAAS,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC;AACzD,SAAK,IAAI,aAAa,KAAK,GAAG,GAAG,KAAK,GAAG,CAAC;AAAA,EAC5C;AAAA;AAAA,EAGQ,UAAgB;AACtB,UAAM,MAAM,UAAU,KAAK,IAAI;AAC/B,SAAK,SAAS,KAAK,QAAQ,OAAO,IAAI,CAAC,WAAW,EAAE,OAAO,UAAU,cAAc,OAAO,KAAK,KAAK,GAAG,KAAK,CAAC,EAAE,EAAE;AACjH,SAAK,iBAAiB,oBAAoB,KAAK,OAAO;AAAA,EACxD;AAAA,EAEQ,QAAc;AACpB,QAAI,KAAK,YAAY,QAAQ,OAAO,0BAA0B,YAAa;AAC3E,SAAK,SAAS;AACd,SAAK,UAAU,sBAAsB,CAAC,QAAQ,KAAK,KAAK,GAAG,CAAC;AAAA,EAC9D;AAAA,EAEQ,OAAa;AACnB,QAAI,KAAK,YAAY,MAAM;AACzB,2BAAqB,KAAK,OAAO;AACjC,WAAK,UAAU;AAAA,IACjB;AAEA,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEQ,KAAK,KAAmB;AAC9B,SAAK,UAAU,sBAAsB,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC;AACxD,QAAI,KAAK,WAAW,EAAG,MAAK,SAAS;AACrC,SAAK,QAAQ,MAAM,KAAK,SAAS,KAAK;AACtC,SAAK,YAAY,KAAK,OAAO,KAAK,IAAI;AAAA,EACxC;AAAA,EAEQ,iBAAuB;AAC7B,UAAM,EAAE,KAAK,GAAG,EAAE,IAAI;AACtB,UAAM,KAAK,KAAK,QAAQ;AACxB,QAAI,CAAC,MAAM,GAAG,SAAS,SAAS;AAC9B,UAAI,YAAY,IAAI,SAAS;AAC7B,UAAI,SAAS,GAAG,GAAG,GAAG,CAAC;AACvB;AAAA,IACF;AACA,UAAM,IAAI,IAAI,qBAAqB,GAAG,GAAG,GAAG,CAAC;AAC7C,eAAW,KAAK,GAAG,MAAO,GAAE,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK;AAChF,QAAI,YAAY;AAChB,QAAI,SAAS,GAAG,GAAG,GAAG,CAAC;AACvB,QAAI,GAAG,MAAM;AACX,UAAI,YAAY,GAAG,KAAK;AACxB,UAAI,SAAS,GAAG,IAAI,GAAG,KAAK,QAAQ,GAAG,GAAG,KAAK,MAAM;AAAA,IACvD;AAAA,EACF;AAAA,EAEQ,WAAW,OAAc,GAAW,GAAiB;AAC3D,UAAM,EAAE,IAAI,IAAI;AAChB,UAAM,IAAI,WAAW,GAAG,GAAG,KAAK,GAAG,KAAK,CAAC;AACzC,UAAM,SAAS,MAAM,MAAM;AAC3B,QAAI,cAAc,QAAQ,GAAG,CAAC;AAC9B,QAAI,OAAO,SAAS,UAAU;AAC5B,YAAM,IAAI,EAAE,OAAO;AACnB,UAAI,OAAO,MAAM;AAEf,cAAM,IAAI,IAAI,qBAAqB,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;AAC3D,UAAE,aAAa,GAAG,OAAO,KAAK;AAC9B,UAAE,aAAa,MAAM,UAAU,OAAO,OAAO,IAAI,CAAC;AAClD,UAAE,aAAa,GAAG,UAAU,OAAO,OAAO,CAAC,CAAC;AAC5C,YAAI,YAAY;AAAA,MAClB,OAAO;AACL,YAAI,YAAY,OAAO;AAAA,MACzB;AACA,UAAI,UAAU;AACd,UAAI,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,KAAK,KAAK,CAAC;AACnC,UAAI,KAAK;AACT;AAAA,IACF;AACA,QAAI,KAAK;AACT,QAAI,UAAU,EAAE,GAAG,EAAE,CAAC;AACtB,QAAI,EAAE,QAAQ,MAAM,MAAM,KAAM,KAAI,MAAM,IAAI,CAAC;AAC/C,QAAI,YAAY;AAChB,QAAI,eAAe;AACnB,QAAI,OAAO,SAAS,SAAS;AAC3B,UAAI,OAAO,GAAG,EAAE,IAAI;AACpB,UAAI,SAAS,OAAO,OAAO,EAAE,WAAW,KAAK,OAAO,OAAO,CAAC,GAAI,GAAG,CAAC;AAAA,IACtE,OAAO;AACL,UAAI,YAAY,OAAO,SAAS;AAChC,UAAI,eAAe,OAAO,YAAY;AACtC,UAAI,OAAO,OAAO,QAAQ,GAAG,EAAE,IAAI;AACnC,UAAI,YAAY,OAAO,SAAS;AAChC,YAAM,OAAO,OAAO,QAAQ,EAAE,WAAW,KAAK,OAAO,QAAQ,CAAC;AAC9D,UAAI,OAAO,SAAU,KAAI,SAAS,MAAM,GAAG,GAAG,OAAO,QAAQ;AAAA,UACxD,KAAI,SAAS,MAAM,GAAG,CAAC;AAAA,IAC9B;AACA,QAAI,QAAQ;AAAA,EACd;AAAA;AAAA,EAGQ,eAAe,GAAiB;AACtC,UAAM,KAAK,KAAK;AAChB,QAAI,CAAC,GAAI;AACT,UAAM,IAAI,GAAG,OAAO,IAAI,KAAK,IAAI,GAAG,UAAU,GAAG;AACjD,SAAK,UAAU,KAAK,IAAI,GAAG,KAAK,SAAS,GAAG,MAAM,GAAG,IAAI,WAAW,CAAC,CAAC;AACtE,QAAI,KAAK,EAAG,MAAK,aAAa;AAG9B,UAAM,MAAM,oBAAoB,KAAK,OAAO;AAC5C,QAAI,QAAQ,KAAK,eAAgB,MAAK,QAAQ;AAAA,QACzC,MAAK,OAAO,QAAQ,CAAC,GAAG,MAAM;AAAE,QAAE,QAAQ,KAAK,QAAQ,OAAO,CAAC,KAAK,EAAE;AAAA,IAAO,CAAC;AAAA,EACrF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAW,OAA2B;AACpC,UAAM,SAAU,OAAO,UAAU,CAAC;AAClC,UAAM,SAAS,kBAAkB,KAAK,SAAS,MAAM;AACrD,QAAI,CAAC,aAAa,MAAM,EAAE,MAAO;AACjC,UAAM,MAAM,OAAO,SACf,OAAO,OAAO,CAAC,GAAG,MAAM,KAAK,IAAI,GAAG,EAAE,OAAO,iBAAiB,GAAG,CAAC,IAClE;AACJ,SAAK,aAAa;AAAA,MAChB,MAAM,KAAK,MAAM,KAAK,UAAU,KAAK,OAAO,CAAC;AAAA,MAC7C,IAAI;AAAA,MACJ,QAAQ,KAAK;AAAA,MACb;AAAA,IACF;AACA,QAAI,KAAK,QAAQ;AAEf,WAAK,aAAa;AAClB,WAAK,UAAU;AACf,UAAI,oBAAoB,KAAK,OAAO,MAAM,KAAK,eAAgB,MAAK,QAAQ;AAAA,UACvE,MAAK,OAAO,QAAQ,CAAC,GAAG,MAAM;AAAE,UAAE,QAAQ,KAAK,QAAQ,OAAO,CAAC,KAAK,EAAE;AAAA,MAAO,CAAC;AACnF,WAAK,YAAY,KAAK,OAAO,KAAK,IAAI;AAAA,IACxC;AAAA,EACF;AAAA;AAAA,EAGA,YAAY,GAAW,OAAqB;AAC1C,SAAK,eAAe,CAAC;AACrB,UAAM,EAAE,IAAI,IAAI;AAChB,QAAI,cAAc;AAClB,QAAI,2BAA2B;AAC/B,SAAK,eAAe;AACpB,eAAW,SAAS,KAAK,QAAQ;AAC/B,UAAI,2BAA2B,MAAM,MAAM,SAAS;AACpD,iBAAW,KAAK,MAAM,SAAU,MAAK,WAAW,OAAO,GAAG,CAAC;AAAA,IAC7D;AACA,QAAI,cAAc;AAClB,QAAI,2BAA2B;AAAA,EACjC;AAAA,EAEA,UAAU,QAAuB;AAC/B,SAAK,SAAS;AACd,QAAI,QAAQ;AACV,WAAK,KAAK;AAAA,IACZ,OAAO;AACL,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AAAA,EAEA,OAAO,OAAe,QAAgB,KAAoB;AACxD,SAAK,IAAI;AACT,SAAK,IAAI;AACT,QAAI,QAAQ,OAAW,MAAK,SAAS,MAAM;AAC3C,SAAK,WAAW;AAChB,SAAK,QAAQ;AACb,QAAI,KAAK,OAAQ,MAAK,YAAY,GAAG,KAAK,IAAI;AAAA,EAChD;AAAA,EAEA,UAAgB;AACd,SAAK,KAAK;AACV,QAAI,OAAO,sBAAsB,eAAe,KAAK,kBAAkB,kBAAmB,MAAK,OAAO,OAAO;AAAA,EAC/G;AACF;AAOO,SAAS,aAAa,MAA4B;AACvD,QAAM,QAAQ,gBAAgB,IAAI;AAClC,SAAO;AAAA,IACL,UAAU,YAAY,KAAK;AAAA,IAC3B,OAAO,CAAC,QAAsB,IAAI,aAAa,OAAO,GAAG;AAAA,IACzD,MAAM;AAAA,EACR;AACF;;;AE7RO,IAAM,gBAA2B;AAAA,EACtC,eAAe;AAAA,EACf,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,YAAY;AAAA,IACV,MAAM;AAAA,IACN,OAAO;AAAA,MACL,EAAE,IAAI,GAAG,OAAO,UAAU;AAAA,MAC1B,EAAE,IAAI,KAAK,OAAO,UAAU;AAAA,MAC5B,EAAE,IAAI,GAAG,OAAO,UAAU;AAAA,IAC5B;AAAA,IACA,MAAM,EAAE,OAAO,WAAW,QAAQ,GAAG;AAAA,EACvC;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,SAAS,QAAQ,CAAC,aAAM,aAAM,aAAM,aAAM,WAAI,EAAE;AAAA,MAChE,MAAM,CAAC,IAAI,EAAE;AAAA,MACb,QAAQ,EAAE,MAAM,SAAS,OAAO,CAAC,IAAI,GAAG,GAAG,eAAe,MAAM,KAAK,EAAE;AAAA,MACvE,MAAM;AAAA,IACR;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,UAAU,QAAQ,CAAC,GAAG,CAAC,GAAG,OAAO,UAAU;AAAA,MAC3D,QAAQ,EAAE,MAAM,QAAQ,OAAO,CAAC,IAAI,EAAE,GAAG,MAAM,EAAE;AAAA,IACnD;AAAA,EACF;AACF;;;AC5BO,IAAM,iBAA4B;AAAA,EACvC,eAAe;AAAA,EACf,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,YAAY;AAAA,IACV,MAAM;AAAA,IACN,OAAO;AAAA,MACL,EAAE,IAAI,GAAG,OAAO,UAAU;AAAA,MAC1B,EAAE,IAAI,KAAK,OAAO,UAAU;AAAA,MAC5B,EAAE,IAAI,GAAG,OAAO,UAAU;AAAA,IAC5B;AAAA,EACF;AAAA,EACA,QAAQ;AAAA;AAAA,IAEN;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,UAAU,QAAQ,CAAC,KAAK,GAAG,GAAG,OAAO,UAAU;AAAA,MAC/D,OAAO,CAAC,KAAK,IAAI;AAAA,MACjB,QAAQ,EAAE,MAAM,SAAS,OAAO,CAAC,KAAK,GAAG,GAAG,KAAK,IAAI;AAAA,IACvD;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,UAAU,QAAQ,CAAC,GAAG,GAAG,GAAG,OAAO,WAAW,MAAM,KAAK;AAAA,MACzE,OAAO,CAAC,MAAM,IAAI;AAAA,MAClB,OAAO;AAAA,MACP,OAAO,EAAE,KAAK,MAAM,QAAQ,IAAK;AAAA,MACjC,QAAQ,EAAE,MAAM,SAAS,OAAO,CAAC,KAAK,GAAG,GAAG,KAAK,IAAI;AAAA,IACvD;AAAA;AAAA,IAEA;AAAA,MACE,KAAK;AAAA,MAAS,OAAO;AAAA,MACrB,UAAU,EAAE,GAAG,KAAK,GAAG,KAAK;AAAA,MAC5B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,WAAW;AAAA,QAAG,OAAO;AAAA,QACrD,MAAM;AAAA,QAAiC,OAAO;AAAA,MAAS;AAAA,MACzD,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAU,OAAO;AAAA,MACtB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,eAAU;AAAA,QAAG,OAAO;AAAA,QACpD,MAAM;AAAA,QAAuB,OAAO;AAAA,MAAQ;AAAA,MAC9C,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAU,OAAO;AAAA,MACtB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,WAAW;AAAA,QAAG,OAAO;AAAA,QACrD,MAAM;AAAA,QAAkB,OAAO;AAAA,MAAO;AAAA,MACxC,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA;AAAA,IAEA;AAAA,MACE,KAAK;AAAA,MAAe,OAAO;AAAA,MAC3B,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,YAAY;AAAA,QAAG,OAAO;AAAA,QACtD,MAAM;AAAA,QAAiB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MACxD,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAe,OAAO;AAAA,MAC3B,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,KAAK;AAAA,QAAG,OAAO;AAAA,QAC/C,MAAM;AAAA,QAAuB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MAC9D,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAe,OAAO;AAAA,MAC3B,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,OAAO;AAAA,QAAG,OAAO;AAAA,QACjD,MAAM;AAAA,QAAiB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MACxD,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAe,OAAO;AAAA,MAC3B,UAAU,EAAE,GAAG,MAAM,GAAG,IAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,IAAI;AAAA,QAAG,OAAO;AAAA,QAC9C,MAAM;AAAA,QAAuB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MAC9D,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAe,OAAO;AAAA,MAC3B,UAAU,EAAE,GAAG,MAAM,GAAG,IAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,OAAO;AAAA,QAAG,OAAO;AAAA,QACjD,MAAM;AAAA,QAAiB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MACxD,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAe,OAAO;AAAA,MAC3B,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,IAAI;AAAA,QAAG,OAAO;AAAA,QAC9C,MAAM;AAAA,QAAuB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MAC9D,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAgB,OAAO;AAAA,MAC5B,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,QAAQ;AAAA,QAAG,OAAO;AAAA,QAClD,MAAM;AAAA,QAAiB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MACxD,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAgB,OAAO;AAAA,MAC5B,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,GAAG;AAAA,QAAG,OAAO;AAAA,QAC7C,MAAM;AAAA,QAAuB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MAC9D,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA;AAAA,IAEA;AAAA,MACE,KAAK;AAAA,MAAa,OAAO;AAAA,MACzB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,KAAK;AAAA,QAAG,OAAO;AAAA,QAC/C,MAAM;AAAA,QAAiB,OAAO;AAAA,MAAS;AAAA,MACzC,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAa,OAAO;AAAA,MACzB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,KAAK;AAAA,QAAG,OAAO;AAAA,QAC/C,MAAM;AAAA,QAAuB,OAAO;AAAA,MAAS;AAAA,MAC/C,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAa,OAAO;AAAA,MACzB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,QAAQ;AAAA,QAAG,OAAO;AAAA,QAClD,MAAM;AAAA,QAAiB,OAAO;AAAA,MAAS;AAAA,MACzC,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAa,OAAO;AAAA,MACzB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,QAAQ;AAAA,QAAG,OAAO;AAAA,QAClD,MAAM;AAAA,QAAuB,OAAO;AAAA,MAAS;AAAA,MAC/C,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAgB,OAAO;AAAA,MAC5B,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,QAAQ;AAAA,QAAG,OAAO;AAAA,QAClD,MAAM;AAAA,QAAiB,OAAO;AAAA,MAAS;AAAA,MACzC,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAgB,OAAO;AAAA,MAC5B,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,MAAM;AAAA,QAAG,OAAO;AAAA,QAChD,MAAM;AAAA,QAAuB,OAAO;AAAA,MAAS;AAAA,MAC/C,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA;AAAA,IAEA;AAAA,MACE,KAAK;AAAA,MAAe,OAAO;AAAA,MAC3B,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,cAAc;AAAA,QAAG,OAAO;AAAA,QACxD,MAAM;AAAA,QAAiB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MACxD,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAU,OAAO;AAAA,MACtB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,eAAe;AAAA,QAAG,OAAO;AAAA,QACzD,MAAM;AAAA,QAAkB,OAAO;AAAA,QAAQ,UAAU;AAAA,QAAO,UAAU;AAAA,MAAI;AAAA,MACxE,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAU,OAAO;AAAA,MACtB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,kBAAkB;AAAA,QAAG,OAAO;AAAA,QAC5D,MAAM;AAAA,QAAkB,OAAO;AAAA,QAAQ,UAAU;AAAA,QAAO,UAAU;AAAA,MAAI;AAAA,MACxE,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAU,OAAO;AAAA,MACtB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,4BAA4B;AAAA,QAAG,OAAO;AAAA,QACtE,MAAM;AAAA,QAAkB,OAAO;AAAA,QAAQ,UAAU;AAAA,QAAO,UAAU;AAAA,MAAI;AAAA,MACxE,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAU,OAAO;AAAA,MACtB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,wBAAwB;AAAA,QAAG,OAAO;AAAA,QAClE,MAAM;AAAA,QAAkB,OAAO;AAAA,QAAQ,UAAU;AAAA,QAAO,UAAU;AAAA,MAAI;AAAA,MACxE,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAU,OAAO;AAAA,MACtB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,wBAAwB;AAAA,QAAG,OAAO;AAAA,QAClE,MAAM;AAAA,QAAkB,OAAO;AAAA,QAAQ,UAAU;AAAA,QAAO,UAAU;AAAA,MAAI;AAAA,MACxE,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA;AAAA,IAEA;AAAA,MACE,KAAK;AAAA,MAAiB,OAAO;AAAA,MAC7B,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,SAAS;AAAA,QAAG,OAAO;AAAA,QACnD,MAAM;AAAA,QAAiB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MACxD,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAiB,OAAO;AAAA,MAC7B,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,iBAAiB;AAAA,QAAG,OAAO;AAAA,QAC3D,MAAM;AAAA,QAAkB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MACzD,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAgB,OAAO;AAAA,MAC5B,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,oBAAoB;AAAA,QAAG,OAAO;AAAA,QAC9D,MAAM;AAAA,QAAkB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MACzD,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA;AAAA,IAEA;AAAA,MACE,KAAK;AAAA,MAAa,OAAO;AAAA,MACzB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,WAAW;AAAA,QAAG,OAAO;AAAA,QACrD,MAAM;AAAA,QAAiB,OAAO;AAAA,QAAQ,UAAU;AAAA,MAAM;AAAA,MACxD,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAS,OAAO;AAAA,MACrB,UAAU,EAAE,GAAG,MAAM,GAAG,IAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,+BAA+B;AAAA,QAAG,OAAO;AAAA,QACzE,MAAM;AAAA,QAAkB,OAAO;AAAA,QAAQ,UAAU;AAAA,QAAO,UAAU;AAAA,MAAI;AAAA,MACxE,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAS,OAAO;AAAA,MACrB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,0CAAqC;AAAA,QAAG,OAAO;AAAA,QAC/E,MAAM;AAAA,QAAkB,OAAO;AAAA,QAAQ,UAAU;AAAA,QAAO,UAAU;AAAA,MAAI;AAAA,MACxE,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAS,OAAO;AAAA,MACrB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,mCAAmC;AAAA,QAAG,OAAO;AAAA,QAC7E,MAAM;AAAA,QAAkB,OAAO;AAAA,QAAQ,UAAU;AAAA,QAAO,UAAU;AAAA,MAAI;AAAA,MACxE,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAS,OAAO;AAAA,MACrB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,8CAA8C;AAAA,QAAG,OAAO;AAAA,QACxF,MAAM;AAAA,QAAkB,OAAO;AAAA,QAAQ,UAAU;AAAA,QAAO,UAAU;AAAA,MAAI;AAAA,MACxE,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MAAS,OAAO;AAAA,MACrB,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,MAC7B,QAAQ;AAAA,QAAE,MAAM;AAAA,QAAQ,SAAS,CAAC,2CAAsC;AAAA,QAAG,OAAO;AAAA,QAChF,MAAM;AAAA,QAAkB,OAAO;AAAA,QAAQ,UAAU;AAAA,QAAO,UAAU;AAAA,MAAI;AAAA,MACxE,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,EACF;AACF;;;AClQO,IAAM,gBAA2B;AAAA,EACtC,eAAe;AAAA,EACf,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,YAAY;AAAA,IACV,MAAM;AAAA,IACN,OAAO;AAAA,MACL,EAAE,IAAI,GAAG,OAAO,UAAU;AAAA,MAC1B,EAAE,IAAI,MAAM,OAAO,UAAU;AAAA,MAC7B,EAAE,IAAI,MAAM,OAAO,UAAU;AAAA,MAC7B,EAAE,IAAI,GAAG,OAAO,UAAU;AAAA,IAC5B;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,UAAU,QAAQ,CAAC,KAAK,GAAG,GAAG,OAAO,UAAU;AAAA,MAC/D,OAAO,CAAC,MAAM,CAAC;AAAA,MACf,QAAQ,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE;AAAA,MACvB,QAAQ,EAAE,MAAM,SAAS,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,EAAE;AAAA,IACnD;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,QAAQ,CAAC,KAAK,CAAC;AAAA,QACf,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,MACA,OAAO,CAAC,KAAK,GAAG;AAAA,MAChB,OAAO;AAAA,MACP,OAAO,EAAE,KAAK,MAAM,QAAQ,KAAK;AAAA,MACjC,QAAQ,EAAE,MAAM,QAAQ,OAAO,CAAC,GAAG,EAAE,GAAG,MAAM,EAAE;AAAA,IAClD;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,QAAQ,CAAC,KAAK,GAAG;AAAA,QACjB,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,MACA,OAAO,CAAC,KAAK,CAAC;AAAA,MACd,OAAO;AAAA,MACP,OAAO,EAAE,KAAK,MAAM,QAAQ,KAAK;AAAA,MACjC,QAAQ,EAAE,MAAM,QAAQ,OAAO,CAAC,IAAI,EAAE,GAAG,MAAM,EAAE;AAAA,IACnD;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,SAAS,QAAQ,CAAC,WAAI,EAAE;AAAA,MACxC,MAAM,CAAC,IAAI,EAAE;AAAA,MACb,OAAO,CAAC,MAAM,CAAC;AAAA,MACf,QAAQ,EAAE,MAAM,QAAQ,OAAO,CAAC,IAAI,EAAE,GAAG,MAAM,GAAG;AAAA,IACpD;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,SAAS,QAAQ,CAAC,WAAI,EAAE;AAAA,MACxC,MAAM,CAAC,IAAI,EAAE;AAAA,MACb,QAAQ,EAAE,MAAM,QAAQ,OAAO,CAAC,IAAI,EAAE,GAAG,MAAM,GAAG;AAAA,IACpD;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,UAAU,QAAQ,CAAC,GAAG,CAAC,GAAG,OAAO,WAAW,MAAM,KAAK;AAAA,MACvE,OAAO,CAAC,MAAM,IAAI;AAAA,MAClB,OAAO;AAAA,MACP,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE;AAAA,MACvB,OAAO,EAAE,KAAK,KAAK,QAAQ,KAAK;AAAA,MAChC,QAAQ,EAAE,MAAM,SAAS,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,EAAE;AAAA,IACjD;AAAA,EACF;AACF;;;AChFO,IAAM,YAAuB;AAAA,EAClC,eAAe;AAAA,EACf,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,YAAY,EAAE,MAAM,SAAS,OAAO,UAAU;AAAA,EAC9C,QAAQ;AAAA,IACN;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,QAAQ,SAAS,CAAC,QAAG,GAAG,OAAO,UAAU;AAAA,MACzD,MAAM,CAAC,IAAI,EAAE;AAAA,MACb,QAAQ,EAAE,MAAM,SAAS,OAAO,IAAI,OAAO,CAAC,KAAK,GAAG,EAAE;AAAA,IACxD;AAAA,EACF;AACF;;;ACLO,IAAM,cAAyB;AAAA,EACpC,eAAe;AAAA,EACf,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,YAAY;AAAA,IACV,MAAM;AAAA,IACN,OAAO;AAAA,MACL,EAAE,IAAI,GAAG,OAAO,UAAU;AAAA,MAC1B,EAAE,IAAI,MAAM,OAAO,UAAU;AAAA,MAC7B,EAAE,IAAI,KAAK,OAAO,UAAU;AAAA,MAC5B,EAAE,IAAI,GAAG,OAAO,UAAU;AAAA,IAC5B;AAAA,IACA,MAAM,EAAE,OAAO,WAAW,QAAQ,GAAG;AAAA,EACvC;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,UAAU,QAAQ,CAAC,KAAK,GAAG,GAAG,OAAO,WAAW,MAAM,KAAK;AAAA,MAC3E,OAAO,CAAC,KAAK,GAAG;AAAA,MAChB,OAAO;AAAA,MACP,QAAQ,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE;AAAA,MACvB,OAAO,EAAE,KAAK,MAAM,QAAQ,IAAK;AAAA,MACjC,QAAQ,EAAE,MAAM,SAAS,OAAO,CAAC,KAAK,GAAG,GAAG,KAAK,EAAE;AAAA,IACrD;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,SAAS,QAAQ,CAAC,WAAI,EAAE;AAAA,MACxC,MAAM,CAAC,IAAI,EAAE;AAAA,MACb,OAAO,CAAC,KAAK,GAAG;AAAA,MAChB,QAAQ,EAAE,MAAM,SAAS,OAAO,CAAC,IAAI,EAAE,GAAG,OAAO,KAAK,KAAK,EAAE;AAAA,IAC/D;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,SAAS,QAAQ,CAAC,WAAI,EAAE;AAAA,MACxC,MAAM,CAAC,IAAI,EAAE;AAAA,MACb,OAAO,CAAC,KAAK,IAAI;AAAA,MACjB,QAAQ,EAAE,MAAM,SAAS,OAAO,CAAC,IAAI,EAAE,GAAG,OAAO,KAAK,KAAK,EAAE;AAAA,IAC/D;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,SAAS,QAAQ,CAAC,WAAI,EAAE;AAAA,MACxC,MAAM,CAAC,IAAI,EAAE;AAAA,MACb,OAAO,CAAC,MAAM,CAAC;AAAA,MACf,QAAQ,EAAE,MAAM,SAAS,OAAO,CAAC,IAAI,EAAE,GAAG,OAAO,KAAK,KAAK,GAAG;AAAA,IAChE;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,UAAU,QAAQ,CAAC,KAAK,CAAC,GAAG,OAAO,WAAW,MAAM,KAAK;AAAA,MACzE,OAAO,CAAC,KAAK,GAAG;AAAA,MAChB,OAAO;AAAA,MACP,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE;AAAA,MACtB,OAAO,EAAE,KAAK,MAAM,QAAQ,KAAK;AAAA,MACjC,QAAQ,EAAE,MAAM,SAAS,OAAO,CAAC,KAAK,GAAG,GAAG,OAAO,KAAK,KAAK,EAAE;AAAA,IACjE;AAAA,EACF;AACF;;;ACnEO,IAAM,gBAA2B;AAAA,EACtC,eAAe;AAAA,EACf,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,YAAY;AAAA,IACV,MAAM;AAAA,IACN,OAAO;AAAA,MACL,EAAE,IAAI,GAAG,OAAO,UAAU;AAAA,MAC1B,EAAE,IAAI,KAAK,OAAO,UAAU;AAAA,MAC5B,EAAE,IAAI,GAAG,OAAO,UAAU;AAAA,IAC5B;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,UAAU,QAAQ,CAAC,GAAG,GAAG,GAAG,OAAO,UAAU;AAAA,MAC7D,QAAQ,EAAE,MAAM,SAAS,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,GAAG,KAAK,EAAE;AAAA,IAC9D;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,UAAU,QAAQ,CAAC,GAAG,GAAG,GAAG,OAAO,UAAU;AAAA,MAC7D,QAAQ,EAAE,MAAM,SAAS,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,GAAG,KAAK,EAAE;AAAA,IAC9D;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,QAAQ,EAAE,MAAM,UAAU,QAAQ,CAAC,KAAK,CAAC,GAAG,OAAO,UAAU;AAAA,MAC7D,QAAQ,EAAE,MAAM,SAAS,OAAO,IAAI,OAAO,CAAC,IAAI,GAAG,GAAG,KAAK,GAAG;AAAA,IAChE;AAAA,EACF;AACF;;;ACXO,IAAM,kBAA4C;AAAA,EACvD,EAAE,IAAI,YAAY,OAAO,YAAY,MAAM,cAAc;AAAA,EACzD,EAAE,IAAI,QAAQ,OAAO,QAAQ,MAAM,UAAU;AAAA,EAC7C,EAAE,IAAI,YAAY,OAAO,YAAY,MAAM,cAAc;AAAA,EACzD,EAAE,IAAI,YAAY,OAAO,kBAAkB,MAAM,cAAc;AAAA,EAC/D,EAAE,IAAI,UAAU,OAAO,gBAAgB,MAAM,YAAY;AAAA,EACzD,EAAE,IAAI,iBAAiB,OAAO,iBAAiB,MAAM,eAAe;AACtE;AAGO,IAAM,gBAA6B,gBAAgB,IAAI,CAAC,MAAM,EAAE,IAAI;AAGpE,IAAM,gBAAqD,OAAO;AAAA,EACvE,gBAAgB,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC;AAC3C;","names":["HEX"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idle-screens/schema",
3
- "version": "0.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "Declarative, agent-authorable saver format for idle-screens: validate a data spec, then compile it into a runnable, seeded, flash-safe SaverPlugin.",
5
5
  "license": "MIT",
6
6
  "author": "Shawn Partridge",
@@ -19,7 +19,8 @@
19
19
  ".": {
20
20
  "types": "./dist/index.d.ts",
21
21
  "import": "./dist/index.js"
22
- }
22
+ },
23
+ "./saver-spec.schema.json": "./saver-spec.schema.json"
23
24
  },
24
25
  "publishConfig": {
25
26
  "access": "public"
@@ -36,13 +37,16 @@
36
37
  "url": "https://github.com/shaaaaawn/idle-screens/issues"
37
38
  },
38
39
  "files": [
39
- "dist"
40
+ "dist",
41
+ "saver-spec.schema.json",
42
+ "FORMAT.md"
40
43
  ],
41
44
  "peerDependencies": {
42
- "@idle-screens/core": "0.1.0"
45
+ "@idle-screens/core": "0.3.0"
43
46
  },
44
47
  "devDependencies": {
45
- "@idle-screens/core": "0.1.0"
48
+ "ajv": "^8.17.1",
49
+ "@idle-screens/core": "0.3.0"
46
50
  },
47
51
  "scripts": {
48
52
  "build": "tsup",
@@ -0,0 +1,224 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://idlescreens.com/schemas/saver-spec-1.schema.json",
4
+ "title": "SaverSpec (format version 1)",
5
+ "description": "Declarative, agent-authorable screensaver: a static background plus layers of moving sprites. No code, no network, no strobe primitive — safety by construction. This schema is stricter than the runtime validator (unknown properties are rejected here to catch authoring typos; the runtime ignores them). Cross-field rules the schema cannot express: total entities across all layers must be <= 800; range pairs must satisfy min <= max.",
6
+ "type": "object",
7
+ "required": ["schemaVersion", "id", "label", "layers"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "schemaVersion": { "const": 1, "description": "Format version. Independent of the npm package version." },
11
+ "id": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "Unique kebab-case saver id." },
12
+ "label": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "Human-readable name." },
13
+ "seed": { "type": "number", "description": "Deterministic RNG seed. Same spec + same seed = identical frames. Falls back to the host's seed." },
14
+ "motionIntensity": { "enum": ["calm", "moderate", "energetic"], "description": "Advisory intensity for capability/reduced-motion gating." },
15
+ "background": { "$ref": "#/definitions/background" },
16
+ "layers": {
17
+ "type": "array",
18
+ "minItems": 1,
19
+ "maxItems": 36,
20
+ "items": { "$ref": "#/definitions/layer" },
21
+ "description": "Rendered back-to-front. At most 36 layers; at most 800 entities total."
22
+ }
23
+ },
24
+ "definitions": {
25
+ "hexColor": {
26
+ "type": "string",
27
+ "pattern": "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$",
28
+ "description": "Hex colour like #0a3a52."
29
+ },
30
+ "range01": {
31
+ "type": "array",
32
+ "items": { "type": "number", "minimum": 0, "maximum": 1 },
33
+ "minItems": 2,
34
+ "maxItems": 2,
35
+ "description": "[min, max] with min <= max, both 0..1."
36
+ },
37
+ "positiveRange": {
38
+ "type": "array",
39
+ "items": { "type": "number", "exclusiveMinimum": 0 },
40
+ "minItems": 2,
41
+ "maxItems": 2,
42
+ "description": "[min, max] px with 0 < min <= max."
43
+ },
44
+ "speedRange": {
45
+ "type": "array",
46
+ "items": { "type": "number", "minimum": 0, "maximum": 4000 },
47
+ "minItems": 2,
48
+ "maxItems": 2,
49
+ "description": "[min, max] px/sec, capped at 4000 so nothing teleports."
50
+ },
51
+ "background": {
52
+ "oneOf": [
53
+ {
54
+ "type": "object",
55
+ "required": ["type", "color"],
56
+ "additionalProperties": false,
57
+ "properties": {
58
+ "type": { "const": "solid" },
59
+ "color": { "$ref": "#/definitions/hexColor" }
60
+ }
61
+ },
62
+ {
63
+ "type": "object",
64
+ "required": ["type", "stops"],
65
+ "additionalProperties": false,
66
+ "properties": {
67
+ "type": { "const": "gradient" },
68
+ "stops": {
69
+ "type": "array",
70
+ "minItems": 2,
71
+ "items": {
72
+ "type": "object",
73
+ "required": ["at", "color"],
74
+ "additionalProperties": false,
75
+ "properties": {
76
+ "at": { "type": "number", "minimum": 0, "maximum": 1, "description": "0 = top, 1 = bottom." },
77
+ "color": { "$ref": "#/definitions/hexColor" }
78
+ }
79
+ },
80
+ "description": "Vertical gradient stops, top to bottom."
81
+ },
82
+ "band": {
83
+ "type": "object",
84
+ "required": ["color", "height"],
85
+ "additionalProperties": false,
86
+ "properties": {
87
+ "color": { "$ref": "#/definitions/hexColor" },
88
+ "height": { "type": "number", "exclusiveMinimum": 0, "description": "Band height in px (e.g. an aquarium seafloor)." }
89
+ }
90
+ }
91
+ }
92
+ }
93
+ ]
94
+ },
95
+ "layer": {
96
+ "type": "object",
97
+ "required": ["count", "sprite", "motion"],
98
+ "additionalProperties": false,
99
+ "properties": {
100
+ "count": { "type": "integer", "minimum": 1, "maximum": 400, "description": "Entities in this layer (max 400 per layer, 800 total)." },
101
+ "sprite": { "$ref": "#/definitions/sprite" },
102
+ "motion": { "$ref": "#/definitions/motion" },
103
+ "size": { "$ref": "#/definitions/positiveRange", "description": "Sprite size range in px (font size for emoji/text; ignored for circle)." },
104
+ "wrap": { "type": "boolean", "description": "Wrap to the opposite edge when leaving the viewport. Default true." },
105
+ "flip": { "type": "boolean", "description": "Flip the sprite horizontally to face its heading. Default false." },
106
+ "alpha": { "$ref": "#/definitions/range01", "description": "Per-entity opacity range. Default [1, 1]." },
107
+ "blend": { "const": "lighter", "description": "Additive compositing (glow stacking). Omit for normal source-over." },
108
+ "region": {
109
+ "type": "object",
110
+ "additionalProperties": false,
111
+ "properties": {
112
+ "x": { "$ref": "#/definitions/range01" },
113
+ "y": { "$ref": "#/definitions/range01" }
114
+ },
115
+ "description": "Fractional spawn window (constrains placement, not travel). Default full viewport."
116
+ },
117
+ "pulse": {
118
+ "type": "object",
119
+ "required": ["amp", "period"],
120
+ "additionalProperties": false,
121
+ "properties": {
122
+ "amp": { "type": "number", "exclusiveMinimum": 0, "maximum": 0.5, "description": "Opacity breathing amplitude, capped at 0.5." },
123
+ "period": { "type": "number", "minimum": 500, "description": "Breathing period in ms; floor of 500 (max 2 Hz — flash-safety)." }
124
+ },
125
+ "description": "Sinusoidal opacity breathing. Per-entity seeded phase — a layer can never strobe in unison."
126
+ },
127
+ "key": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "Addressable name so setParam can use key.field paths." },
128
+ "position": {
129
+ "type": "object",
130
+ "required": ["x", "y"],
131
+ "additionalProperties": false,
132
+ "properties": {
133
+ "x": { "type": "number", "minimum": 0, "maximum": 1 },
134
+ "y": { "type": "number", "minimum": 0, "maximum": 1 }
135
+ },
136
+ "description": "Exact fractional position for a single entity. Requires count: 1. Overrides region scatter."
137
+ }
138
+ },
139
+ "if": { "required": ["position"] },
140
+ "then": { "properties": { "count": { "const": 1 } } }
141
+ },
142
+ "sprite": {
143
+ "oneOf": [
144
+ {
145
+ "type": "object",
146
+ "required": ["kind", "glyphs"],
147
+ "additionalProperties": false,
148
+ "properties": {
149
+ "kind": { "const": "emoji" },
150
+ "glyphs": { "type": "array", "minItems": 1, "items": { "type": "string" }, "description": "Emoji picked per entity (seeded)." }
151
+ }
152
+ },
153
+ {
154
+ "type": "object",
155
+ "required": ["kind", "strings"],
156
+ "additionalProperties": false,
157
+ "properties": {
158
+ "kind": { "const": "text" },
159
+ "strings": { "type": "array", "minItems": 1, "items": { "type": "string" } },
160
+ "color": { "$ref": "#/definitions/hexColor" },
161
+ "font": { "type": "string", "description": "CSS font shorthand, e.g. 'bold 24px monospace'." },
162
+ "align": { "enum": ["left", "center", "right"] },
163
+ "baseline": { "enum": ["top", "middle", "bottom"] },
164
+ "maxWidth": { "type": "number", "exclusiveMinimum": 0 }
165
+ }
166
+ },
167
+ {
168
+ "type": "object",
169
+ "required": ["kind", "radius", "color"],
170
+ "additionalProperties": false,
171
+ "properties": {
172
+ "kind": { "const": "circle" },
173
+ "radius": { "$ref": "#/definitions/positiveRange" },
174
+ "color": { "$ref": "#/definitions/hexColor" },
175
+ "soft": { "type": "boolean", "description": "Radial falloff (glow orb) instead of a hard disc." }
176
+ }
177
+ }
178
+ ]
179
+ },
180
+ "motion": {
181
+ "oneOf": [
182
+ {
183
+ "type": "object",
184
+ "required": ["type", "speed"],
185
+ "additionalProperties": false,
186
+ "properties": {
187
+ "type": { "const": "drift" },
188
+ "speed": { "$ref": "#/definitions/speedRange" },
189
+ "angle": { "type": "number", "description": "Heading in degrees: 0 = right, 90 = down. Default 0." },
190
+ "bidirectional": { "type": "boolean", "description": "Randomly flip horizontal direction per entity (fish)." },
191
+ "bob": { "type": "number", "description": "Vertical wobble amplitude in px." }
192
+ }
193
+ },
194
+ {
195
+ "type": "object",
196
+ "required": ["type", "speed"],
197
+ "additionalProperties": false,
198
+ "properties": {
199
+ "type": { "const": "rise" },
200
+ "speed": { "$ref": "#/definitions/speedRange" },
201
+ "sway": { "type": "number", "description": "Horizontal sway amplitude in px (bubbles)." }
202
+ }
203
+ },
204
+ {
205
+ "type": "object",
206
+ "required": ["type", "speed"],
207
+ "additionalProperties": false,
208
+ "properties": {
209
+ "type": { "const": "bounce" },
210
+ "speed": { "$ref": "#/definitions/speedRange" }
211
+ }
212
+ },
213
+ {
214
+ "type": "object",
215
+ "required": ["type"],
216
+ "additionalProperties": false,
217
+ "properties": {
218
+ "type": { "const": "static", "description": "Entity stays exactly where placed. Use with position for pinned elements." }
219
+ }
220
+ }
221
+ ]
222
+ }
223
+ }
224
+ }