@epicbagel/saas-kit 1.7.0 → 1.11.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 +72 -1
- package/THIRD_PARTY_NOTICES.md +14 -0
- package/dist/chunk-2BSFYU22.cjs +71 -0
- package/dist/chunk-5OF6D3GH.js +158 -0
- package/dist/chunk-7QTFYEPE.cjs +153 -0
- package/dist/{chunk-GTVSRRE3.cjs → chunk-BGCNGYPL.cjs} +2 -2
- package/dist/{chunk-QTWJQQDH.cjs → chunk-FBVZRRA4.cjs} +6 -6
- package/dist/chunk-GJ7S3L6R.js +71 -0
- package/dist/chunk-HLAHOVEI.js +412 -0
- package/dist/{chunk-F3B4M6DU.js → chunk-HTPPNW74.js} +1 -1
- package/dist/chunk-KQBXAQAY.cjs +221 -0
- package/dist/{chunk-JN6JOSZG.js → chunk-LQPJGZIE.js} +7 -7
- package/dist/chunk-NKWHDI4C.cjs +158 -0
- package/dist/chunk-T3XMHWYV.js +221 -0
- package/dist/chunk-UOVA4YKW.js +153 -0
- package/dist/chunk-ZZ7ICQR2.cjs +412 -0
- package/dist/hooks/use-agent-audio-visualizer-bar.cjs +8 -0
- package/dist/hooks/use-agent-audio-visualizer-bar.d.cts +4 -0
- package/dist/hooks/use-agent-audio-visualizer-bar.d.ts +4 -0
- package/dist/hooks/use-agent-audio-visualizer-bar.js +8 -0
- package/dist/hooks/use-dictation.cjs +10 -0
- package/dist/hooks/use-dictation.d.cts +61 -0
- package/dist/hooks/use-dictation.d.ts +61 -0
- package/dist/hooks/use-dictation.js +10 -0
- package/dist/primitives/activity-feed.cjs +3 -3
- package/dist/primitives/activity-feed.js +4 -4
- package/dist/primitives/agent-audio-visualizer-bar.cjs +16 -0
- package/dist/primitives/agent-audio-visualizer-bar.d.cts +75 -0
- package/dist/primitives/agent-audio-visualizer-bar.d.ts +75 -0
- package/dist/primitives/agent-audio-visualizer-bar.js +16 -0
- package/dist/primitives/alert.d.cts +1 -1
- package/dist/primitives/alert.d.ts +1 -1
- package/dist/primitives/archive-confirmation-dialog.cjs +2 -2
- package/dist/primitives/archive-confirmation-dialog.js +3 -3
- package/dist/primitives/attachment.d.cts +1 -1
- package/dist/primitives/attachment.d.ts +1 -1
- package/dist/primitives/badge.d.cts +1 -1
- package/dist/primitives/badge.d.ts +1 -1
- package/dist/primitives/board-view.cjs +6 -6
- package/dist/primitives/board-view.js +5 -5
- package/dist/primitives/bubble.d.cts +1 -1
- package/dist/primitives/bubble.d.ts +1 -1
- package/dist/primitives/button.d.cts +2 -2
- package/dist/primitives/button.d.ts +2 -2
- package/dist/primitives/chat.cjs +59 -28
- package/dist/primitives/chat.d.cts +8 -3
- package/dist/primitives/chat.d.ts +8 -3
- package/dist/primitives/chat.js +56 -25
- package/dist/primitives/code-block.cjs +4 -4
- package/dist/primitives/code-block.js +3 -3
- package/dist/primitives/connection.cjs +6 -6
- package/dist/primitives/connection.js +7 -7
- package/dist/primitives/copy-button.cjs +3 -3
- package/dist/primitives/copy-button.js +2 -2
- package/dist/primitives/dictation-provider.cjs +8 -0
- package/dist/primitives/dictation-provider.d.cts +2 -0
- package/dist/primitives/dictation-provider.d.ts +2 -0
- package/dist/primitives/dictation-provider.js +8 -0
- package/dist/primitives/editable-list.cjs +1 -1
- package/dist/primitives/editable-list.js +1 -1
- package/dist/primitives/file-dropzone.cjs +3 -3
- package/dist/primitives/file-dropzone.js +2 -2
- package/dist/primitives/file-preview.cjs +6 -6
- package/dist/primitives/file-preview.js +5 -5
- package/dist/primitives/filter-bar.cjs +4 -4
- package/dist/primitives/filter-bar.js +3 -3
- package/dist/primitives/input-group.d.cts +1 -1
- package/dist/primitives/input-group.d.ts +1 -1
- package/dist/primitives/item.d.cts +2 -2
- package/dist/primitives/item.d.ts +2 -2
- package/dist/primitives/resource-list.cjs +6 -6
- package/dist/primitives/resource-list.js +5 -5
- package/dist/primitives/section-header.d.cts +1 -1
- package/dist/primitives/section-header.d.ts +1 -1
- package/dist/primitives/section.cjs +2 -2
- package/dist/primitives/section.js +3 -3
- package/dist/primitives/sheet.d.cts +1 -1
- package/dist/primitives/sheet.d.ts +1 -1
- package/dist/primitives/switch-field.cjs +2 -2
- package/dist/primitives/switch-field.js +3 -3
- package/dist/primitives/theme-selector.cjs +3 -3
- package/dist/primitives/theme-selector.js +4 -4
- package/dist/primitives/toggle-group.d.cts +2 -2
- package/dist/primitives/toggle-group.d.ts +2 -2
- package/dist/primitives/toggle.d.cts +2 -2
- package/dist/primitives/toggle.d.ts +2 -2
- package/dist/primitives/voice-dictation-button.cjs +5 -2
- package/dist/primitives/voice-dictation-button.d.cts +5 -9
- package/dist/primitives/voice-dictation-button.d.ts +5 -9
- package/dist/primitives/voice-dictation-button.js +4 -1
- package/dist/primitives/voice-session-controls.cjs +12 -0
- package/dist/primitives/voice-session-controls.d.cts +31 -0
- package/dist/primitives/voice-session-controls.d.ts +31 -0
- package/dist/primitives/voice-session-controls.js +12 -0
- package/package.json +51 -1
- package/dist/chunk-LKPM6DQP.cjs +0 -244
- package/dist/chunk-TI57GAQP.js +0 -244
- package/dist/{chunk-HREEZUKL.js → chunk-DILYV4FI.js} +3 -3
- package/dist/{chunk-MU4F3DSW.cjs → chunk-FARCLWLU.cjs} +2 -2
- package/dist/{chunk-CRTH5GJ5.cjs → chunk-MW67PHAQ.cjs} +3 -3
- package/dist/{chunk-KNKV25WS.js → chunk-UFFHGKI7.js} +3 -3
- package/dist/{chunk-HBRVOGLA.js → chunk-WHDHUYS6.js} +3 -3
- package/dist/{chunk-Z3JJSNR5.cjs → chunk-ZLEM2SAU.cjs} +2 -2
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ dark modes.
|
|
|
7
7
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
10
|
+
|
|
10
11
|
```bash
|
|
11
12
|
npm install @epicbagel/saas-kit
|
|
12
13
|
```
|
|
@@ -14,6 +15,75 @@ npm install @epicbagel/saas-kit
|
|
|
14
15
|
Peer dependencies: `react` and `react-dom` (18 or 19), `lucide-react`
|
|
15
16
|
(`>=0.400.0 <2`), `react-icons` (5) and `tailwindcss` (4.3 or later).
|
|
16
17
|
|
|
18
|
+
## Live voice in Chat
|
|
19
|
+
|
|
20
|
+
Pass a controlled `voiceSession` to `Chat` or `ChatInput` to show start, mute,
|
|
21
|
+
end, connection and speaking controls alongside a separate dictation mic.
|
|
22
|
+
Dictation is disabled during a live call and the typed draft is preserved.
|
|
23
|
+
The active session appears as a compact horizontal strip above the text field:
|
|
24
|
+
microphone badge, audio bars, status, latest utterance, **Mute** and **End voice**.
|
|
25
|
+
It wraps its controls on narrow screens. Pass the latest user utterance as
|
|
26
|
+
`voiceSession.transcript`; this is plain preview text and never submits or replaces
|
|
27
|
+
the typed draft. `VoiceSessionControls` is also exported on its own.
|
|
28
|
+
The host owns audio, authentication, transcripts, retries and cleanup; the kit
|
|
29
|
+
does not import a voice provider. Stop and release audio tracks before setting
|
|
30
|
+
`state: "error"` or unmounting. Every label is replaceable through `labels`.
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<Chat messages={messages} onSend={sendMessage} voiceSession={{
|
|
34
|
+
state: voiceState, muted, error, transcript: latestUserUtterance, audioLevels,
|
|
35
|
+
onStart: startVoice, onEnd: endVoice, onMutedChange: setMuted,
|
|
36
|
+
}} />
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## API dictation
|
|
40
|
+
|
|
41
|
+
Wrap the application in `DictationProvider` to use an authenticated transcription
|
|
42
|
+
API for every `VoiceDictationButton`, including buttons nested in chat, forms or
|
|
43
|
+
another component library. A compact recorder shows the elapsed time and LiveKit
|
|
44
|
+
audio bars measured from the recording stream, without opening a second microphone.
|
|
45
|
+
It also works with hidden/programmatic triggers and inside another dialog.
|
|
46
|
+
The recorder runs until **Done**, shows
|
|
47
|
+
**Transcribing…**, then commits the returned text once. Failed requests keep the
|
|
48
|
+
take available for retry. Cancel stops microphone tracks, aborts the request and
|
|
49
|
+
ignores a late answer. The kit never persists audio or knows provider credentials.
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { DictationProvider } from "@epicbagel/saas-kit/primitives/dictation-provider";
|
|
53
|
+
|
|
54
|
+
<DictationProvider scopeKey={`${actorId}:${workspaceId}`} transcribe={async (audio, { language, signal }) => {
|
|
55
|
+
// The host sends the Blob to its authenticated API. Preserve audio.type
|
|
56
|
+
// and pass signal to fetch; keep provider keys on the server.
|
|
57
|
+
return transcribeRecording(audio, { language, signal });
|
|
58
|
+
}}>
|
|
59
|
+
<App />
|
|
60
|
+
</DictationProvider>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Set `scopeKey` from the acting user and workspace (including impersonation, if
|
|
64
|
+
the app supports it). A changed scope discards the active take, stops recording
|
|
65
|
+
and aborts transcription without remounting the application. Responses from the
|
|
66
|
+
previous scope are ignored even before the cleanup effect runs; a failed take
|
|
67
|
+
cannot be retried under the new scope.
|
|
68
|
+
|
|
69
|
+
`useDictation` (`hooks/use-dictation`) exposes the same capture lifecycle for a
|
|
70
|
+
custom trigger such as press-and-hold: `start()`, `stop()`, `retry()`, `cancel()`,
|
|
71
|
+
`state`, `supported`, `transcript`, `interimTranscript`, `error` and `canRetry`.
|
|
72
|
+
For API recording, `audioLevels` exposes a subscribable snapshot of real microphone
|
|
73
|
+
levels. Subscribe only the waveform to keep audio sampling out of form renders;
|
|
74
|
+
native recognition has no audio-level source.
|
|
75
|
+
`stop()` and `retry()` return the completed text, or `null` after cancellation or
|
|
76
|
+
failure. Choose either that result or `onTranscript` to commit; using both would
|
|
77
|
+
duplicate the host action. The callback receives one complete API transcript,
|
|
78
|
+
or finalized chunks with native recognition. API recording requires HTTPS (or
|
|
79
|
+
localhost), microphone permission and MediaRecorder. It does not produce live
|
|
80
|
+
interim text; the live voice controls are a separate feature.
|
|
81
|
+
|
|
82
|
+
Without a provider, controls retain browser-native speech recognition. The
|
|
83
|
+
private ui-kit's `VoiceInput` uses this same provider and capture hook, and its
|
|
84
|
+
provider export is a shim of this exact context.
|
|
85
|
+
|
|
86
|
+
|
|
17
87
|
## Styles
|
|
18
88
|
|
|
19
89
|
If the app has no Tailwind entry of its own, import the complete stylesheet:
|
|
@@ -104,7 +174,8 @@ public in 1.1.0, and these were the decisions about its props then:
|
|
|
104
174
|
for the textarea's starting height; there is no class-name or `rows` prop.
|
|
105
175
|
The textarea grows with its text (`field-sizing: content`).
|
|
106
176
|
- **Compact mobile replies.** Opt into `Chat.mobileCompactComposer` to fold
|
|
107
|
-
an active conversation's empty composer into
|
|
177
|
+
an active conversation's empty composer into a short text row above its
|
|
178
|
+
controls below 768px, keeping all four actions clear of the text. Focusing
|
|
108
179
|
its textarea expands it; leaving the composer folds it again only when
|
|
109
180
|
there is no draft or staged file. The same textarea stays mounted, and
|
|
110
181
|
its file menu and voice dialog keep an editing session open. New
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -268,3 +268,17 @@ The Apache License, Version 2.0:
|
|
|
268
268
|
|
|
269
269
|
END OF TERMS AND CONDITIONS
|
|
270
270
|
```
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
## LiveKit Agents UI
|
|
274
|
+
|
|
275
|
+
Source: https://github.com/livekit/components-js/tree/3f74806f1c8484b2eb2fd40773dd1be80760750c/packages/shadcn
|
|
276
|
+
|
|
277
|
+
`primitives/agent-audio-visualizer-bar` and
|
|
278
|
+
`hooks/use-agent-audio-visualizer-bar` are adapted from LiveKit's
|
|
279
|
+
`components/agents-ui/agent-audio-visualizer-bar.tsx` and
|
|
280
|
+
`hooks/agents-ui/use-agent-audio-visualizer-bar.ts` at that commit.
|
|
281
|
+
LiveKit contributors license them under Apache-2.0 (full license above).
|
|
282
|
+
The compiled output includes these adaptations. We removed the LiveKit React
|
|
283
|
+
track hook and accept host-supplied volume bands, clamp band values/counts,
|
|
284
|
+
and respect reduced motion and stop idle animation loops.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } 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
|
+
// src/hooks/use-agent-audio-visualizer-bar.ts
|
|
4
|
+
var _react = require('react');
|
|
5
|
+
function generateConnectingSequenceBar(columns) {
|
|
6
|
+
const seq = [];
|
|
7
|
+
for (let x = 0; x < columns; x++) {
|
|
8
|
+
seq.push([x, columns - 1 - x]);
|
|
9
|
+
}
|
|
10
|
+
return seq;
|
|
11
|
+
}
|
|
12
|
+
function generateListeningSequenceBar(columns) {
|
|
13
|
+
const center = Math.floor(columns / 2);
|
|
14
|
+
const noIndex = -1;
|
|
15
|
+
return [[center], [noIndex]];
|
|
16
|
+
}
|
|
17
|
+
function useAgentAudioVisualizerBarAnimator(state, columns, interval) {
|
|
18
|
+
const [index, setIndex] = _react.useState.call(void 0, 0);
|
|
19
|
+
const [sequence, setSequence] = _react.useState.call(void 0, [[]]);
|
|
20
|
+
_react.useEffect.call(void 0, () => {
|
|
21
|
+
if (state === "thinking") {
|
|
22
|
+
setSequence(generateListeningSequenceBar(columns));
|
|
23
|
+
} else if (state === "connecting" || state === "initializing") {
|
|
24
|
+
const sequence2 = [...generateConnectingSequenceBar(columns)];
|
|
25
|
+
setSequence(sequence2);
|
|
26
|
+
} else if (state === "listening") {
|
|
27
|
+
setSequence(generateListeningSequenceBar(columns));
|
|
28
|
+
} else if (state === void 0 || state === "speaking") {
|
|
29
|
+
setSequence([new Array(columns).fill(0).map((_, idx) => idx)]);
|
|
30
|
+
} else {
|
|
31
|
+
setSequence([[]]);
|
|
32
|
+
}
|
|
33
|
+
setIndex(0);
|
|
34
|
+
}, [state, columns]);
|
|
35
|
+
const animationFrameId = _react.useRef.call(void 0, null);
|
|
36
|
+
_react.useEffect.call(void 0, () => {
|
|
37
|
+
if (sequence.length < 2) return;
|
|
38
|
+
const preference = _optionalChain([window, 'access', _2 => _2.matchMedia, 'optionalCall', _3 => _3("(prefers-reduced-motion: reduce)")]);
|
|
39
|
+
const stop = () => {
|
|
40
|
+
if (animationFrameId.current !== null) cancelAnimationFrame(animationFrameId.current);
|
|
41
|
+
animationFrameId.current = null;
|
|
42
|
+
};
|
|
43
|
+
const start = () => {
|
|
44
|
+
stop();
|
|
45
|
+
if (_optionalChain([preference, 'optionalAccess', _4 => _4.matches])) {
|
|
46
|
+
setIndex(0);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
let startTime = performance.now();
|
|
50
|
+
const animate = (time) => {
|
|
51
|
+
if (time - startTime >= interval) {
|
|
52
|
+
setIndex((prev) => prev + 1);
|
|
53
|
+
startTime = time;
|
|
54
|
+
}
|
|
55
|
+
animationFrameId.current = requestAnimationFrame(animate);
|
|
56
|
+
};
|
|
57
|
+
animationFrameId.current = requestAnimationFrame(animate);
|
|
58
|
+
};
|
|
59
|
+
start();
|
|
60
|
+
_optionalChain([preference, 'optionalAccess', _5 => _5.addEventListener, 'call', _6 => _6("change", start)]);
|
|
61
|
+
return () => {
|
|
62
|
+
stop();
|
|
63
|
+
_optionalChain([preference, 'optionalAccess', _7 => _7.removeEventListener, 'call', _8 => _8("change", start)]);
|
|
64
|
+
};
|
|
65
|
+
}, [interval, columns, state, sequence.length]);
|
|
66
|
+
return _nullishCoalesce(sequence[index % sequence.length], () => ( []));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
exports.useAgentAudioVisualizerBarAnimator = useAgentAudioVisualizerBarAnimator;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
cn
|
|
4
|
+
} from "./chunk-G6SRSWXB.js";
|
|
5
|
+
import {
|
|
6
|
+
useAgentAudioVisualizerBarAnimator
|
|
7
|
+
} from "./chunk-GJ7S3L6R.js";
|
|
8
|
+
|
|
9
|
+
// src/primitives/agent-audio-visualizer-bar.tsx
|
|
10
|
+
import React, {
|
|
11
|
+
Children,
|
|
12
|
+
cloneElement,
|
|
13
|
+
isValidElement,
|
|
14
|
+
useMemo
|
|
15
|
+
} from "react";
|
|
16
|
+
import { cva } from "class-variance-authority";
|
|
17
|
+
import { jsx } from "react/jsx-runtime";
|
|
18
|
+
function normalizeVolumeBands(bands, count) {
|
|
19
|
+
if (bands.length === count) return bands;
|
|
20
|
+
if (bands.length > count) return bands.slice(0, count);
|
|
21
|
+
const lastValue = bands[bands.length - 1] ?? 0;
|
|
22
|
+
return [...bands, ...new Array(count - bands.length).fill(lastValue)];
|
|
23
|
+
}
|
|
24
|
+
function cloneSingleChild(children, props, key) {
|
|
25
|
+
return Children.map(children, (child) => {
|
|
26
|
+
if (isValidElement(child) && Children.only(children)) {
|
|
27
|
+
const childProps = child.props;
|
|
28
|
+
if (childProps.className) {
|
|
29
|
+
props ??= {};
|
|
30
|
+
props.className = cn(childProps.className, props.className);
|
|
31
|
+
props.style = {
|
|
32
|
+
...childProps.style,
|
|
33
|
+
...props.style
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return cloneElement(child, { ...props, key: key ? String(key) : void 0 });
|
|
37
|
+
}
|
|
38
|
+
return child;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
var AgentAudioVisualizerBarElementVariants = cva(
|
|
42
|
+
[
|
|
43
|
+
"rounded-full transition-colors duration-250 ease-linear motion-reduce:transition-none",
|
|
44
|
+
"bg-current/10 data-[lk-highlighted=true]:bg-current"
|
|
45
|
+
],
|
|
46
|
+
{
|
|
47
|
+
variants: {
|
|
48
|
+
size: {
|
|
49
|
+
icon: "w-[4px] min-h-[4px]",
|
|
50
|
+
sm: "w-[8px] min-h-[8px]",
|
|
51
|
+
md: "w-[16px] min-h-[16px]",
|
|
52
|
+
lg: "w-[32px] min-h-[32px]",
|
|
53
|
+
xl: "w-[64px] min-h-[64px]"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
defaultVariants: {
|
|
57
|
+
size: "md"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
var AgentAudioVisualizerBarVariants = cva("relative flex items-center justify-center", {
|
|
62
|
+
variants: {
|
|
63
|
+
size: {
|
|
64
|
+
icon: "h-[24px] gap-[2px]",
|
|
65
|
+
sm: "h-[56px] gap-[4px]",
|
|
66
|
+
md: "h-[112px] gap-[8px]",
|
|
67
|
+
lg: "h-[224px] gap-[16px]",
|
|
68
|
+
xl: "h-[448px] gap-[32px]"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
defaultVariants: {
|
|
72
|
+
size: "md"
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
function AgentAudioVisualizerBar({
|
|
76
|
+
size = "md",
|
|
77
|
+
state = "connecting",
|
|
78
|
+
color,
|
|
79
|
+
barCount,
|
|
80
|
+
volumeBands,
|
|
81
|
+
className,
|
|
82
|
+
children,
|
|
83
|
+
style,
|
|
84
|
+
...props
|
|
85
|
+
}) {
|
|
86
|
+
const _barCount = useMemo(() => {
|
|
87
|
+
if (barCount && Number.isFinite(barCount)) {
|
|
88
|
+
return Math.max(1, Math.min(64, Math.floor(barCount)));
|
|
89
|
+
}
|
|
90
|
+
switch (size) {
|
|
91
|
+
case "icon":
|
|
92
|
+
case "sm":
|
|
93
|
+
return 3;
|
|
94
|
+
default:
|
|
95
|
+
return 5;
|
|
96
|
+
}
|
|
97
|
+
}, [barCount, size]);
|
|
98
|
+
const resolvedVolumeBands = normalizeVolumeBands(volumeBands ?? [], _barCount).map((value) => Number.isFinite(value) ? Math.max(0, Math.min(1, value)) : 0);
|
|
99
|
+
const sequencerInterval = useMemo(() => {
|
|
100
|
+
switch (state) {
|
|
101
|
+
case "connecting":
|
|
102
|
+
return 2e3 / _barCount;
|
|
103
|
+
case "initializing":
|
|
104
|
+
return 2e3;
|
|
105
|
+
case "listening":
|
|
106
|
+
return 500;
|
|
107
|
+
case "thinking":
|
|
108
|
+
return 150;
|
|
109
|
+
default:
|
|
110
|
+
return 1e3;
|
|
111
|
+
}
|
|
112
|
+
}, [state, _barCount]);
|
|
113
|
+
const meteredListening = state === "listening" && volumeBands !== void 0;
|
|
114
|
+
const highlightedIndices = useAgentAudioVisualizerBarAnimator(
|
|
115
|
+
meteredListening ? "speaking" : state,
|
|
116
|
+
_barCount,
|
|
117
|
+
sequencerInterval
|
|
118
|
+
);
|
|
119
|
+
const bands = useMemo(
|
|
120
|
+
() => state === "speaking" || meteredListening ? resolvedVolumeBands : new Array(_barCount).fill(0),
|
|
121
|
+
[state, meteredListening, resolvedVolumeBands, _barCount]
|
|
122
|
+
);
|
|
123
|
+
if (children && Array.isArray(children)) {
|
|
124
|
+
throw new Error("AgentAudioVisualizerBar children must be a single element.");
|
|
125
|
+
}
|
|
126
|
+
return /* @__PURE__ */ jsx(
|
|
127
|
+
"div",
|
|
128
|
+
{
|
|
129
|
+
"data-lk-state": state,
|
|
130
|
+
style: { ...style, color },
|
|
131
|
+
className: cn(AgentAudioVisualizerBarVariants({ size }), className),
|
|
132
|
+
...props,
|
|
133
|
+
children: bands.map(
|
|
134
|
+
(band, idx) => children ? /* @__PURE__ */ jsx(React.Fragment, { children: cloneSingleChild(children, {
|
|
135
|
+
"data-lk-index": idx,
|
|
136
|
+
"data-lk-highlighted": highlightedIndices.includes(idx),
|
|
137
|
+
style: { height: `${band * 100}%` }
|
|
138
|
+
}) }, idx) : /* @__PURE__ */ jsx(
|
|
139
|
+
"div",
|
|
140
|
+
{
|
|
141
|
+
"data-lk-index": idx,
|
|
142
|
+
"data-lk-highlighted": highlightedIndices.includes(idx),
|
|
143
|
+
style: { height: `${band * 100}%` },
|
|
144
|
+
className: cn(AgentAudioVisualizerBarElementVariants({ size }))
|
|
145
|
+
},
|
|
146
|
+
idx
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export {
|
|
154
|
+
normalizeVolumeBands,
|
|
155
|
+
AgentAudioVisualizerBarElementVariants,
|
|
156
|
+
AgentAudioVisualizerBarVariants,
|
|
157
|
+
AgentAudioVisualizerBar
|
|
158
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } 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
|
+
// src/primitives/voice-session-controls.tsx
|
|
13
|
+
var _react = require('react');
|
|
14
|
+
var _lucidereact = require('lucide-react');
|
|
15
|
+
var _jsxruntime = require('react/jsx-runtime');
|
|
16
|
+
var SILENCE = [];
|
|
17
|
+
var silence = () => SILENCE;
|
|
18
|
+
var noSubscription = () => () => {
|
|
19
|
+
};
|
|
20
|
+
var DEFAULT_LABELS = {
|
|
21
|
+
disconnected: "Voice chat",
|
|
22
|
+
connecting: "Connecting\u2026",
|
|
23
|
+
listening: "Listening\u2026",
|
|
24
|
+
thinking: "Thinking\u2026",
|
|
25
|
+
speaking: "Speaking",
|
|
26
|
+
reconnecting: "Reconnecting\u2026",
|
|
27
|
+
error: "Voice chat unavailable",
|
|
28
|
+
start: "Start voice chat",
|
|
29
|
+
end: "End voice chat",
|
|
30
|
+
mute: "Mute microphone",
|
|
31
|
+
unmute: "Unmute microphone",
|
|
32
|
+
muted: "Microphone muted",
|
|
33
|
+
retry: "Retry voice chat",
|
|
34
|
+
endShort: "End voice",
|
|
35
|
+
muteShort: "Mute",
|
|
36
|
+
unmuteShort: "Unmute",
|
|
37
|
+
transcriptPlaceholder: "Speak to continue the conversation."
|
|
38
|
+
};
|
|
39
|
+
function VoiceSessionControls({
|
|
40
|
+
state,
|
|
41
|
+
onStart,
|
|
42
|
+
onEnd,
|
|
43
|
+
muted = false,
|
|
44
|
+
onMutedChange,
|
|
45
|
+
disabled = false,
|
|
46
|
+
error,
|
|
47
|
+
transcript,
|
|
48
|
+
volumeBands,
|
|
49
|
+
audioLevels,
|
|
50
|
+
labels,
|
|
51
|
+
size = "icon",
|
|
52
|
+
className
|
|
53
|
+
}) {
|
|
54
|
+
const liveBands = _react.useSyncExternalStore.call(void 0, _nullishCoalesce(_optionalChain([audioLevels, 'optionalAccess', _ => _.subscribe]), () => ( noSubscription)), _nullishCoalesce(_optionalChain([audioLevels, 'optionalAccess', _2 => _2.getSnapshot]), () => ( silence)), silence);
|
|
55
|
+
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
56
|
+
const active = state !== "disconnected" && state !== "error";
|
|
57
|
+
const waiting = state === "connecting" || state === "reconnecting";
|
|
58
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
59
|
+
"div",
|
|
60
|
+
{
|
|
61
|
+
"data-slot": "voice-session-controls",
|
|
62
|
+
"data-state": state,
|
|
63
|
+
className: _chunkKWLSBTB2cjs.cn.call(void 0, "min-w-0", active ? "@container/voice w-full" : "flex flex-wrap items-center gap-2", className),
|
|
64
|
+
children: [
|
|
65
|
+
active ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { "data-slot": "voice-session-strip", className: "flex w-full flex-wrap items-center gap-4 bg-primary/5 p-4 @2xl/voice:flex-nowrap @2xl/voice:px-5", children: [
|
|
66
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex min-w-0 flex-1 basis-full items-center gap-3 @2xl/voice:basis-auto @2xl/voice:gap-4", children: [
|
|
67
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { "aria-hidden": "true", className: _chunkKWLSBTB2cjs.cn.call(void 0,
|
|
68
|
+
"flex size-12 shrink-0 items-center justify-center rounded-full ring-8 ring-primary/5 @sm/voice:size-14",
|
|
69
|
+
muted ? "bg-muted text-muted-foreground" : "bg-primary text-primary-foreground"
|
|
70
|
+
), children: muted ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.MicOff, { className: "size-5" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Mic, { className: "size-5" }) }),
|
|
71
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
72
|
+
_chunkNKWHDI4Ccjs.AgentAudioVisualizerBar,
|
|
73
|
+
{
|
|
74
|
+
"aria-hidden": "true",
|
|
75
|
+
size: "sm",
|
|
76
|
+
barCount: 5,
|
|
77
|
+
className: "shrink-0 text-primary [&>div]:w-1",
|
|
78
|
+
state: muted && state === "listening" ? "disconnected" : state === "reconnecting" ? "connecting" : state,
|
|
79
|
+
volumeBands: muted && state === "listening" ? SILENCE : _nullishCoalesce(volumeBands, () => ( liveBands))
|
|
80
|
+
}
|
|
81
|
+
),
|
|
82
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "min-w-0 flex-1 space-y-1", children: [
|
|
83
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { role: "status", className: "block text-sm font-medium text-primary", children: muted && state === "listening" ? copy.muted : copy[state] }),
|
|
84
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
85
|
+
"p",
|
|
86
|
+
{
|
|
87
|
+
"data-slot": "voice-session-transcript",
|
|
88
|
+
title: transcript || void 0,
|
|
89
|
+
className: _chunkKWLSBTB2cjs.cn.call(void 0, "truncate text-sm font-normal", transcript ? "text-foreground" : "text-muted-foreground"),
|
|
90
|
+
children: transcript || copy.transcriptPlaceholder
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
] })
|
|
94
|
+
] }),
|
|
95
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [
|
|
96
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
97
|
+
_chunkJDEXZGIYcjs.Button,
|
|
98
|
+
{
|
|
99
|
+
type: "button",
|
|
100
|
+
variant: "outline",
|
|
101
|
+
size: "sm",
|
|
102
|
+
className: "min-h-[44px] shrink-0 rounded-xl bg-background px-3 text-foreground",
|
|
103
|
+
"aria-label": muted ? copy.unmute : copy.mute,
|
|
104
|
+
title: muted ? copy.unmute : copy.mute,
|
|
105
|
+
"aria-pressed": muted,
|
|
106
|
+
disabled: waiting,
|
|
107
|
+
onClick: () => onMutedChange(!muted),
|
|
108
|
+
children: [
|
|
109
|
+
muted ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Mic, { "aria-hidden": "true" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.MicOff, { "aria-hidden": "true" }),
|
|
110
|
+
muted ? copy.unmuteShort : copy.muteShort
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
),
|
|
114
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
115
|
+
_chunkJDEXZGIYcjs.Button,
|
|
116
|
+
{
|
|
117
|
+
type: "button",
|
|
118
|
+
variant: "outline",
|
|
119
|
+
size: "sm",
|
|
120
|
+
className: "min-h-[44px] shrink-0 rounded-xl bg-background px-3 text-foreground",
|
|
121
|
+
"aria-label": copy.end,
|
|
122
|
+
title: copy.end,
|
|
123
|
+
onClick: onEnd,
|
|
124
|
+
children: [
|
|
125
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Square, { "aria-hidden": "true", className: "fill-current" }),
|
|
126
|
+
copy.endShort
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
] })
|
|
131
|
+
] }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
132
|
+
_chunkJDEXZGIYcjs.Button,
|
|
133
|
+
{
|
|
134
|
+
type: "button",
|
|
135
|
+
size: "icon",
|
|
136
|
+
shape: "circle",
|
|
137
|
+
disabled,
|
|
138
|
+
onClick: onStart,
|
|
139
|
+
"aria-label": state === "error" ? copy.retry : copy.start,
|
|
140
|
+
title: state === "error" ? copy.retry : copy.start,
|
|
141
|
+
className: _chunkKWLSBTB2cjs.cn.call(void 0, "min-h-[44px] min-w-[44px] shrink-0 rounded-full", size === "hero" && "size-24 [&_svg]:size-7"),
|
|
142
|
+
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.AudioLines, { "aria-hidden": "true" })
|
|
143
|
+
}
|
|
144
|
+
),
|
|
145
|
+
state === "error" && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { role: "alert", className: "basis-full text-xs text-destructive", children: error || copy.error })
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
exports.VoiceSessionControls = VoiceSessionControls;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var _chunkFARCLWLUcjs = require('./chunk-FARCLWLU.cjs');
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
|
|
@@ -38,7 +38,7 @@ function CodeBlock({
|
|
|
38
38
|
}
|
|
39
39
|
),
|
|
40
40
|
copy ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
41
|
-
|
|
41
|
+
_chunkFARCLWLUcjs.CopyButton,
|
|
42
42
|
{
|
|
43
43
|
text: copyText,
|
|
44
44
|
label: copyLabel,
|
|
@@ -7,16 +7,16 @@ var _chunkEUFV25KLcjs = require('./chunk-EUFV25KL.cjs');
|
|
|
7
7
|
var _chunk7YCKII7Gcjs = require('./chunk-7YCKII7G.cjs');
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _chunkMW67PHAQcjs = require('./chunk-MW67PHAQ.cjs');
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
var _chunkLELBCBDTcjs = require('./chunk-LELBCBDT.cjs');
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
|
|
16
|
+
var _chunkU2NES7VRcjs = require('./chunk-U2NES7VR.cjs');
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
var
|
|
19
|
+
var _chunkLELBCBDTcjs = require('./chunk-LELBCBDT.cjs');
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
|
|
@@ -32,10 +32,10 @@ var _chunkPQUSJEXLcjs = require('./chunk-PQUSJEXL.cjs');
|
|
|
32
32
|
var _chunkJDEXZGIYcjs = require('./chunk-JDEXZGIY.cjs');
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
var
|
|
35
|
+
var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
var
|
|
38
|
+
var _chunkR35FT53Gcjs = require('./chunk-R35FT53G.cjs');
|
|
39
39
|
|
|
40
40
|
// src/primitives/resource-list.tsx
|
|
41
41
|
var _lucidereact = require('lucide-react');
|
|
@@ -268,7 +268,7 @@ function ResourceList({
|
|
|
268
268
|
const controlsVisible = !(hideControlsWhenEmpty && !loading && !error && _optionalChain([data, 'optionalAccess', _5 => _5.length]) === 0);
|
|
269
269
|
const filterOnQuickRow = filterBarVisible && !toolbarHasOtherControls && hasQuickFilters;
|
|
270
270
|
const filterBarNode = filterBarVisible ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
271
|
-
|
|
271
|
+
_chunkMW67PHAQcjs.FilterBar,
|
|
272
272
|
{
|
|
273
273
|
filters: filterSchema,
|
|
274
274
|
onFiltersChange,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/hooks/use-agent-audio-visualizer-bar.ts
|
|
4
|
+
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
function generateConnectingSequenceBar(columns) {
|
|
6
|
+
const seq = [];
|
|
7
|
+
for (let x = 0; x < columns; x++) {
|
|
8
|
+
seq.push([x, columns - 1 - x]);
|
|
9
|
+
}
|
|
10
|
+
return seq;
|
|
11
|
+
}
|
|
12
|
+
function generateListeningSequenceBar(columns) {
|
|
13
|
+
const center = Math.floor(columns / 2);
|
|
14
|
+
const noIndex = -1;
|
|
15
|
+
return [[center], [noIndex]];
|
|
16
|
+
}
|
|
17
|
+
function useAgentAudioVisualizerBarAnimator(state, columns, interval) {
|
|
18
|
+
const [index, setIndex] = useState(0);
|
|
19
|
+
const [sequence, setSequence] = useState([[]]);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (state === "thinking") {
|
|
22
|
+
setSequence(generateListeningSequenceBar(columns));
|
|
23
|
+
} else if (state === "connecting" || state === "initializing") {
|
|
24
|
+
const sequence2 = [...generateConnectingSequenceBar(columns)];
|
|
25
|
+
setSequence(sequence2);
|
|
26
|
+
} else if (state === "listening") {
|
|
27
|
+
setSequence(generateListeningSequenceBar(columns));
|
|
28
|
+
} else if (state === void 0 || state === "speaking") {
|
|
29
|
+
setSequence([new Array(columns).fill(0).map((_, idx) => idx)]);
|
|
30
|
+
} else {
|
|
31
|
+
setSequence([[]]);
|
|
32
|
+
}
|
|
33
|
+
setIndex(0);
|
|
34
|
+
}, [state, columns]);
|
|
35
|
+
const animationFrameId = useRef(null);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (sequence.length < 2) return;
|
|
38
|
+
const preference = window.matchMedia?.("(prefers-reduced-motion: reduce)");
|
|
39
|
+
const stop = () => {
|
|
40
|
+
if (animationFrameId.current !== null) cancelAnimationFrame(animationFrameId.current);
|
|
41
|
+
animationFrameId.current = null;
|
|
42
|
+
};
|
|
43
|
+
const start = () => {
|
|
44
|
+
stop();
|
|
45
|
+
if (preference?.matches) {
|
|
46
|
+
setIndex(0);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
let startTime = performance.now();
|
|
50
|
+
const animate = (time) => {
|
|
51
|
+
if (time - startTime >= interval) {
|
|
52
|
+
setIndex((prev) => prev + 1);
|
|
53
|
+
startTime = time;
|
|
54
|
+
}
|
|
55
|
+
animationFrameId.current = requestAnimationFrame(animate);
|
|
56
|
+
};
|
|
57
|
+
animationFrameId.current = requestAnimationFrame(animate);
|
|
58
|
+
};
|
|
59
|
+
start();
|
|
60
|
+
preference?.addEventListener("change", start);
|
|
61
|
+
return () => {
|
|
62
|
+
stop();
|
|
63
|
+
preference?.removeEventListener("change", start);
|
|
64
|
+
};
|
|
65
|
+
}, [interval, columns, state, sequence.length]);
|
|
66
|
+
return sequence[index % sequence.length] ?? [];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export {
|
|
70
|
+
useAgentAudioVisualizerBarAnimator
|
|
71
|
+
};
|