/** * Signal -- live data feeds from the browser environment. * * (viewport, scroll, pointer, time, media queries, custom). * * @module */ import type { Stream, Scope } from 'effect'; import { Effect } from 'effect'; import type { AVBridge } from './av-bridge.js'; /** Tag of a {@link SignalSource} — the family of live data feed a signal binds to. */ export type SignalSourceType = 'viewport' | 'time' | 'pointer' | 'scroll' | 'media' | 'custom' | 'audio'; /** * Configuration describing what a {@link Signal} reads from: viewport axis, * time mode, pointer axis, scroll axis, media query, custom push source, * or audio sample/normalized mode. * * Discriminant payloads default to the common case when omitted: * viewport `axis: 'width'`, time `mode: 'elapsed'`, pointer `axis: 'x'`, * scroll `axis: 'y'`, audio `mode: 'sample'`. {@link Signal.make} normalizes * the source, so the returned signal's `source` always carries explicit values. * * Audio modes: * - `sample` / `normalized` — offline/scrub reads via {@link Signal.audio} * (raw sample index / 0..1 progress over a known duration). * - `amplitude` / `beat` — LIVE analyser-driven feeds, published by a runtime * producer (e.g. the Astro `audio.*` rAF observer reading an AnalyserNode). * `amplitude` is 0..1 RMS loudness; `beat` is a 0/1 onset pulse. These are * "driven externally" stubs here — `@czap/core` owns the vocabulary and * initial value; the host publishes the live samples. */ export type SignalSource = { readonly type: 'viewport'; readonly axis?: 'width' | 'height'; } | { readonly type: 'time'; readonly mode?: 'elapsed' | 'absolute' | 'scheduled'; } | { readonly type: 'pointer'; readonly axis?: 'x' | 'y' | 'pressure'; } | { readonly type: 'scroll'; readonly axis?: 'x' | 'y' | 'progress'; } | { readonly type: 'media'; readonly query: string; } | { readonly type: 'custom'; readonly id: string; } | { readonly type: 'audio'; readonly mode?: 'sample' | 'normalized' | 'amplitude' | 'beat'; }; interface SignalShape { readonly source: SignalSource; readonly current: Effect.Effect; readonly changes: Stream.Stream; } interface ControllableSignalShape extends SignalShape { seek(to: T): Effect.Effect; pause(): Effect.Effect; resume(): Effect.Effect; } /** * Create a reactive signal from a browser environment source. * * Returns a scoped Effect that sets up event listeners (resize, scroll, * pointermove, etc.) and cleans them up when the scope closes. The signal * exposes `.current` (latest value) and `.changes` (stream of updates). * * @example * ```ts * import { Effect, Scope } from 'effect'; * import { Signal } from '@czap/core'; * * const program = Effect.scoped(Effect.gen(function* () { * const sig = yield* Signal.make({ type: 'viewport', axis: 'width' }); * const width = yield* sig.current; * // width === current window.innerWidth * })); * ``` */ declare function _make(rawSource: SignalSource): Effect.Effect, never, Scope.Scope>; /** * Create a controllable time signal for video rendering / scrubbing. * * External code drives the signal value via seek(); no automatic ticking. * Supports pause/resume to temporarily ignore seek updates. * * @example * ```ts * import { Effect } from 'effect'; * import { Signal } from '@czap/core'; * * const program = Effect.scoped(Effect.gen(function* () { * const ctrl = yield* Signal.controllable(); * yield* ctrl.seek(1500); * const t = yield* ctrl.current; * // t === 1500 * yield* ctrl.pause(); * yield* ctrl.seek(2000); // ignored while paused * })); * ``` */ declare function _controllable(): Effect.Effect, never, Scope.Scope>; interface AudioSignalShape extends SignalShape { poll(): Effect.Effect; } /** * Create an audio signal backed by an AVBridge. * * In 'sample' mode, returns the raw sample index. In 'normalized' mode, * returns a 0..1 progress value based on totalDurationSec — omitting * `totalDurationSec` (or passing a non-positive value) in 'normalized' * mode throws a `ValidationError`. Call `.poll()` to read the latest * sample from the bridge and update the signal. * * @example * ```ts * import { Effect } from 'effect'; * import { Signal } from '@czap/core'; * * const program = Effect.scoped(Effect.gen(function* () { * const audioSig = yield* Signal.audio(bridge, 'normalized', 120); * const progress = yield* audioSig.poll(); * // progress is a number between 0 and 1 * })); * ``` */ declare function _audio(bridge: AVBridge.Shape, mode?: 'sample' | 'normalized', totalDurationSec?: number): Effect.Effect; /** * Signal namespace -- live data feeds from the browser environment. * * Create reactive signals from viewport, scroll, pointer, time, media query, * audio, or custom sources. Each signal provides `.current` and `.changes` * backed by Effect's SubscriptionRef. Scoped for automatic listener cleanup. * * @example * ```ts * import { Effect } from 'effect'; * import { Signal } from '@czap/core'; * * const program = Effect.scoped(Effect.gen(function* () { * const viewport = yield* Signal.make({ type: 'viewport', axis: 'width' }); * const width = yield* viewport.current; * const ctrl = yield* Signal.controllable(); * yield* ctrl.seek(500); * })); * ``` */ export declare const Signal: { make: typeof _make; controllable: typeof _controllable; audio: typeof _audio; }; export declare namespace Signal { /** Structural shape of a passive {@link Signal}: `source` + `current` + `changes`. */ type Shape = SignalShape; /** Structural shape of a seekable, pausable signal — e.g. driven by Remotion or a scrub UI. */ type Controllable = ControllableSignalShape; /** Structural shape of an audio-sourced signal backed by an {@link AVBridge}. */ type Audio = AudioSignalShape; } export {}; //# sourceMappingURL=signal.d.ts.map