panelui-web 0.6.0 → 0.8.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,195 @@
1
+ /**
2
+ * Soundwave — what a voice looks like while an app listens to it.
3
+ *
4
+ * ```tsx
5
+ * <Soundwave variant="dots" state="listening" level={level} />
6
+ *
7
+ * <Soundwave variant="radial" state="speaking">
8
+ * <Soundwave.Center>
9
+ * <Avatar name="Khalid Abdi" size="xl" />
10
+ * </Soundwave.Center>
11
+ * </Soundwave>
12
+ * ```
13
+ *
14
+ * Seven looks. Four are the app's: `pills` over a microphone button, `bars`
15
+ * for a metering strip, `line` for a travelling wave, `ambient` for a glow
16
+ * behind a whole screen. Three are drawn in dots and spokes: `dots`, a field
17
+ * of dots lit into a wave; `radial`, spokes around a person; and `ring`,
18
+ * rings of dots lit outward by angle.
19
+ *
20
+ * ## It draws a level; it does not record one
21
+ *
22
+ * Nothing here touches the microphone. The app owns the recorder and hands
23
+ * over a number from 0 to 1, so the same wave works against a real meter, a
24
+ * synthesised one or a remote peer's. With no level it runs its own motion
25
+ * for the current `state`, so a voice screen can be built before any audio
26
+ * exists.
27
+ *
28
+ * ## Levels arrive faster than React should render
29
+ *
30
+ * A meter reports dozens of times a second. `level` takes a ref as well as a
31
+ * number: write each reading into `ref.current` and the wave picks it up on
32
+ * its next frame with nothing above it rendering. Each frame writes the `d`
33
+ * of one or two SVG paths, never React state, and the dots are zero-length
34
+ * round-capped segments, so a field of four hundred dots is a single path.
35
+ *
36
+ * Either way the level is smoothed with a fast attack and a slow release,
37
+ * which is what makes it read as a meter: it snaps up on a syllable and falls
38
+ * back gently instead of chattering on every sample.
39
+ */
40
+ import { type ComponentProps, type ReactNode, type RefObject } from 'react';
41
+ export type SoundwaveVariant = 'pills' | 'bars' | 'line' | 'ambient' | 'dots' | 'radial' | 'ring';
42
+ export type SoundwaveState = 'idle' | 'listening' | 'thinking' | 'speaking';
43
+ /** A role from the theme. The wave never carries a colour of its own. */
44
+ export type SoundwaveColor = 'foreground' | 'primary' | 'info' | 'success' | 'warning' | 'destructive' | 'chart-1' | 'chart-2' | 'chart-3' | 'chart-4' | 'chart-5';
45
+ export interface SoundwaveProps extends Omit<ComponentProps<'div'>, 'color' | 'children'> {
46
+ /**
47
+ * Which look to draw: `pills` for a few capsules over a microphone button,
48
+ * `bars` for a metering strip, `line` for a travelling wave, `ambient` for a
49
+ * glow that fills its parent, `dots` for a field of dots lit into a wave,
50
+ * `radial` for spokes around a centre, and `ring` for rings of dots lit
51
+ * outward by angle.
52
+ */
53
+ variant?: SoundwaveVariant;
54
+ /**
55
+ * What the app is doing. With no `level` it picks the motion the wave runs
56
+ * on its own, and it always sets what a screen reader announces.
57
+ */
58
+ state?: SoundwaveState;
59
+ /**
60
+ * How loud the input is now, from 0 to 1. Pass a ref and write each
61
+ * reading into `ref.current` to keep updates out of React entirely. Leave
62
+ * it out and the wave runs its own motion for `state`.
63
+ */
64
+ level?: number | RefObject<number | null>;
65
+ /**
66
+ * Sound to draw, in place of `level`: a `MediaStream` from a microphone or
67
+ * a call, or an `<audio>` or `<video>` element. A media file has to be on
68
+ * the same site or sent with CORS headers, or the browser hides its sound
69
+ * from the page and the wave stays still.
70
+ */
71
+ source?: SoundwaveSource | null;
72
+ /**
73
+ * Fixed values, 0 to 1 each: the stored shape of a recording, a frequency
74
+ * analysis, or one value per angle for `ring` (24 for the hours of a day).
75
+ * Resampled to the bar count, and drawn still rather than animated.
76
+ */
77
+ levels?: readonly number[];
78
+ /**
79
+ * How far a recording has played, from 0 to 1. Bars and dots behind it keep
80
+ * full ink and the rest are dimmed. `bars` and `dots`, with `levels`.
81
+ */
82
+ progress?: number;
83
+ /** How many capsules, bars or spokes, or how many angles for `ring`. `dots` fits as many columns as the width allows. */
84
+ bars?: number;
85
+ /** Capsule or bar width, the stroke of `line` and `radial`, or the size of a dot. */
86
+ barWidth?: number;
87
+ /** Space between capsules, or between dots. `bars` spaces itself across the width. */
88
+ barGap?: number;
89
+ /** Drawing height. For `radial` and `ring` it is the diameter. `ambient` fills its parent instead. */
90
+ height?: number;
91
+ /**
92
+ * `static` gives every bar a share of the current level; `scrolling` keeps a
93
+ * history that slides across, newest at the trailing edge. `bars` and `dots`.
94
+ */
95
+ mode?: 'static' | 'scrolling';
96
+ /** Grow bars and dots from the middle out rather than up from the bottom. */
97
+ centered?: boolean;
98
+ /** Fade a strip out at both ends, so it does not stop at a hard edge. */
99
+ fadeEdges?: boolean;
100
+ /** Multiplier on the incoming level, applied before it is capped at 1. */
101
+ sensitivity?: number;
102
+ /** Multiplier on the wave's own tempo, including how fast a history scrolls. */
103
+ speed?: number;
104
+ /**
105
+ * The ink, as a role from the theme. Left unset the wave is drawn in the
106
+ * text colour of whatever it sits in, which is right on most surfaces;
107
+ * `ambient` defaults to `info`, since a glow in the text colour reads as a
108
+ * shadow.
109
+ */
110
+ color?: SoundwaveColor;
111
+ /**
112
+ * Colours across the wave instead of one ink, as theme roles: left to right
113
+ * for the strips, up from the bottom for `ambient`.
114
+ */
115
+ gradient?: readonly SoundwaveColor[];
116
+ /**
117
+ * The colour of the unlit dots in `dots` and `ring`, and of the part of a
118
+ * recording not yet played in `bars`. Defaults to the ink, faint.
119
+ */
120
+ trackColor?: SoundwaveColor;
121
+ /** Hold the current frame. */
122
+ paused?: boolean;
123
+ /** What a screen reader calls it. Defaults to the state: "Listening". */
124
+ label?: string;
125
+ /** For `radial` and `ring`: a `Soundwave.Center` holding what sits in the middle. */
126
+ children?: ReactNode;
127
+ }
128
+ /** A voice as a wave. Draws a `level` it is handed, or runs its own motion for `state`. */
129
+ declare function SoundwaveRoot({ variant, state, level: levelProp, source, levels, progress, bars, barWidth, barGap, height, mode, centered, fadeEdges, sensitivity, speed, color, gradient, trackColor, paused, label, className, style, children, ...props }: SoundwaveProps): import("react").JSX.Element;
130
+ declare namespace SoundwaveRoot {
131
+ var displayName: string;
132
+ }
133
+ export interface SoundwaveCenterProps extends ComponentProps<'div'> {
134
+ }
135
+ /**
136
+ * What sits in the hole of a `radial` or `ring` wave: a person's avatar, a
137
+ * figure, a button. It is sized to the hole and centred in it.
138
+ */
139
+ declare function SoundwaveCenter({ className, ...props }: SoundwaveCenterProps): import("react").JSX.Element;
140
+ declare namespace SoundwaveCenter {
141
+ var displayName: string;
142
+ }
143
+ export declare const Soundwave: typeof SoundwaveRoot & {
144
+ Center: typeof SoundwaveCenter;
145
+ };
146
+ export { SoundwaveCenter };
147
+ /** Something with sound in it: a microphone or call stream, or an `<audio>` or `<video>` element. */
148
+ export type SoundwaveSource = MediaStream | HTMLMediaElement;
149
+ export interface AudioLevelOptions {
150
+ /**
151
+ * The quietest sound that still moves the wave, in decibels below full
152
+ * scale. Default `-60`. Raise it towards `-45` in a noisy room.
153
+ */
154
+ floor?: number;
155
+ /** The loudness that fills the wave, in decibels below full scale. Default `-12`. */
156
+ ceiling?: number;
157
+ }
158
+ /**
159
+ * How loud `source` is, from 0 to 1, kept in a ref that is updated every
160
+ * frame. Pass the ref to `Soundwave` as `level`, or read `ref.current`
161
+ * yourself. With no source the ref holds 0.
162
+ */
163
+ export declare function useAudioLevel(source: SoundwaveSource | null | undefined, { floor, ceiling }?: AudioLevelOptions): RefObject<number>;
164
+ export type MicrophoneStatus = 'idle' | 'requesting' | 'listening' | 'denied' | 'unsupported';
165
+ export interface UseMicrophoneOptions extends AudioLevelOptions {
166
+ /** Passed to `getUserMedia` as `audio`, to pick a device or change its processing. */
167
+ constraints?: MediaTrackConstraints;
168
+ }
169
+ export interface MicrophoneControls {
170
+ /** How loud the microphone is, from 0 to 1. Pass it to `Soundwave` as `level`. */
171
+ level: RefObject<number>;
172
+ /** The live stream while listening, for a recorder or a call. `null` otherwise. */
173
+ stream: MediaStream | null;
174
+ /** `requesting` while the browser asks for permission, `denied` if it was refused or there is no microphone. */
175
+ status: MicrophoneStatus;
176
+ /** Whether it is listening now. */
177
+ active: boolean;
178
+ /** Ask for the microphone and start listening. Call it from a click: browsers only allow sound to start from one. */
179
+ start: () => Promise<void>;
180
+ /** Stop listening and release the microphone, which turns the browser's recording light off. */
181
+ stop: () => void;
182
+ /** `start` when stopped, `stop` when listening. */
183
+ toggle: () => void;
184
+ }
185
+ /**
186
+ * The microphone, ready to draw. Nothing is asked for until `start`, and the
187
+ * microphone is released on `stop` and when the component unmounts.
188
+ *
189
+ * ```tsx
190
+ * const mic = useMicrophone();
191
+ * <Soundwave level={mic.active ? mic.level : undefined} />
192
+ * <Button onClick={mic.toggle}>{mic.active ? 'Stop' : 'Speak'}</Button>
193
+ * ```
194
+ */
195
+ export declare function useMicrophone({ constraints, ...options }?: UseMicrophoneOptions): MicrophoneControls;