@epicbagel/saas-kit 1.11.0 → 1.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.
Files changed (30) hide show
  1. package/README.md +34 -12
  2. package/dist/{chunk-ZZ7ICQR2.cjs → chunk-7Q4EFCUI.cjs} +93 -28
  3. package/dist/chunk-JKVVTFAP.js +294 -0
  4. package/dist/{chunk-UOVA4YKW.js → chunk-KF4X37DP.js} +2 -15
  5. package/dist/chunk-QWUMIAYD.cjs +294 -0
  6. package/dist/{chunk-HLAHOVEI.js → chunk-RPSRCB5O.js} +79 -14
  7. package/dist/{chunk-7QTFYEPE.cjs → chunk-YMI3X2YL.cjs} +2 -15
  8. package/dist/hooks/use-dictation.cjs +2 -2
  9. package/dist/hooks/use-dictation.d.cts +27 -5
  10. package/dist/hooks/use-dictation.d.ts +27 -5
  11. package/dist/hooks/use-dictation.js +1 -1
  12. package/dist/primitives/chat.cjs +17 -13
  13. package/dist/primitives/chat.d.cts +13 -6
  14. package/dist/primitives/chat.d.ts +13 -6
  15. package/dist/primitives/chat.js +12 -8
  16. package/dist/primitives/dictation-provider.cjs +2 -2
  17. package/dist/primitives/dictation-provider.d.cts +1 -1
  18. package/dist/primitives/dictation-provider.d.ts +1 -1
  19. package/dist/primitives/dictation-provider.js +1 -1
  20. package/dist/primitives/voice-dictation-button.cjs +3 -3
  21. package/dist/primitives/voice-dictation-button.d.cts +8 -6
  22. package/dist/primitives/voice-dictation-button.d.ts +8 -6
  23. package/dist/primitives/voice-dictation-button.js +2 -2
  24. package/dist/primitives/voice-session-controls.cjs +2 -2
  25. package/dist/primitives/voice-session-controls.d.cts +2 -2
  26. package/dist/primitives/voice-session-controls.d.ts +2 -2
  27. package/dist/primitives/voice-session-controls.js +1 -1
  28. package/package.json +1 -1
  29. package/dist/chunk-KQBXAQAY.cjs +0 -221
  30. package/dist/chunk-T3XMHWYV.js +0 -221
@@ -1,221 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
2
-
3
-
4
- var _chunkNKWHDI4Ccjs = require('./chunk-NKWHDI4C.cjs');
5
-
6
-
7
- var _chunkJDEXZGIYcjs = require('./chunk-JDEXZGIY.cjs');
8
-
9
-
10
- var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
11
-
12
-
13
- var _chunkZZ7ICQR2cjs = require('./chunk-ZZ7ICQR2.cjs');
14
-
15
- // src/primitives/voice-dictation-button.tsx
16
-
17
-
18
-
19
-
20
-
21
-
22
- var _react = require('react');
23
- var _reactdialog = require('@radix-ui/react-dialog'); var DialogPrimitive = _interopRequireWildcard(_reactdialog);
24
- var _lucidereact = require('lucide-react');
25
- var _jsxruntime = require('react/jsx-runtime');
26
- function VoiceDictationButton({
27
- onCommit,
28
- onCancel,
29
- title = "Dictate your message",
30
- caption = "Recording",
31
- lang = "en-US",
32
- continuous = true,
33
- className,
34
- size = "sm",
35
- showLabel = false,
36
- children,
37
- disabled = false,
38
- visibility = "visible",
39
- shrink = true
40
- }) {
41
- const [open, setOpen] = _react.useState.call(void 0, false);
42
- const capture = _chunkZZ7ICQR2cjs.useDictation.call(void 0, { lang, continuous });
43
- const { transcript, interimTranscript: interim, supported, error } = capture;
44
- const generation = _react.useRef.call(void 0, 0);
45
- const finishing = _react.useRef.call(void 0, false);
46
- const previousFocus = _react.useRef.call(void 0, null);
47
- const elapsed = useRecordingTime(capture.state);
48
- const busy = capture.state === "requesting" || capture.state === "transcribing";
49
- const openOverlay = _react.useCallback.call(void 0, () => {
50
- previousFocus.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
51
- generation.current++;
52
- finishing.current = false;
53
- setOpen(true);
54
- void capture.start();
55
- }, [capture.start]);
56
- const closeOverlay = _react.useCallback.call(void 0, () => {
57
- generation.current++;
58
- finishing.current = false;
59
- capture.cancel();
60
- setOpen(false);
61
- }, [capture.cancel]);
62
- const handleDone = _react.useCallback.call(void 0, async () => {
63
- if (finishing.current || busy) return;
64
- if (capture.state === "error" && !capture.canRetry) {
65
- void capture.start();
66
- return;
67
- }
68
- finishing.current = true;
69
- const started = generation.current;
70
- const final = await (capture.canRetry ? capture.retry() : capture.stop());
71
- if (started !== generation.current) return;
72
- finishing.current = false;
73
- if (final === null) return;
74
- closeOverlay();
75
- if (final) onCommit(final);
76
- else _optionalChain([onCancel, 'optionalCall', _ => _()]);
77
- }, [busy, capture.canRetry, capture.retry, capture.start, capture.state, capture.stop, onCommit, onCancel, closeOverlay]);
78
- const handleCancel = _react.useCallback.call(void 0, () => {
79
- closeOverlay();
80
- _optionalChain([onCancel, 'optionalCall', _2 => _2()]);
81
- }, [closeOverlay, onCancel]);
82
- _react.useEffect.call(void 0, () => {
83
- if (open && capture.state === "idle") handleCancel();
84
- }, [capture.state, handleCancel, open]);
85
- _react.useEffect.call(void 0, () => {
86
- return () => {
87
- generation.current++;
88
- };
89
- }, []);
90
- if (!supported) return null;
91
- const displayTranscript = `${transcript}${interim ? (transcript ? " " : "") + interim : ""}`;
92
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
93
- children ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
94
- "button",
95
- {
96
- type: "button",
97
- onClick: openOverlay,
98
- disabled,
99
- "aria-label": "Start dictation",
100
- className: _chunkKWLSBTB2cjs.cn.call(void 0, visibility === "hidden" && "hidden", className),
101
- children
102
- }
103
- ) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
104
- _chunkJDEXZGIYcjs.Button,
105
- {
106
- type: "button",
107
- onClick: openOverlay,
108
- disabled,
109
- variant: "outline",
110
- size,
111
- "aria-label": "Start dictation",
112
- className: _chunkKWLSBTB2cjs.cn.call(void 0, "transition-colors", visibility === "hidden" && "hidden", !shrink && "shrink-0", className),
113
- children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "flex items-center justify-center gap-2", children: [
114
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Mic, { className: "h-4 w-4" }),
115
- showLabel && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: "Speak" })
116
- ] })
117
- }
118
- ),
119
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
120
- DialogPrimitive.Root,
121
- {
122
- open,
123
- onOpenChange: (next) => {
124
- if (!next) handleCancel();
125
- },
126
- children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DialogPrimitive.Portal, { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
127
- DialogPrimitive.Content,
128
- {
129
- "aria-label": "Voice dictation",
130
- "aria-labelledby": void 0,
131
- "data-slot": "voice-dictation-panel",
132
- "data-state": capture.state,
133
- onCloseAutoFocus: (event) => {
134
- event.preventDefault();
135
- if (_optionalChain([previousFocus, 'access', _3 => _3.current, 'optionalAccess', _4 => _4.isConnected])) previousFocus.current.focus();
136
- },
137
- onKeyDown: (event) => {
138
- if (event.key === "Enter" && !event.shiftKey && !(event.target instanceof HTMLElement && event.target.closest("button, input, textarea, select"))) {
139
- event.preventDefault();
140
- void handleDone();
141
- }
142
- },
143
- className: "fixed bottom-4 left-1/2 z-[200] flex max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-sm -translate-x-1/2 flex-col gap-3 overflow-y-auto rounded-2xl border border-border bg-background p-4 text-foreground shadow-lg outline-hidden sm:bottom-6",
144
- children: [
145
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex min-w-0 items-start justify-between gap-3", children: [
146
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "min-w-0 space-y-1", children: [
147
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DialogPrimitive.Title, { className: "text-sm font-medium", children: title || "Voice dictation" }),
148
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { role: "status", className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
149
- busy ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.LoaderCircle, { "aria-hidden": "true", className: "size-3.5 animate-spin motion-reduce:animate-none" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Mic, { "aria-hidden": "true", className: "size-3.5" }),
150
- capture.state === "transcribing" ? "Transcribing\u2026" : capture.state === "requesting" ? "Waiting for microphone\u2026" : error ? "Recording not saved" : capture.state === "ready" ? "Ready" : caption
151
- ] })
152
- ] }),
153
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "time", { "aria-label": "Recording duration", dateTime: `PT${elapsed}S`, className: "pt-0.5 font-mono text-xs tabular-nums text-muted-foreground", children: [
154
- Math.floor(elapsed / 60).toString().padStart(2, "0"),
155
- ":",
156
- (elapsed % 60).toString().padStart(2, "0")
157
- ] })
158
- ] }),
159
- capture.audioLevels && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DictationWaveform, { source: capture.audioLevels, state: capture.state }),
160
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DialogPrimitive.Description, { className: "text-xs text-muted-foreground", children: capture.state === "transcribing" ? "Converting your recording to text\u2026" : capture.state === "requesting" ? "Allow microphone access to start." : error ? capture.canRetry ? "Retry this take or record again." : "Check microphone access, then try again." : "Speak, then choose Done to add the text." }),
161
- displayTranscript && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { className: "max-h-24 overflow-y-auto text-sm leading-relaxed", children: [
162
- transcript,
163
- interim && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "text-muted-foreground", children: [
164
- transcript ? " " : "",
165
- interim
166
- ] })
167
- ] }),
168
- error && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { role: "alert", className: "text-xs text-destructive", children: error }),
169
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-wrap items-center justify-end gap-2", children: [
170
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { type: "button", variant: "ghost", size: "sm", className: "min-h-[44px]", onClick: handleCancel, children: "Cancel" }),
171
- capture.canRetry && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { type: "button", variant: "outline", size: "sm", className: "min-h-[44px]", onClick: () => void capture.start(), children: "Record again" }),
172
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { type: "button", size: "sm", className: "min-h-[44px]", onClick: () => void handleDone(), disabled: busy, children: capture.state === "transcribing" ? "Transcribing\u2026" : capture.canRetry ? "Retry transcription" : error ? "Try again" : "Done" })
173
- ] })
174
- ]
175
- }
176
- ) })
177
- }
178
- )
179
- ] });
180
- }
181
- var SILENCE = [];
182
- var silence = () => SILENCE;
183
- function DictationWaveform({ source, state }) {
184
- const bands = _react.useSyncExternalStore.call(void 0, source.subscribe, source.getSnapshot, silence);
185
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
186
- _chunkNKWHDI4Ccjs.AgentAudioVisualizerBar,
187
- {
188
- "aria-hidden": "true",
189
- "data-slot": "dictation-waveform",
190
- size: "icon",
191
- barCount: 24,
192
- state: state === "recording" ? "listening" : state === "transcribing" ? "thinking" : state === "requesting" ? "connecting" : "disconnected",
193
- volumeBands: state === "recording" ? bands : void 0,
194
- className: "h-9 w-full text-foreground"
195
- }
196
- );
197
- }
198
- function useRecordingTime(state) {
199
- const [elapsed, setElapsed] = _react.useState.call(void 0, 0);
200
- const startedAt = _react.useRef.call(void 0, null);
201
- _react.useEffect.call(void 0, () => {
202
- if (state !== "recording") {
203
- if (state === "idle" || state === "requesting") setElapsed(0);
204
- else if (startedAt.current !== null) setElapsed(Math.floor((performance.now() - startedAt.current) / 1e3));
205
- startedAt.current = null;
206
- return;
207
- }
208
- startedAt.current = performance.now();
209
- const update = () => setElapsed(Math.floor((performance.now() - startedAt.current) / 1e3));
210
- update();
211
- const timer = setInterval(update, 1e3);
212
- return () => {
213
- clearInterval(timer);
214
- };
215
- }, [state]);
216
- return elapsed;
217
- }
218
-
219
-
220
-
221
- exports.VoiceDictationButton = VoiceDictationButton;
@@ -1,221 +0,0 @@
1
- "use client";
2
- import {
3
- AgentAudioVisualizerBar
4
- } from "./chunk-5OF6D3GH.js";
5
- import {
6
- Button
7
- } from "./chunk-W326GKML.js";
8
- import {
9
- cn
10
- } from "./chunk-G6SRSWXB.js";
11
- import {
12
- useDictation
13
- } from "./chunk-HLAHOVEI.js";
14
-
15
- // src/primitives/voice-dictation-button.tsx
16
- import {
17
- useCallback,
18
- useEffect,
19
- useRef,
20
- useState,
21
- useSyncExternalStore
22
- } from "react";
23
- import * as DialogPrimitive from "@radix-ui/react-dialog";
24
- import { Mic, LoaderCircle } from "lucide-react";
25
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
26
- function VoiceDictationButton({
27
- onCommit,
28
- onCancel,
29
- title = "Dictate your message",
30
- caption = "Recording",
31
- lang = "en-US",
32
- continuous = true,
33
- className,
34
- size = "sm",
35
- showLabel = false,
36
- children,
37
- disabled = false,
38
- visibility = "visible",
39
- shrink = true
40
- }) {
41
- const [open, setOpen] = useState(false);
42
- const capture = useDictation({ lang, continuous });
43
- const { transcript, interimTranscript: interim, supported, error } = capture;
44
- const generation = useRef(0);
45
- const finishing = useRef(false);
46
- const previousFocus = useRef(null);
47
- const elapsed = useRecordingTime(capture.state);
48
- const busy = capture.state === "requesting" || capture.state === "transcribing";
49
- const openOverlay = useCallback(() => {
50
- previousFocus.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
51
- generation.current++;
52
- finishing.current = false;
53
- setOpen(true);
54
- void capture.start();
55
- }, [capture.start]);
56
- const closeOverlay = useCallback(() => {
57
- generation.current++;
58
- finishing.current = false;
59
- capture.cancel();
60
- setOpen(false);
61
- }, [capture.cancel]);
62
- const handleDone = useCallback(async () => {
63
- if (finishing.current || busy) return;
64
- if (capture.state === "error" && !capture.canRetry) {
65
- void capture.start();
66
- return;
67
- }
68
- finishing.current = true;
69
- const started = generation.current;
70
- const final = await (capture.canRetry ? capture.retry() : capture.stop());
71
- if (started !== generation.current) return;
72
- finishing.current = false;
73
- if (final === null) return;
74
- closeOverlay();
75
- if (final) onCommit(final);
76
- else onCancel?.();
77
- }, [busy, capture.canRetry, capture.retry, capture.start, capture.state, capture.stop, onCommit, onCancel, closeOverlay]);
78
- const handleCancel = useCallback(() => {
79
- closeOverlay();
80
- onCancel?.();
81
- }, [closeOverlay, onCancel]);
82
- useEffect(() => {
83
- if (open && capture.state === "idle") handleCancel();
84
- }, [capture.state, handleCancel, open]);
85
- useEffect(() => {
86
- return () => {
87
- generation.current++;
88
- };
89
- }, []);
90
- if (!supported) return null;
91
- const displayTranscript = `${transcript}${interim ? (transcript ? " " : "") + interim : ""}`;
92
- return /* @__PURE__ */ jsxs(Fragment, { children: [
93
- children ? /* @__PURE__ */ jsx(
94
- "button",
95
- {
96
- type: "button",
97
- onClick: openOverlay,
98
- disabled,
99
- "aria-label": "Start dictation",
100
- className: cn(visibility === "hidden" && "hidden", className),
101
- children
102
- }
103
- ) : /* @__PURE__ */ jsx(
104
- Button,
105
- {
106
- type: "button",
107
- onClick: openOverlay,
108
- disabled,
109
- variant: "outline",
110
- size,
111
- "aria-label": "Start dictation",
112
- className: cn("transition-colors", visibility === "hidden" && "hidden", !shrink && "shrink-0", className),
113
- children: /* @__PURE__ */ jsxs("span", { className: "flex items-center justify-center gap-2", children: [
114
- /* @__PURE__ */ jsx(Mic, { className: "h-4 w-4" }),
115
- showLabel && /* @__PURE__ */ jsx("span", { children: "Speak" })
116
- ] })
117
- }
118
- ),
119
- /* @__PURE__ */ jsx(
120
- DialogPrimitive.Root,
121
- {
122
- open,
123
- onOpenChange: (next) => {
124
- if (!next) handleCancel();
125
- },
126
- children: /* @__PURE__ */ jsx(DialogPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
127
- DialogPrimitive.Content,
128
- {
129
- "aria-label": "Voice dictation",
130
- "aria-labelledby": void 0,
131
- "data-slot": "voice-dictation-panel",
132
- "data-state": capture.state,
133
- onCloseAutoFocus: (event) => {
134
- event.preventDefault();
135
- if (previousFocus.current?.isConnected) previousFocus.current.focus();
136
- },
137
- onKeyDown: (event) => {
138
- if (event.key === "Enter" && !event.shiftKey && !(event.target instanceof HTMLElement && event.target.closest("button, input, textarea, select"))) {
139
- event.preventDefault();
140
- void handleDone();
141
- }
142
- },
143
- className: "fixed bottom-4 left-1/2 z-[200] flex max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-sm -translate-x-1/2 flex-col gap-3 overflow-y-auto rounded-2xl border border-border bg-background p-4 text-foreground shadow-lg outline-hidden sm:bottom-6",
144
- children: [
145
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start justify-between gap-3", children: [
146
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
147
- /* @__PURE__ */ jsx(DialogPrimitive.Title, { className: "text-sm font-medium", children: title || "Voice dictation" }),
148
- /* @__PURE__ */ jsxs("span", { role: "status", className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
149
- busy ? /* @__PURE__ */ jsx(LoaderCircle, { "aria-hidden": "true", className: "size-3.5 animate-spin motion-reduce:animate-none" }) : /* @__PURE__ */ jsx(Mic, { "aria-hidden": "true", className: "size-3.5" }),
150
- capture.state === "transcribing" ? "Transcribing\u2026" : capture.state === "requesting" ? "Waiting for microphone\u2026" : error ? "Recording not saved" : capture.state === "ready" ? "Ready" : caption
151
- ] })
152
- ] }),
153
- /* @__PURE__ */ jsxs("time", { "aria-label": "Recording duration", dateTime: `PT${elapsed}S`, className: "pt-0.5 font-mono text-xs tabular-nums text-muted-foreground", children: [
154
- Math.floor(elapsed / 60).toString().padStart(2, "0"),
155
- ":",
156
- (elapsed % 60).toString().padStart(2, "0")
157
- ] })
158
- ] }),
159
- capture.audioLevels && /* @__PURE__ */ jsx(DictationWaveform, { source: capture.audioLevels, state: capture.state }),
160
- /* @__PURE__ */ jsx(DialogPrimitive.Description, { className: "text-xs text-muted-foreground", children: capture.state === "transcribing" ? "Converting your recording to text\u2026" : capture.state === "requesting" ? "Allow microphone access to start." : error ? capture.canRetry ? "Retry this take or record again." : "Check microphone access, then try again." : "Speak, then choose Done to add the text." }),
161
- displayTranscript && /* @__PURE__ */ jsxs("p", { className: "max-h-24 overflow-y-auto text-sm leading-relaxed", children: [
162
- transcript,
163
- interim && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
164
- transcript ? " " : "",
165
- interim
166
- ] })
167
- ] }),
168
- error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-xs text-destructive", children: error }),
169
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [
170
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "min-h-[44px]", onClick: handleCancel, children: "Cancel" }),
171
- capture.canRetry && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", className: "min-h-[44px]", onClick: () => void capture.start(), children: "Record again" }),
172
- /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", className: "min-h-[44px]", onClick: () => void handleDone(), disabled: busy, children: capture.state === "transcribing" ? "Transcribing\u2026" : capture.canRetry ? "Retry transcription" : error ? "Try again" : "Done" })
173
- ] })
174
- ]
175
- }
176
- ) })
177
- }
178
- )
179
- ] });
180
- }
181
- var SILENCE = [];
182
- var silence = () => SILENCE;
183
- function DictationWaveform({ source, state }) {
184
- const bands = useSyncExternalStore(source.subscribe, source.getSnapshot, silence);
185
- return /* @__PURE__ */ jsx(
186
- AgentAudioVisualizerBar,
187
- {
188
- "aria-hidden": "true",
189
- "data-slot": "dictation-waveform",
190
- size: "icon",
191
- barCount: 24,
192
- state: state === "recording" ? "listening" : state === "transcribing" ? "thinking" : state === "requesting" ? "connecting" : "disconnected",
193
- volumeBands: state === "recording" ? bands : void 0,
194
- className: "h-9 w-full text-foreground"
195
- }
196
- );
197
- }
198
- function useRecordingTime(state) {
199
- const [elapsed, setElapsed] = useState(0);
200
- const startedAt = useRef(null);
201
- useEffect(() => {
202
- if (state !== "recording") {
203
- if (state === "idle" || state === "requesting") setElapsed(0);
204
- else if (startedAt.current !== null) setElapsed(Math.floor((performance.now() - startedAt.current) / 1e3));
205
- startedAt.current = null;
206
- return;
207
- }
208
- startedAt.current = performance.now();
209
- const update = () => setElapsed(Math.floor((performance.now() - startedAt.current) / 1e3));
210
- update();
211
- const timer = setInterval(update, 1e3);
212
- return () => {
213
- clearInterval(timer);
214
- };
215
- }, [state]);
216
- return elapsed;
217
- }
218
-
219
- export {
220
- VoiceDictationButton
221
- };