voice-ring 0.0.0-stage → 0.1.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.
@@ -0,0 +1,554 @@
1
+ import { CanvasHTMLAttributes } from 'react';
2
+ import { CSSProperties } from 'react';
3
+ import { JSX as JSX_2 } from 'react/jsx-runtime';
4
+
5
+ export declare const ACCENT_COLORS: Record<VoiceOrbAccent, OrbTint>;
6
+
7
+ /**
8
+ * An AnalyserNode fed by `stream`, plus the function that tears it down.
9
+ * Returns null when the browser has no Web Audio or the stream carries no
10
+ * audio track.
11
+ */
12
+ export declare function acquireAnalyser(stream: MediaStream): AnalyserLease | null;
13
+
14
+ export declare interface AnalyserLease {
15
+ analyser: AnalyserNode;
16
+ release: () => void;
17
+ }
18
+
19
+ declare interface Dot {
20
+ x: number;
21
+ y: number;
22
+ z: number;
23
+ r: number;
24
+ /** Ink value: 0 = darkest ink on paper. Mirrored on dark themes. */
25
+ white: number;
26
+ a?: number;
27
+ }
28
+
29
+ /** Soft, slightly desaturated hues that sit well beside gray ink. */
30
+ export declare const EMOTION_COLORS: Record<VoiceOrbEmotion, OrbTint>;
31
+
32
+ declare interface FollowerConfig {
33
+ sensitivity: number;
34
+ threshold: number;
35
+ attack: number;
36
+ release: number;
37
+ idle: number;
38
+ /** The voice clock runs at 1 + driveGain × level. */
39
+ driveGain: number;
40
+ /** Waiting pace: with no voice and no work the clocks run at this share of the tuned speed. */
41
+ calm: number;
42
+ /** Orb seconds per real second: the type's tuned speed × the `speed` prop. */
43
+ rate: number;
44
+ /** Working on what was heard: sources are ignored and the processing motion eases in. */
45
+ processing: boolean;
46
+ /** Where the source says the voice is, −1 left … 1 right (0 when unknown). */
47
+ direction: number;
48
+ }
49
+
50
+ /**
51
+ * How a luminous frame is lit. Its strokes are painted as light rather
52
+ * than ink — a conic spectrum around the orb, bloomed in soft passes, with
53
+ * chromatic fringes and specular hot spots — and everything the painter
54
+ * needs is here, so a port can draw it too (an AngularGradient in SwiftUI).
55
+ */
56
+ declare interface FrameLight {
57
+ /** Orb centre and rest radius, CSS px. */
58
+ cx: number;
59
+ cy: number;
60
+ r: number;
61
+ /** The spectrum: `[offset 0–1, r, g, b]` stops around the orb… */
62
+ stops: Array<[number, number, number, number]>;
63
+ /** …starting at this angle, radians clockwise from +x. */
64
+ angle: number;
65
+ /** Overall brightness, ~0.6 at rest to ~1.3 in full voice. */
66
+ intensity: number;
67
+ /** Strength of the soft halo around the rings, 0 for none. */
68
+ halo: number;
69
+ /** Specular hot spots, white light pooled where it touches the rings. */
70
+ spec: Array<{
71
+ x: number;
72
+ y: number;
73
+ r: number;
74
+ a: number;
75
+ }>;
76
+ /** Chromatic fringe offset, as a share of `r`. */
77
+ fringe: number;
78
+ }
79
+
80
+ /**
81
+ * The page's shared AudioContext, created on first use. Call it from a
82
+ * user gesture (the microphone button) where you can: Safari only lets a
83
+ * context start inside one. It is resumed on every call, so a context the
84
+ * browser suspended in the background picks up again.
85
+ */
86
+ export declare function getAudioContext(): AudioContext | null;
87
+
88
+ /**
89
+ * Web Audio plumbing shared by every VoiceOrb instance.
90
+ *
91
+ * One AudioContext for the page, one MediaStreamAudioSourceNode per stream
92
+ * (reference-counted, so several orbs can listen to the same microphone),
93
+ * and one AnalyserNode per instance. Nothing is connected to the
94
+ * destination, so the audio is analysed but never played back.
95
+ */
96
+ /** True when this browser can analyse audio at all. */
97
+ export declare function isAudioSupported(): boolean;
98
+
99
+ /** How strongly the orb leans, each as a share of its radius at full pan. */
100
+ declare interface LeanAmounts {
101
+ /** The whole orb drifts toward the voice. */
102
+ move: number;
103
+ /** The near side is drawn out further than the far side — a stretch. */
104
+ pull: number;
105
+ /** The near flank bulges, strongest at the equator. */
106
+ deform: number;
107
+ /** Pressing on at the screen's edge, it squashes against it: 0 never … 1 most. */
108
+ squash?: number;
109
+ /** Pressing on at the edge, it is refracted into it as through glass: 0 none … 1 … 2 most. */
110
+ lens?: number;
111
+ /** The lens draws it out toward the edge (distortion): 0 none … 1 … 2. */
112
+ stretch?: number;
113
+ /** The lens parts red from blue (chromatic aberration): 0 none … 1 … 2. */
114
+ split?: number;
115
+ /** The lens blurs it, more the nearer the edge: 0 none … 1 … 2. */
116
+ blur?: number;
117
+ /** It bounces as it meets the edge: 0 none … 1 … 2. */
118
+ bounce?: number;
119
+ }
120
+
121
+ export declare type MicrophoneState = 'idle' | 'requesting' | 'live' | 'denied' | 'unsupported' | 'error';
122
+
123
+ /** Where a leaning orb meets the screen's edge, and how hard it presses. */
124
+ declare interface OrbContact {
125
+ /** -1 the left edge, 1 the right. */
126
+ side: number;
127
+ /** The edge, CSS px: marks stop here. */
128
+ x: number;
129
+ /** The stretch of the face that meets it, top and bottom. */
130
+ y0: number;
131
+ y1: number;
132
+ /** How deep into the orb the squash reaches. */
133
+ depth: number;
134
+ /** 0 just touching … 1 pressing hard. */
135
+ press: number;
136
+ /** The refraction into the edge, for the painter; none when it is off. */
137
+ lens?: OrbLens;
138
+ }
139
+
140
+ /**
141
+ * One rendered instant: a complete, final set of draw instructions.
142
+ * Strokes paint first, then `dots` in array order (already z-sorted
143
+ * far → near), so nodes sit on top of the lines they ride.
144
+ */
145
+ declare interface OrbFrame {
146
+ dots: Dot[];
147
+ strokes: Stroke[];
148
+ /** Present on luminous types: paint the strokes as coloured light. */
149
+ light?: FrameLight;
150
+ /** Paint the dots as squares, `r` their half side: pixels, not points. */
151
+ square?: boolean;
152
+ /** Set by the lean while the orb presses on the screen's edge. */
153
+ contact?: OrbContact;
154
+ }
155
+
156
+ /**
157
+ * The lens a pressing orb is refracted through, CSS px. A zone back from
158
+ * the edge it begins; toward the edge it draws the image out across the
159
+ * gap the orb rests short by, opens it taller by `flare`, parts red from
160
+ * blue by `split` and blurs it by `blur`, each at full strength at the edge.
161
+ */
162
+ declare interface OrbLens {
163
+ zone: number;
164
+ gap: number;
165
+ flare: number;
166
+ split: number;
167
+ blur: number;
168
+ /** The height the flare opens from. */
169
+ cy: number;
170
+ }
171
+
172
+ /**
173
+ * Theme mode, resolved exactly as thinking-orbs does: `auto` (default)
174
+ * reads an ancestor `data-theme` / `.dark` / `.light`, then
175
+ * `prefers-color-scheme`, live. `dark` draws light ink, `light` dark ink.
176
+ */
177
+ export declare type OrbTheme = 'auto' | 'dark' | 'light';
178
+
179
+ /** Optional ink tint, resolved to an RGB triple (0–255 each). */
180
+ declare interface OrbTint {
181
+ r: number;
182
+ g: number;
183
+ b: number;
184
+ }
185
+
186
+ declare interface Preset {
187
+ /** Orb seconds per real second, on both clocks. */
188
+ speed: number;
189
+ /** The voice clock runs at 1 + driveGain × level. */
190
+ driveGain: number;
191
+ /** Input gain tuned for the type, under the `sensitivity` prop. Default 1. */
192
+ gain?: number;
193
+ /** The type's rise time, when the `attack` prop is not set. Default 0.06 s. */
194
+ attack?: number;
195
+ /** The type's mark size, when the `dotSize` prop is not set. Default 1. */
196
+ dotSize?: number;
197
+ /** How much a voice speeds the type up, when the `voiceSpeed` prop is not set. Default 1. */
198
+ voiceSpeed?: number;
199
+ /** The type's own lean, where the `direction…` props are not set. Default LEAN_DEFAULTS. */
200
+ lean?: Partial<LeanAmounts>;
201
+ /** CSS px the lean stops short of the screen's edge, when `directionMargin` is not set. Default 32. */
202
+ margin?: number;
203
+ /** Knobs at the 200px design. */
204
+ opts: TypeOpts;
205
+ /** Count knobs → the exponent each takes of the size × density scale. */
206
+ counts: Record<string, number>;
207
+ }
208
+
209
+ export declare const PRESETS: Record<VoiceOrbType, Preset>;
210
+
211
+ declare interface Resolved {
212
+ frame: TypeFrame;
213
+ speed: number;
214
+ driveGain: number;
215
+ gain: number;
216
+ opts: TypeOpts;
217
+ }
218
+
219
+ /** Resolve a type at a size, with the density and dot-size multipliers. */
220
+ export declare function resolvePreset(type: VoiceOrbType, size: number, dots?: number, dotSize?: number): Resolved;
221
+
222
+ export declare const SPECTRUM_N = 16;
223
+
224
+ /** A polyline in screen space whose opacity varies point to point. */
225
+ declare interface Stroke {
226
+ /** Flat `[x0, y0, x1, y1, …]` in CSS px. */
227
+ pts: number[];
228
+ /** Opacity per point — depth fades the far side of a line. */
229
+ a: number[];
230
+ /** Ink value, same convention as `Dot.white`. */
231
+ white: number;
232
+ /** Width in CSS px. */
233
+ w: number;
234
+ closed?: boolean;
235
+ }
236
+
237
+ /**
238
+ * Geometry for one instant: pure math over (size, t, voice, opts), no
239
+ * rendering surface and no theme. `t` is the ambient clock (spin, sway);
240
+ * `voice.drive` is the clock a voice speeds up.
241
+ */
242
+ export declare type TypeFrame = (size: number, t: number, voice: VoiceInput, opts: TypeOpts) => OrbFrame;
243
+
244
+ /** Type knobs. Numbers only, so a preset survives JSON and native ports. */
245
+ export declare interface TypeOpts {
246
+ [key: string]: number | undefined;
247
+ }
248
+
249
+ /**
250
+ * Microphone access for VoiceOrb.
251
+ *
252
+ * ```tsx
253
+ * const mic = useMicrophone();
254
+ * <button onClick={mic.start}>Listen</button>
255
+ * <VoiceOrb type="orbit" stream={mic.stream} />
256
+ * ```
257
+ *
258
+ * The stream is stopped on unmount, and when the browser ends the track
259
+ * (device unplugged, permission revoked) the state falls back to 'idle'.
260
+ */
261
+ export declare function useMicrophone(options?: UseMicrophoneOptions): UseMicrophoneResult;
262
+
263
+ export declare interface UseMicrophoneOptions {
264
+ /**
265
+ * Extra `getUserMedia` audio constraints. The defaults turn the browser's
266
+ * voice processing off (echo cancellation, noise suppression, auto gain)
267
+ * so the orb sees the real dynamics of the voice rather than a levelled
268
+ * signal; pass `{}` to keep the browser defaults.
269
+ */
270
+ constraints?: MediaTrackConstraints;
271
+ /** Ask for the microphone on mount rather than waiting for `start()`. */
272
+ autoStart?: boolean;
273
+ }
274
+
275
+ export declare interface UseMicrophoneResult {
276
+ /** The live stream to hand to `<VoiceOrb stream={…}>`, or null. */
277
+ stream: MediaStream | null;
278
+ state: MicrophoneState;
279
+ /** The error behind a 'denied' / 'error' state, if any. */
280
+ error: Error | null;
281
+ /** True when this browser can capture audio at all. */
282
+ supported: boolean;
283
+ /** Request the microphone. Call it from a click so Safari lets audio start. */
284
+ start: () => Promise<MediaStream | null>;
285
+ /** Stop every track and drop the stream. */
286
+ stop: () => void;
287
+ }
288
+
289
+ export declare const VOICE_ORB_TYPES: readonly VoiceOrbType[];
290
+
291
+ export declare class VoiceFollower {
292
+ /** Real seconds this orb has run: onsets and breathing keep time on it. */
293
+ clock: number;
294
+ /** The ambient clock (spin, sway) in orb seconds. */
295
+ t: number;
296
+ /** The voice clock, in orb seconds. */
297
+ drive: number;
298
+ private level;
299
+ private bands;
300
+ private spectrum;
301
+ private floor;
302
+ /** The voice alone on the soft envelope — what wakes the clocks. */
303
+ private heard;
304
+ private proc;
305
+ private spin;
306
+ private pan;
307
+ private panVel;
308
+ private raw;
309
+ private timeBuf;
310
+ private freqBuf;
311
+ private out;
312
+ /** Read an analyser: RMS loudness, three voice bands and the spectrum. */
313
+ readAnalyser(analyser: AnalyserNode, sensitivity: number, dt: number): void;
314
+ /**
315
+ * Read a plain level. There is no spectrum behind it, so the bands and
316
+ * slots are given a little independent life — slow, out-of-phase drifts
317
+ * scaled by the level, with two formant-like humps wandering the
318
+ * spectrum — so a level-driven orb still moves like a voice.
319
+ */
320
+ readLevel(level: number, sensitivity: number): void;
321
+ /** Nothing to read: let every envelope settle. */
322
+ readSilence(): void;
323
+ /** Advance by `dt` real seconds. `still` (reduced motion) holds the clocks. */
324
+ step(dt: number, cfg: FollowerConfig, still: boolean): VoiceInput;
325
+ }
326
+
327
+ /**
328
+ * What the voice is doing at one instant, already gated, saturated and
329
+ * smoothed — every value can go straight into geometry. The follower in
330
+ * `voice.ts` builds it from a microphone, any AnalyserNode or a plain
331
+ * level; a custom renderer can just as well build it by hand.
332
+ */
333
+ export declare interface VoiceInput {
334
+ /** Loudness envelope, 0–1, with the idle breathing folded in. */
335
+ level: number;
336
+ /** Low / mid / high envelopes, 0–1: chest, vowels, sibilance. */
337
+ bands: [number, number, number];
338
+ /** `level` on a slow envelope (~0.35 s up, ~0.9 s down): for shape and
339
+ * amplitude, which read as mechanical if they pump with every syllable. */
340
+ soft: number;
341
+ /** `bands` on the same slow envelope. */
342
+ softBands: [number, number, number];
343
+ /** Log-spaced spectrum envelope, low → high, 0–1 per slot. */
344
+ spectrum: ArrayLike<number>;
345
+ /**
346
+ * The voice clock, in seconds: it runs at 1 + gain × level, so motion
347
+ * driven by it speeds up while someone speaks and settles after — with
348
+ * no jump, since it is integrated rather than multiplied.
349
+ */
350
+ drive: number;
351
+ /**
352
+ * How far into the processing state, 0–1, eased: the orb is working on
353
+ * what it heard. Sources are ignored meanwhile; each type answers with
354
+ * its own working motion (a contraction, a sweep, a beat).
355
+ */
356
+ processing: number;
357
+ /**
358
+ * Processing's turn, in radians, for motion that goes round while the orb
359
+ * works (a spinning spectrum, a sweeping highlight). It is integrated, so
360
+ * it speeds up and slows down with the eased blend instead of jumping,
361
+ * and once processing ends it settles back to the nearest whole turn.
362
+ */
363
+ spin: number;
364
+ /**
365
+ * Where the voice comes from, −1 left … 1 right, smoothed: it follows the
366
+ * source while a voice is heard and drifts back to centre in silence.
367
+ */
368
+ pan: number;
369
+ }
370
+
371
+ export declare function VoiceOrb({ type, state, size, stream, analyser, level, sensitivity, threshold, attack, release, idle, calm, voiceSpeed: voiceSpeedProp, emotion, emotionColors, saturation, accent, gradientAngle, gradientSpin, neutral, colorBrightness, colorHue, gradientStart, gradientWidth, direction, directionMove, directionPull, directionDeform, directionSquash, directionLens, directionStretch, directionSplit, directionBlur, directionBounce, directionMargin: marginProp, directionBounds, theme, speed, paused, color, dots, dotSize: dotSizeProp, opts: optsOverride, frame: customFrame, onLevel, style, 'aria-label': ariaLabel, ...rest }: VoiceOrbProps): JSX_2.Element;
372
+
373
+ /** Colourful mode's accents: the same three hues, by colour. */
374
+ export declare type VoiceOrbAccent = 'green' | 'red' | 'blue';
375
+
376
+ export declare type VoiceOrbEmotion = 'positive' | 'negative' | 'calm';
377
+
378
+ /** Props for the VoiceOrb React component. */
379
+ export declare interface VoiceOrbProps extends Omit<CanvasHTMLAttributes<HTMLCanvasElement>, 'style'> {
380
+ /** Which orb. @default 'orbit' */
381
+ type?: VoiceOrbType;
382
+ /** Conversation state; changes ease, never cut. @default 'listening' */
383
+ state?: VoiceOrbState;
384
+ /** Rendered size in CSS px; any size, designed at 200. @default 160 */
385
+ size?: number;
386
+ /**
387
+ * A live audio stream — usually `useMicrophone().stream`. The orb opens
388
+ * its own analyser on it (shared with any other orb on the same stream)
389
+ * and wins over `analyser` and `level`.
390
+ */
391
+ stream?: MediaStream | null;
392
+ /**
393
+ * Any AnalyserNode you already have: the assistant's voice playing
394
+ * through an `<audio>` element or a WebRTC track, say. Wins over `level`.
395
+ */
396
+ analyser?: AnalyserNode | null;
397
+ /**
398
+ * Drive it yourself, 0–1. A number for occasional changes, or a getter
399
+ * sampled every frame without re-rendering your tree.
400
+ */
401
+ level?: number | (() => number);
402
+ /** Input gain — raise for quiet sources. @default 0.85 */
403
+ sensitivity?: number;
404
+ /** Noise gate: below this the orb hears silence. @default 0.04 */
405
+ threshold?: number;
406
+ /** Seconds to rise. @default 0.06 (0.16 for `pixel`) */
407
+ attack?: number;
408
+ /** Seconds to settle. @default 0.32 */
409
+ release?: number;
410
+ /** Breathing kept under the voice, so silence never looks dead; 0 for none. @default 0.1 */
411
+ idle?: number;
412
+ /**
413
+ * Waiting pace: with no voice and nothing processing, the clocks run at
414
+ * this share of the tuned speed — low for a calm, slow orb. @default 0.4
415
+ */
416
+ calm?: number;
417
+ /** How much a voice speeds the motion up, as a multiplier on the type's tuning. @default 1 (0.9 for `aura`) */
418
+ voiceSpeed?: number;
419
+ /**
420
+ * An emotion detected in the voice — `positive` (green), `negative`
421
+ * (red) or `calm` (blue). Its colour drifts through the orb in soft
422
+ * patches, near marks taking it most; it eases in, crossfades between
423
+ * emotions and fades out on `null`. Detection is yours to supply.
424
+ * @default null
425
+ */
426
+ emotion?: VoiceOrbEmotion | null;
427
+ /** Override any emotion's colour as an RGB triple (0–255). */
428
+ emotionColors?: Partial<Record<VoiceOrbEmotion, OrbTint>>;
429
+ /**
430
+ * Colourful mode, for the ink types (orbit, sash, signal, swell, pixel): a
431
+ * two-colour gradient across the orb, from the neutral ink into one
432
+ * colour, every mark keeping its depth shading. The colour is the
433
+ * `emotion`'s when one is set, else `accent`; with neither the orb stays
434
+ * neutral. `saturation` is how strong the colour gets: 0 never, 1 the
435
+ * colour itself, up to 2 purer and deeper still. Aura keeps its own spectrum.
436
+ * @default 2
437
+ */
438
+ saturation?: number;
439
+ /** Colourful mode: a fixed colour for the gradient, shown even without an emotion. @default null */
440
+ accent?: VoiceOrbAccent | null;
441
+ /** Colourful mode: which way the gradient runs, in degrees — 0 left → right, 90 top → bottom. @default 99 */
442
+ gradientAngle?: number;
443
+ /** Colourful mode: how fast the gradient turns, degrees per second; 0 holds it still. @default 12 */
444
+ gradientSpin?: number;
445
+ /**
446
+ * Where the voice comes from, −1 left … 1 right — a number, or a getter
447
+ * sampled every frame. While a voice is heard the orb leans that way (it
448
+ * drifts, is pulled out and bulges on the near side) and settles back to
449
+ * centre in silence. Supply it from a stereo estimate (the SwiftUI port
450
+ * reads the iPhone's stereo mics) or drive it yourself.
451
+ */
452
+ direction?: number | (() => number);
453
+ /**
454
+ * Lean: how far the orb drifts toward the voice, as a share of its radius.
455
+ * Every `direction…` amount left unset takes the type's own; `aura`
456
+ * barely drifts but leans, bulges a touch, never squashes, and meets the
457
+ * edge through a softer lens. @default 2 (aura 0.21)
458
+ */
459
+ directionMove?: number;
460
+ /** Lean: how far its near side is pulled out beyond the far side. @default 0.7 (aura 0.36) */
461
+ directionPull?: number;
462
+ /** Lean: how much the near flank bulges. @default 0.1 (aura 0.28) */
463
+ directionDeform?: number;
464
+ /** Lean: pressing on at the screen's edge, how much the orb squashes against it, 0–1. @default 0.5 (aura 0) */
465
+ directionSquash?: number;
466
+ /**
467
+ * Lean: pressing on at the screen's edge, how strongly the orb is refracted
468
+ * into it, as through glass — it rests short of the edge and its near side
469
+ * is drawn out across the gap, its colours parted and blurred toward the
470
+ * edge. 0 none … 1 … 2 most. @default 0.67 (aura 0.49)
471
+ */
472
+ directionLens?: number;
473
+ /** Lean: how far the edge lens draws the orb out toward the edge (distortion), 0–2. @default 1 (aura 1.04) */
474
+ directionStretch?: number;
475
+ /** Lean: how far the edge lens parts red from blue (chromatic aberration), 0–2. @default 1 (aura 1.01) */
476
+ directionSplit?: number;
477
+ /** Lean: how much the edge lens blurs, more the nearer the edge, 0–2. @default 1 (aura 1.24) */
478
+ directionBlur?: number;
479
+ /** Lean: how much the orb bounces as it meets the edge, 0–2. @default 1 (aura 0.92) */
480
+ directionBounce?: number;
481
+ /** Lean: CSS px the orb stops short of the screen's edge. @default 32 */
482
+ directionMargin?: number;
483
+ /**
484
+ * Lean: an element whose left and right edges the orb leans up to instead
485
+ * of the screen's — a card or a panel it lives in (a ref). Clip that
486
+ * element (`overflow: hidden`) so the glow past the margin is cut there.
487
+ */
488
+ directionBounds?: {
489
+ readonly current: Element | null;
490
+ };
491
+ /**
492
+ * Colourful mode: how neutral the gradient's start is — 1 begins in the
493
+ * plain monochrome ink and gathers the accent toward the far side, 0
494
+ * covers the orb in the accent end to end. @default 1
495
+ */
496
+ neutral?: number;
497
+ /** Colourful mode: the colour's brightness, a multiplier on its lightness. @default 1.02 */
498
+ colorBrightness?: number;
499
+ /** Colourful mode: degrees to turn the colour's hue. @default 0 */
500
+ colorHue?: number;
501
+ /** Colourful mode: where across the orb (0–1) the colour begins to come in. @default 0.1 */
502
+ gradientStart?: number;
503
+ /** Colourful mode: how far across the orb (0–1) the colour takes to arrive in full. @default 0.6 */
504
+ gradientWidth?: number;
505
+ /** Theme mode; `auto` detects from the host project. @default 'auto' */
506
+ theme?: OrbTheme;
507
+ /** Speed multiplier on the type's tuned clocks. @default 1 */
508
+ speed?: number;
509
+ /** Freeze on the current frame. @default false */
510
+ paused?: boolean;
511
+ /** Optional ink tint — `#rgb`, `#rrggbb` or `rgb()`; depth shading is kept on it. */
512
+ color?: string;
513
+ /** Density multiplier on the type's dot / line counts. @default 1 */
514
+ dots?: number;
515
+ /** Multiplier on every dot radius. @default 1 (0.55 for `pixel`) */
516
+ dotSize?: number;
517
+ /** Advanced: raw type knobs merged over the resolved preset, last. */
518
+ opts?: TypeOpts;
519
+ /** Escape hatch: replace the type's geometry, same contract as `TYPE_FRAMES`. */
520
+ frame?: TypeFrame;
521
+ /** Called every frame with the shaped level, 0–1 — for a meter or a label. */
522
+ onLevel?: (level: number) => void;
523
+ style?: CSSProperties;
524
+ }
525
+
526
+ /**
527
+ * The conversation state:
528
+ * - `idle` — waiting for someone to talk: sources ignored, a calm, very slow drift
529
+ * - `listening` — the user's turn: follows the source (usually the microphone)
530
+ * - `speaking` — the assistant's turn: follows the source (usually its playback)
531
+ * - `processing` — working on what was heard: sources ignored, each type's working motion
532
+ *
533
+ * Listening and speaking draw the same way; the source is what differs.
534
+ * Without a voice, either one settles into the same calm pace as `idle`.
535
+ */
536
+ export declare type VoiceOrbState = 'idle' | 'listening' | 'speaking' | 'processing';
537
+
538
+ /**
539
+ * The six orb types:
540
+ * - `orbit` — particles on tilted orbits in perspective; a voice zooms it toward you
541
+ * - `sash` — an undulating band of dotted lanes; a voice deepens the wave
542
+ * - `signal` — a wireframe of great circles; a voice deforms it like a skin
543
+ * - `swell` — a halftone sphere of drifting clouds; a voice bulges its surface
544
+ * - `pixel` — a ball of square pixels, halftone shaded; a voice bulges it and lights it in rings
545
+ * - `aura` — signal's rings drawn in light: a blooming spectrum; a voice bends and brightens it
546
+ */
547
+ export declare type VoiceOrbType = 'orbit' | 'sash' | 'signal' | 'swell' | 'pixel' | 'aura';
548
+
549
+ /** A voice-reactive ring of light: VoiceOrb with `type="aura"` fixed. */
550
+ export declare function VoiceRing(props: VoiceRingProps): JSX_2.Element;
551
+
552
+ export declare type VoiceRingProps = Omit<VoiceOrbProps, 'type' | 'frame'>;
553
+
554
+ export { }