@burtson-labs/ui 0.10.1 → 0.12.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/README.md +7 -2
- package/dist/components/audio-player.d.ts +39 -0
- package/dist/components/audio-player.js +250 -0
- package/dist/components/audio-player.js.map +1 -0
- package/dist/components/chat-history.d.ts +64 -0
- package/dist/components/chat-history.js +282 -0
- package/dist/components/chat-history.js.map +1 -0
- package/dist/components/chat-layout.d.ts +42 -0
- package/dist/components/chat-layout.js +217 -0
- package/dist/components/chat-layout.js.map +1 -0
- package/dist/components/combobox.js +1 -1
- package/dist/components/composer.d.ts +16 -1
- package/dist/components/composer.js +103 -20
- package/dist/components/composer.js.map +1 -1
- package/dist/components/data-table.js +1 -1
- package/dist/components/message-actions.d.ts +56 -0
- package/dist/components/message-actions.js +181 -0
- package/dist/components/message-actions.js.map +1 -0
- package/dist/components/secret-input.js +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/voice-recorder.d.ts +28 -0
- package/dist/components/voice-recorder.js +240 -0
- package/dist/components/voice-recorder.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +13 -8
- package/dist/mui/index.d.ts +25 -2
- package/dist/mui/index.js +191 -13
- package/dist/mui/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/theme.css +13 -0
- package/dist/tokens.d.ts +53 -1
- package/dist/tokens.js +97 -1
- package/dist/tokens.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { Button } from "./button.js";
|
|
3
|
+
import { formatDuration } from "./audio-player.js";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import Pause from "@burtson-labs/icons/react/pause";
|
|
7
|
+
import Play from "@burtson-labs/icons/react/play";
|
|
8
|
+
import Trash from "@burtson-labs/icons/react/trash";
|
|
9
|
+
import Square from "@burtson-labs/icons/react/square";
|
|
10
|
+
import Mic from "@burtson-labs/icons/react/mic";
|
|
11
|
+
//#region src/components/voice-recorder.tsx
|
|
12
|
+
var LEVEL_BARS = 12;
|
|
13
|
+
function recordingSupported() {
|
|
14
|
+
return typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function" && typeof globalThis.MediaRecorder === "function";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A microphone control for a composer: tap to record, then pause, stop to
|
|
18
|
+
* keep the clip or discard it. While recording it shows a live level meter
|
|
19
|
+
* and the elapsed time, in the recording colour (a state, not an error).
|
|
20
|
+
* When the browser can't record or the microphone is blocked it says so and
|
|
21
|
+
* how to fix it.
|
|
22
|
+
*/
|
|
23
|
+
function VoiceRecorder({ onRecorded, onCancel, onStateChange, maxDurationMs = 3e5, mimeType, label = "Record a voice message", className, ...props }) {
|
|
24
|
+
const [state, setStateRaw] = React.useState("idle");
|
|
25
|
+
const [elapsed, setElapsed] = React.useState(0);
|
|
26
|
+
const [levels, setLevels] = React.useState(() => Array.from({ length: LEVEL_BARS }, () => 0));
|
|
27
|
+
const recorder = React.useRef(null);
|
|
28
|
+
const stream = React.useRef(null);
|
|
29
|
+
const chunks = React.useRef([]);
|
|
30
|
+
const started = React.useRef(0);
|
|
31
|
+
const pausedTotal = React.useRef(0);
|
|
32
|
+
const pausedAt = React.useRef(0);
|
|
33
|
+
const discard = React.useRef(false);
|
|
34
|
+
const frame = React.useRef(0);
|
|
35
|
+
const audioCtx = React.useRef(null);
|
|
36
|
+
const tick = React.useRef(void 0);
|
|
37
|
+
const setState = React.useCallback((s) => {
|
|
38
|
+
setStateRaw(s);
|
|
39
|
+
onStateChange?.(s);
|
|
40
|
+
}, [onStateChange]);
|
|
41
|
+
const cleanup = React.useCallback(() => {
|
|
42
|
+
cancelAnimationFrame(frame.current);
|
|
43
|
+
clearInterval(tick.current);
|
|
44
|
+
stream.current?.getTracks().forEach((t) => t.stop());
|
|
45
|
+
stream.current = null;
|
|
46
|
+
audioCtx.current?.close().catch(() => void 0);
|
|
47
|
+
audioCtx.current = null;
|
|
48
|
+
setLevels(Array.from({ length: LEVEL_BARS }, () => 0));
|
|
49
|
+
}, []);
|
|
50
|
+
React.useEffect(() => cleanup, [cleanup]);
|
|
51
|
+
const now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
52
|
+
const recordedMs = () => now() - started.current - pausedTotal.current - (pausedAt.current ? now() - pausedAt.current : 0);
|
|
53
|
+
const meter = (s) => {
|
|
54
|
+
const Ctx = globalThis.AudioContext ?? globalThis.webkitAudioContext;
|
|
55
|
+
if (!Ctx) return;
|
|
56
|
+
try {
|
|
57
|
+
const ctx = new Ctx();
|
|
58
|
+
audioCtx.current = ctx;
|
|
59
|
+
const analyser = ctx.createAnalyser();
|
|
60
|
+
analyser.fftSize = 256;
|
|
61
|
+
ctx.createMediaStreamSource(s).connect(analyser);
|
|
62
|
+
const data = new Uint8Array(analyser.frequencyBinCount);
|
|
63
|
+
const draw = () => {
|
|
64
|
+
analyser.getByteFrequencyData(data);
|
|
65
|
+
const per = Math.floor(data.length / LEVEL_BARS) || 1;
|
|
66
|
+
setLevels(Array.from({ length: LEVEL_BARS }, (_, i) => {
|
|
67
|
+
let sum = 0;
|
|
68
|
+
for (let j = i * per; j < (i + 1) * per; j++) sum += data[j] ?? 0;
|
|
69
|
+
return Math.min(1, sum / per / 180);
|
|
70
|
+
}));
|
|
71
|
+
frame.current = requestAnimationFrame(draw);
|
|
72
|
+
};
|
|
73
|
+
draw();
|
|
74
|
+
} catch {
|
|
75
|
+
audioCtx.current?.close().catch(() => void 0);
|
|
76
|
+
audioCtx.current = null;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const start = async () => {
|
|
80
|
+
if (!recordingSupported()) {
|
|
81
|
+
setState("unsupported");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
setState("requesting");
|
|
85
|
+
let s;
|
|
86
|
+
try {
|
|
87
|
+
s = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
88
|
+
} catch (err) {
|
|
89
|
+
const name = err?.name;
|
|
90
|
+
setState(name === "NotAllowedError" || name === "SecurityError" ? "denied" : "error");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
stream.current = s;
|
|
94
|
+
const type = mimeType && MediaRecorder.isTypeSupported?.(mimeType) ? mimeType : void 0;
|
|
95
|
+
let rec;
|
|
96
|
+
try {
|
|
97
|
+
rec = new MediaRecorder(s, type ? { mimeType: type } : void 0);
|
|
98
|
+
} catch {
|
|
99
|
+
cleanup();
|
|
100
|
+
setState("error");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
recorder.current = rec;
|
|
104
|
+
chunks.current = [];
|
|
105
|
+
discard.current = false;
|
|
106
|
+
rec.ondataavailable = (e) => {
|
|
107
|
+
if (e.data && e.data.size > 0) chunks.current.push(e.data);
|
|
108
|
+
};
|
|
109
|
+
rec.onstop = () => {
|
|
110
|
+
const durationMs = Math.round(recordedMs());
|
|
111
|
+
const mime = rec.mimeType || type || "audio/webm";
|
|
112
|
+
cleanup();
|
|
113
|
+
setElapsed(0);
|
|
114
|
+
setState("idle");
|
|
115
|
+
if (!discard.current) onRecorded({
|
|
116
|
+
blob: new Blob(chunks.current, { type: mime }),
|
|
117
|
+
mimeType: mime,
|
|
118
|
+
durationMs
|
|
119
|
+
});
|
|
120
|
+
chunks.current = [];
|
|
121
|
+
};
|
|
122
|
+
started.current = now();
|
|
123
|
+
pausedTotal.current = 0;
|
|
124
|
+
pausedAt.current = 0;
|
|
125
|
+
rec.start(250);
|
|
126
|
+
setState("recording");
|
|
127
|
+
meter(s);
|
|
128
|
+
tick.current = setInterval(() => {
|
|
129
|
+
const ms = recordedMs();
|
|
130
|
+
setElapsed(ms);
|
|
131
|
+
if (ms >= maxDurationMs && rec.state !== "inactive") rec.stop();
|
|
132
|
+
}, 200);
|
|
133
|
+
};
|
|
134
|
+
const pause = () => {
|
|
135
|
+
const rec = recorder.current;
|
|
136
|
+
if (!rec) return;
|
|
137
|
+
if (rec.state === "recording") {
|
|
138
|
+
rec.pause();
|
|
139
|
+
pausedAt.current = now();
|
|
140
|
+
setState("paused");
|
|
141
|
+
} else if (rec.state === "paused") {
|
|
142
|
+
rec.resume();
|
|
143
|
+
pausedTotal.current += now() - pausedAt.current;
|
|
144
|
+
pausedAt.current = 0;
|
|
145
|
+
setState("recording");
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
const stop = (keep) => {
|
|
149
|
+
const rec = recorder.current;
|
|
150
|
+
discard.current = !keep;
|
|
151
|
+
if (rec && rec.state !== "inactive") rec.stop();
|
|
152
|
+
else {
|
|
153
|
+
cleanup();
|
|
154
|
+
setState("idle");
|
|
155
|
+
}
|
|
156
|
+
if (!keep) onCancel?.();
|
|
157
|
+
};
|
|
158
|
+
if (!(state === "recording" || state === "paused")) {
|
|
159
|
+
const note = state === "denied" ? "Microphone access is blocked. Allow it in your browser’s site settings, then try again." : state === "unsupported" ? "This browser can’t record audio. Try a current Chrome, Edge, Firefox or Safari." : state === "error" ? "The microphone could not start. Check that one is connected and not in use." : null;
|
|
160
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
161
|
+
"data-slot": "voice-recorder",
|
|
162
|
+
"data-state": state,
|
|
163
|
+
className: cn("inline-flex items-center gap-2", className),
|
|
164
|
+
...props,
|
|
165
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
166
|
+
type: "button",
|
|
167
|
+
size: "icon-sm",
|
|
168
|
+
variant: "ghost",
|
|
169
|
+
"aria-label": label,
|
|
170
|
+
title: label,
|
|
171
|
+
disabled: state === "requesting" || state === "unsupported",
|
|
172
|
+
onClick: () => void start(),
|
|
173
|
+
className: "pointer-coarse:size-11",
|
|
174
|
+
children: /* @__PURE__ */ jsx(Mic, {})
|
|
175
|
+
}), note && /* @__PURE__ */ jsx("p", {
|
|
176
|
+
role: "alert",
|
|
177
|
+
className: "max-w-xs text-xs text-muted-foreground",
|
|
178
|
+
children: note
|
|
179
|
+
})]
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
183
|
+
"data-slot": "voice-recorder",
|
|
184
|
+
"data-state": state,
|
|
185
|
+
role: "group",
|
|
186
|
+
"aria-label": "Recording",
|
|
187
|
+
className: cn("inline-flex items-center gap-2 rounded-full border border-recording/30 bg-recording/8 py-1 pr-1 pl-3", className),
|
|
188
|
+
...props,
|
|
189
|
+
children: [
|
|
190
|
+
/* @__PURE__ */ jsx("span", {
|
|
191
|
+
"aria-hidden": true,
|
|
192
|
+
className: cn("size-2 shrink-0 rounded-full bg-recording", state === "recording" && "motion-safe:animate-pulse")
|
|
193
|
+
}),
|
|
194
|
+
/* @__PURE__ */ jsx("span", {
|
|
195
|
+
"aria-hidden": true,
|
|
196
|
+
className: "flex h-5 items-center gap-[2px]",
|
|
197
|
+
children: levels.map((l, i) => /* @__PURE__ */ jsx("span", {
|
|
198
|
+
className: "w-[3px] rounded-full bg-recording/80 transition-[height] duration-[var(--duration-fast)] ease-[var(--ease-standard)]",
|
|
199
|
+
style: { height: `${Math.max(15, Math.round(l * 100))}%` }
|
|
200
|
+
}, i))
|
|
201
|
+
}),
|
|
202
|
+
/* @__PURE__ */ jsx("span", {
|
|
203
|
+
role: "timer",
|
|
204
|
+
"aria-label": `${state === "paused" ? "Paused at" : "Recording"} ${formatDuration(elapsed / 1e3)}`,
|
|
205
|
+
className: "font-mono text-xs text-foreground tabular-nums",
|
|
206
|
+
children: formatDuration(elapsed / 1e3)
|
|
207
|
+
}),
|
|
208
|
+
/* @__PURE__ */ jsx(Button, {
|
|
209
|
+
type: "button",
|
|
210
|
+
size: "icon-sm",
|
|
211
|
+
variant: "ghost",
|
|
212
|
+
"aria-label": state === "paused" ? "Resume recording" : "Pause recording",
|
|
213
|
+
onClick: pause,
|
|
214
|
+
className: "rounded-full pointer-coarse:size-11",
|
|
215
|
+
children: state === "paused" ? /* @__PURE__ */ jsx(Play, { className: "fill-current" }) : /* @__PURE__ */ jsx(Pause, { className: "fill-current" })
|
|
216
|
+
}),
|
|
217
|
+
/* @__PURE__ */ jsx(Button, {
|
|
218
|
+
type: "button",
|
|
219
|
+
size: "icon-sm",
|
|
220
|
+
variant: "ghost",
|
|
221
|
+
"aria-label": "Discard recording",
|
|
222
|
+
onClick: () => stop(false),
|
|
223
|
+
className: "rounded-full pointer-coarse:size-11",
|
|
224
|
+
children: /* @__PURE__ */ jsx(Trash, {})
|
|
225
|
+
}),
|
|
226
|
+
/* @__PURE__ */ jsx(Button, {
|
|
227
|
+
type: "button",
|
|
228
|
+
size: "icon-sm",
|
|
229
|
+
"aria-label": "Stop and attach recording",
|
|
230
|
+
onClick: () => stop(true),
|
|
231
|
+
className: "rounded-full bg-recording text-recording-foreground hover:bg-recording/90 pointer-coarse:size-11",
|
|
232
|
+
children: /* @__PURE__ */ jsx(Square, { className: "fill-current" })
|
|
233
|
+
})
|
|
234
|
+
]
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
//#endregion
|
|
238
|
+
export { VoiceRecorder };
|
|
239
|
+
|
|
240
|
+
//# sourceMappingURL=voice-recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice-recorder.js","names":[],"sources":["../../src/components/voice-recorder.tsx"],"sourcesContent":["import Mic from '@burtson-labs/icons/react/mic';\nimport Pause from '@burtson-labs/icons/react/pause';\nimport Play from '@burtson-labs/icons/react/play';\nimport Square from '@burtson-labs/icons/react/square';\nimport Trash from '@burtson-labs/icons/react/trash';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport { formatDuration } from './audio-player';\nimport { Button } from './button';\n\nexport type VoiceRecorderState =\n 'idle' | 'requesting' | 'recording' | 'paused' | 'denied' | 'unsupported' | 'error';\n\nexport interface VoiceRecording {\n blob: Blob;\n mimeType: string;\n durationMs: number;\n}\n\nexport interface VoiceRecorderProps extends React.ComponentProps<'div'> {\n /** The finished recording, after Stop. */\n onRecorded: (recording: VoiceRecording) => void;\n onCancel?: () => void;\n onStateChange?: (state: VoiceRecorderState) => void;\n /** Stops by itself at this length. Default 5 minutes. */\n maxDurationMs?: number;\n /** Preferred container, used when the browser supports it (e.g. \"audio/webm\"). */\n mimeType?: string;\n /** Accessible name of the record button. */\n label?: string;\n}\n\nconst LEVEL_BARS = 12;\n\nfunction recordingSupported() {\n return (\n typeof navigator !== 'undefined' &&\n typeof navigator.mediaDevices?.getUserMedia === 'function' &&\n typeof (globalThis as { MediaRecorder?: unknown }).MediaRecorder === 'function'\n );\n}\n\n/**\n * A microphone control for a composer: tap to record, then pause, stop to\n * keep the clip or discard it. While recording it shows a live level meter\n * and the elapsed time, in the recording colour (a state, not an error).\n * When the browser can't record or the microphone is blocked it says so and\n * how to fix it.\n */\nfunction VoiceRecorder({\n onRecorded,\n onCancel,\n onStateChange,\n maxDurationMs = 5 * 60 * 1000,\n mimeType,\n label = 'Record a voice message',\n className,\n ...props\n}: VoiceRecorderProps) {\n const [state, setStateRaw] = React.useState<VoiceRecorderState>('idle');\n const [elapsed, setElapsed] = React.useState(0);\n const [levels, setLevels] = React.useState<number[]>(() =>\n Array.from({ length: LEVEL_BARS }, () => 0),\n );\n const recorder = React.useRef<MediaRecorder | null>(null);\n const stream = React.useRef<MediaStream | null>(null);\n const chunks = React.useRef<Blob[]>([]);\n const started = React.useRef(0);\n const pausedTotal = React.useRef(0);\n const pausedAt = React.useRef(0);\n const discard = React.useRef(false);\n const frame = React.useRef(0);\n const audioCtx = React.useRef<AudioContext | null>(null);\n const tick = React.useRef<ReturnType<typeof setInterval> | undefined>(undefined);\n\n const setState = React.useCallback(\n (s: VoiceRecorderState) => {\n setStateRaw(s);\n onStateChange?.(s);\n },\n [onStateChange],\n );\n\n const cleanup = React.useCallback(() => {\n cancelAnimationFrame(frame.current);\n clearInterval(tick.current);\n stream.current?.getTracks().forEach((t) => t.stop());\n stream.current = null;\n void audioCtx.current?.close().catch(() => undefined);\n audioCtx.current = null;\n setLevels(Array.from({ length: LEVEL_BARS }, () => 0));\n }, []);\n\n React.useEffect(() => cleanup, [cleanup]);\n\n const now = () => (typeof performance !== 'undefined' ? performance.now() : Date.now());\n const recordedMs = () =>\n now() -\n started.current -\n pausedTotal.current -\n (pausedAt.current ? now() - pausedAt.current : 0);\n\n const meter = (s: MediaStream) => {\n const Ctx =\n globalThis.AudioContext ??\n (globalThis as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Ctx) return;\n try {\n const ctx = new Ctx();\n audioCtx.current = ctx;\n const analyser = ctx.createAnalyser();\n analyser.fftSize = 256;\n ctx.createMediaStreamSource(s).connect(analyser);\n const data = new Uint8Array(analyser.frequencyBinCount);\n const draw = () => {\n analyser.getByteFrequencyData(data);\n const per = Math.floor(data.length / LEVEL_BARS) || 1;\n setLevels(\n Array.from({ length: LEVEL_BARS }, (_, i) => {\n let sum = 0;\n for (let j = i * per; j < (i + 1) * per; j++) sum += data[j] ?? 0;\n return Math.min(1, sum / per / 180);\n }),\n );\n frame.current = requestAnimationFrame(draw);\n };\n draw();\n } catch {\n // No meter is fine; recording still works. Drop the half-made context.\n void audioCtx.current?.close().catch(() => undefined);\n audioCtx.current = null;\n }\n };\n\n const start = async () => {\n if (!recordingSupported()) {\n setState('unsupported');\n return;\n }\n setState('requesting');\n let s: MediaStream;\n try {\n s = await navigator.mediaDevices.getUserMedia({ audio: true });\n } catch (err) {\n const name = (err as { name?: string })?.name;\n setState(name === 'NotAllowedError' || name === 'SecurityError' ? 'denied' : 'error');\n return;\n }\n stream.current = s;\n const type = mimeType && MediaRecorder.isTypeSupported?.(mimeType) ? mimeType : undefined;\n let rec: MediaRecorder;\n try {\n rec = new MediaRecorder(s, type ? { mimeType: type } : undefined);\n } catch {\n cleanup();\n setState('error');\n return;\n }\n recorder.current = rec;\n chunks.current = [];\n discard.current = false;\n rec.ondataavailable = (e) => {\n if (e.data && e.data.size > 0) chunks.current.push(e.data);\n };\n rec.onstop = () => {\n const durationMs = Math.round(recordedMs());\n const mime = rec.mimeType || type || 'audio/webm';\n cleanup();\n setElapsed(0);\n setState('idle');\n if (!discard.current)\n onRecorded({ blob: new Blob(chunks.current, { type: mime }), mimeType: mime, durationMs });\n chunks.current = [];\n };\n started.current = now();\n pausedTotal.current = 0;\n pausedAt.current = 0;\n rec.start(250);\n setState('recording');\n meter(s);\n tick.current = setInterval(() => {\n const ms = recordedMs();\n setElapsed(ms);\n if (ms >= maxDurationMs && rec.state !== 'inactive') rec.stop();\n }, 200);\n };\n\n const pause = () => {\n const rec = recorder.current;\n if (!rec) return;\n if (rec.state === 'recording') {\n rec.pause();\n pausedAt.current = now();\n setState('paused');\n } else if (rec.state === 'paused') {\n rec.resume();\n pausedTotal.current += now() - pausedAt.current;\n pausedAt.current = 0;\n setState('recording');\n }\n };\n\n const stop = (keep: boolean) => {\n const rec = recorder.current;\n discard.current = !keep;\n if (rec && rec.state !== 'inactive') rec.stop();\n else {\n cleanup();\n setState('idle');\n }\n if (!keep) onCancel?.();\n };\n\n const live = state === 'recording' || state === 'paused';\n\n if (!live) {\n const note =\n state === 'denied'\n ? 'Microphone access is blocked. Allow it in your browser’s site settings, then try again.'\n : state === 'unsupported'\n ? 'This browser can’t record audio. Try a current Chrome, Edge, Firefox or Safari.'\n : state === 'error'\n ? 'The microphone could not start. Check that one is connected and not in use.'\n : null;\n return (\n <div\n data-slot=\"voice-recorder\"\n data-state={state}\n className={cn('inline-flex items-center gap-2', className)}\n {...props}\n >\n <Button\n type=\"button\"\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={label}\n title={label}\n disabled={state === 'requesting' || state === 'unsupported'}\n onClick={() => void start()}\n className=\"pointer-coarse:size-11\"\n >\n <Mic />\n </Button>\n {note && (\n <p role=\"alert\" className=\"max-w-xs text-xs text-muted-foreground\">\n {note}\n </p>\n )}\n </div>\n );\n }\n\n return (\n <div\n data-slot=\"voice-recorder\"\n data-state={state}\n role=\"group\"\n aria-label=\"Recording\"\n className={cn(\n 'inline-flex items-center gap-2 rounded-full border border-recording/30 bg-recording/8 py-1 pr-1 pl-3',\n className,\n )}\n {...props}\n >\n <span\n aria-hidden\n className={cn(\n 'size-2 shrink-0 rounded-full bg-recording',\n state === 'recording' && 'motion-safe:animate-pulse',\n )}\n />\n <span aria-hidden className=\"flex h-5 items-center gap-[2px]\">\n {levels.map((l, i) => (\n <span\n key={i}\n className=\"w-[3px] rounded-full bg-recording/80 transition-[height] duration-[var(--duration-fast)] ease-[var(--ease-standard)]\"\n style={{ height: `${Math.max(15, Math.round(l * 100))}%` }}\n />\n ))}\n </span>\n <span\n role=\"timer\"\n aria-label={`${state === 'paused' ? 'Paused at' : 'Recording'} ${formatDuration(elapsed / 1000)}`}\n className=\"font-mono text-xs text-foreground tabular-nums\"\n >\n {formatDuration(elapsed / 1000)}\n </span>\n <Button\n type=\"button\"\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={state === 'paused' ? 'Resume recording' : 'Pause recording'}\n onClick={pause}\n className=\"rounded-full pointer-coarse:size-11\"\n >\n {state === 'paused' ? (\n <Play className=\"fill-current\" />\n ) : (\n <Pause className=\"fill-current\" />\n )}\n </Button>\n <Button\n type=\"button\"\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label=\"Discard recording\"\n onClick={() => stop(false)}\n className=\"rounded-full pointer-coarse:size-11\"\n >\n <Trash />\n </Button>\n <Button\n type=\"button\"\n size=\"icon-sm\"\n aria-label=\"Stop and attach recording\"\n onClick={() => stop(true)}\n className=\"rounded-full bg-recording text-recording-foreground hover:bg-recording/90 pointer-coarse:size-11\"\n >\n <Square className=\"fill-current\" />\n </Button>\n </div>\n );\n}\n\nexport { VoiceRecorder };\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,11 +6,14 @@ export * from './components/alert';
|
|
|
6
6
|
export * from './components/alert-dialog';
|
|
7
7
|
export * from './components/app-shell';
|
|
8
8
|
export * from './components/attachment';
|
|
9
|
+
export * from './components/audio-player';
|
|
9
10
|
export * from './components/avatar';
|
|
10
11
|
export * from './components/badge';
|
|
11
12
|
export * from './components/breadcrumb';
|
|
12
13
|
export * from './components/button';
|
|
13
14
|
export * from './components/card';
|
|
15
|
+
export * from './components/chat-history';
|
|
16
|
+
export * from './components/chat-layout';
|
|
14
17
|
export * from './components/checkbox';
|
|
15
18
|
export * from './components/collapsible';
|
|
16
19
|
export * from './components/combobox';
|
|
@@ -33,6 +36,7 @@ export * from './components/label';
|
|
|
33
36
|
export * from './components/markdown';
|
|
34
37
|
export * from './components/menubar';
|
|
35
38
|
export * from './components/message';
|
|
39
|
+
export * from './components/message-actions';
|
|
36
40
|
export * from './components/mobile-nav';
|
|
37
41
|
export * from './components/navigation-menu';
|
|
38
42
|
export * from './components/onboarding-checklist';
|
|
@@ -66,3 +70,4 @@ export * from './components/toolbar';
|
|
|
66
70
|
export * from './components/tooltip';
|
|
67
71
|
export * from './components/tour';
|
|
68
72
|
export * from './components/tree-view';
|
|
73
|
+
export * from './components/voice-recorder';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "./lib/utils.js";
|
|
2
2
|
import { isApplePlatform, modKeyLabel, shortcutLabel } from "./lib/platform.js";
|
|
3
|
-
import tokens, { brand, dark, fontMono, fontSans, light, motion, radius, shadow } from "./tokens.js";
|
|
3
|
+
import tokens, { accentTokens, brand, contrast, dark, duration, easing, elevation, fontMono, fontSans, light, mix, motion, radius, shadow } from "./tokens.js";
|
|
4
4
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./components/accordion.js";
|
|
5
5
|
import { Alert, AlertDescription, AlertTitle, alertVariants } from "./components/alert.js";
|
|
6
6
|
import { Button, buttonVariants } from "./components/button.js";
|
|
@@ -9,24 +9,30 @@ import { AppShell, AppShellBody, AppShellContent, AppShellHeader, AppShellMain,
|
|
|
9
9
|
import { Progress } from "./components/progress.js";
|
|
10
10
|
import { Spinner } from "./components/spinner.js";
|
|
11
11
|
import { AttachmentItem, AttachmentTray, UploadQueue, formatBytes } from "./components/attachment.js";
|
|
12
|
+
import { AudioPlayer, VoiceMessage, computePeaks, formatDuration, peaksFromChannelData } from "./components/audio-player.js";
|
|
12
13
|
import { Avatar, AvatarFallback, AvatarImage } from "./components/avatar.js";
|
|
13
14
|
import { Badge, badgeVariants } from "./components/badge.js";
|
|
14
15
|
import { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "./components/breadcrumb.js";
|
|
15
16
|
import { Card, CardAction, CardContent, CardDescription, CardEyebrow, CardFooter, CardHeader, CardTitle, cardVariants } from "./components/card.js";
|
|
17
|
+
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger, menuItemClasses, surfaceClasses } from "./components/popover.js";
|
|
18
|
+
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./components/dropdown-menu.js";
|
|
19
|
+
import { ChatHistory, groupConversations, matchesConversation } from "./components/chat-history.js";
|
|
20
|
+
import { IconButton } from "./components/icon-button.js";
|
|
21
|
+
import { ResizablePanel, ResizablePanelGroup, ResizeHandle, useGroupRef, usePanelRef } from "./components/resizable.js";
|
|
22
|
+
import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from "./components/sheet.js";
|
|
23
|
+
import { ChatLayout, useFullscreen } from "./components/chat-layout.js";
|
|
16
24
|
import { Checkbox } from "./components/checkbox.js";
|
|
17
25
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./components/collapsible.js";
|
|
18
26
|
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from "./components/dialog.js";
|
|
19
27
|
import { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from "./components/command.js";
|
|
20
28
|
import { Input, fieldClasses } from "./components/input.js";
|
|
21
|
-
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger, menuItemClasses, surfaceClasses } from "./components/popover.js";
|
|
22
29
|
import { Combobox } from "./components/combobox.js";
|
|
23
|
-
import { Composer, Suggestions } from "./components/composer.js";
|
|
30
|
+
import { Composer, Suggestions, acceptsFile } from "./components/composer.js";
|
|
24
31
|
import { Status, StatusDot, statusDotVariants } from "./components/status.js";
|
|
25
32
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./components/tooltip.js";
|
|
26
33
|
import { ConnectionBanner, ConnectionStatus, SyncStatus } from "./components/connection-status.js";
|
|
27
34
|
import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from "./components/context-menu.js";
|
|
28
35
|
import { Conversation } from "./components/conversation.js";
|
|
29
|
-
import { IconButton } from "./components/icon-button.js";
|
|
30
36
|
import { Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, toastVariants } from "./components/toast.js";
|
|
31
37
|
import { Toaster, toast } from "./components/toaster.js";
|
|
32
38
|
import { CopyButton } from "./components/copy-button.js";
|
|
@@ -34,7 +40,6 @@ import { Pagination, paginationRange } from "./components/pagination.js";
|
|
|
34
40
|
import { Skeleton } from "./components/skeleton.js";
|
|
35
41
|
import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "./components/table.js";
|
|
36
42
|
import { DataTable } from "./components/data-table.js";
|
|
37
|
-
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./components/dropdown-menu.js";
|
|
38
43
|
import { EditorTabs } from "./components/editor-tabs.js";
|
|
39
44
|
import { EmptyState, EmptyStateActions, EmptyStateDescription, EmptyStateIcon, EmptyStateTitle } from "./components/empty-state.js";
|
|
40
45
|
import { Field, FieldDescription, FieldError, FieldHeader, FieldHint, FieldLabel } from "./components/field.js";
|
|
@@ -43,18 +48,17 @@ import { Label } from "./components/label.js";
|
|
|
43
48
|
import { CodeBlock, Markdown, parseMarkdown } from "./components/markdown.js";
|
|
44
49
|
import { Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger } from "./components/menubar.js";
|
|
45
50
|
import { Message } from "./components/message.js";
|
|
51
|
+
import { MessageActions, MessageAttachments, MessageEditor } from "./components/message-actions.js";
|
|
46
52
|
import { MobileNav } from "./components/mobile-nav.js";
|
|
47
53
|
import { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, navigationMenuTriggerStyle } from "./components/navigation-menu.js";
|
|
48
54
|
import { OnboardingChecklist } from "./components/onboarding-checklist.js";
|
|
49
55
|
import { PageHeader, PageHeaderActions, PageHeaderDescription, PageHeaderEyebrow, PageHeaderMain, PageHeaderTitle } from "./components/page-header.js";
|
|
50
56
|
import { RadioGroup, RadioGroupItem } from "./components/radio-group.js";
|
|
51
57
|
import { Reasoning, StreamingIndicator } from "./components/reasoning.js";
|
|
52
|
-
import { ResizablePanel, ResizablePanelGroup, ResizeHandle, useGroupRef, usePanelRef } from "./components/resizable.js";
|
|
53
58
|
import { ScrollArea, ScrollBar } from "./components/scroll-area.js";
|
|
54
59
|
import { SecretInput } from "./components/secret-input.js";
|
|
55
60
|
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue } from "./components/select.js";
|
|
56
61
|
import { Separator } from "./components/separator.js";
|
|
57
|
-
import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from "./components/sheet.js";
|
|
58
62
|
import { Slider } from "./components/slider.js";
|
|
59
63
|
import { SourceCitation, SourceList, safeSourceHref } from "./components/source.js";
|
|
60
64
|
import { StatCard } from "./components/stat-card.js";
|
|
@@ -66,4 +70,5 @@ import { ToolApproval, ToolCall } from "./components/tool-call.js";
|
|
|
66
70
|
import { Toolbar, ToolbarGroup, ToolbarSeparator, ToolbarSpacer } from "./components/toolbar.js";
|
|
67
71
|
import { Spotlight, Tour, TourAnchor, findTourTarget, tourTarget, waitForTourTarget } from "./components/tour.js";
|
|
68
72
|
import { TreeView, flattenTree } from "./components/tree-view.js";
|
|
69
|
-
|
|
73
|
+
import { VoiceRecorder } from "./components/voice-recorder.js";
|
|
74
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, AppShellBody, AppShellContent, AppShellHeader, AppShellMain, AppShellSidebar, AttachmentItem, AttachmentTray, AudioPlayer, Avatar, AvatarFallback, AvatarImage, Badge, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Card, CardAction, CardContent, CardDescription, CardEyebrow, CardFooter, CardHeader, CardTitle, ChatHistory, ChatLayout, Checkbox, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, Composer, ConnectionBanner, ConnectionStatus, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, Conversation, CopyButton, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EditorTabs, EmptyState, EmptyStateActions, EmptyStateDescription, EmptyStateIcon, EmptyStateTitle, Field, FieldDescription, FieldError, FieldHeader, FieldHint, FieldLabel, IconButton, Input, Kbd, KbdGroup, Label, Markdown, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, Message, MessageActions, MessageAttachments, MessageEditor, MobileNav, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, OnboardingChecklist, PageHeader, PageHeaderActions, PageHeaderDescription, PageHeaderEyebrow, PageHeaderMain, PageHeaderTitle, Pagination, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, Reasoning, ResizablePanel, ResizablePanelGroup, ResizeHandle, ScrollArea, ScrollBar, SecretInput, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Skeleton, Slider, SourceCitation, SourceList, Spinner, Spotlight, StatCard, Status, StatusDot, Steps, StreamingIndicator, Suggestions, Switch, SyncStatus, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, ToolApproval, ToolCall, Toolbar, ToolbarGroup, ToolbarSeparator, ToolbarSpacer, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Tour, TourAnchor, TreeView, UploadQueue, VoiceMessage, VoiceRecorder, accentTokens, acceptsFile, alertVariants, badgeVariants, brand, buttonVariants, cardVariants, cn, computePeaks, contrast, dark, duration, easing, elevation, fieldClasses, findTourTarget, flattenTree, fontMono, fontSans, formatBytes, formatDuration, groupConversations, isApplePlatform, light, matchesConversation, menuItemClasses, mix, modKeyLabel, motion, navigationMenuTriggerStyle, paginationRange, parseMarkdown, peaksFromChannelData, radius, safeSourceHref, shadow, shortcutLabel, statusDotVariants, surfaceClasses, toast, toastVariants, tokens, tourTarget, useFullscreen, useGroupRef, usePanelRef, waitForTourTarget };
|
package/dist/mui/index.d.ts
CHANGED
|
@@ -5,9 +5,32 @@
|
|
|
5
5
|
* import { ThemeProvider } from '@mui/material/styles';
|
|
6
6
|
* import { createBurtsonTheme } from '@burtson-labs/ui/mui';
|
|
7
7
|
* <ThemeProvider theme={createBurtsonTheme('dark')}>…</ThemeProvider>
|
|
8
|
+
* <ThemeProvider theme={createBurtsonTheme({ mode: 'dark', accent: '#2563eb', density: 'compact' })}>
|
|
9
|
+
*
|
|
10
|
+
* MUI is an adapter here: every colour, radius, shadow and timing comes from
|
|
11
|
+
* the Burtson tokens, and the component overrides give MUI controls the same
|
|
12
|
+
* shape and states as the Burtson components (focus ring, radius per role,
|
|
13
|
+
* no uppercase buttons, quiet borders instead of heavy shadows).
|
|
8
14
|
*/
|
|
9
15
|
import { type Theme, type ThemeOptions } from '@mui/material/styles';
|
|
16
|
+
import { type Palette } from '../tokens';
|
|
10
17
|
export type BurtsonMode = 'light' | 'dark';
|
|
18
|
+
export type BurtsonDensity = 'comfortable' | 'compact';
|
|
19
|
+
export interface BurtsonThemeConfig {
|
|
20
|
+
mode?: BurtsonMode;
|
|
21
|
+
/** Any hex colour; primary, brand, soft and ring tones are derived with contrast kept. */
|
|
22
|
+
accent?: string;
|
|
23
|
+
/** `compact` for admin and data-dense screens: smaller controls, dense lists and tables. */
|
|
24
|
+
density?: BurtsonDensity;
|
|
25
|
+
/** Replace individual palette tokens (a product skin's tinted surfaces). */
|
|
26
|
+
palette?: Partial<Palette>;
|
|
27
|
+
}
|
|
28
|
+
/** The full token palette for a config: base mode, accent-derived brand tones, then explicit overrides. */
|
|
29
|
+
export declare function burtsonPalette(config?: BurtsonThemeConfig): Palette;
|
|
11
30
|
/** Theme options only, for apps that deep-merge their own overrides first. */
|
|
12
|
-
export declare function burtsonThemeOptions(
|
|
13
|
-
|
|
31
|
+
export declare function burtsonThemeOptions(modeOrConfig?: BurtsonMode | BurtsonThemeConfig): ThemeOptions;
|
|
32
|
+
/**
|
|
33
|
+
* `createBurtsonTheme('dark')` or `createBurtsonTheme({ mode, accent, density })`,
|
|
34
|
+
* then any MUI overrides, merged in order.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createBurtsonTheme(modeOrConfig?: BurtsonMode | BurtsonThemeConfig, ...overrides: ThemeOptions[]): Theme;
|