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.
- package/LICENSE +21 -0
- package/README.md +68 -2
- package/dist/index.cjs +41 -0
- package/dist/index.d.ts +554 -0
- package/dist/index.es.js +1186 -0
- package/package.json +65 -4
package/dist/index.d.ts
ADDED
|
@@ -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 { }
|