@meridial/react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +134 -0
- package/dist/chunk-QCWLFL7O.js +4294 -0
- package/dist/chunk-QCWLFL7O.js.map +1 -0
- package/dist/chunk-VDQAVB4N.js +1754 -0
- package/dist/chunk-VDQAVB4N.js.map +1 -0
- package/dist/chunk-WCRZUGN4.js +1933 -0
- package/dist/chunk-WCRZUGN4.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/recorder.d.ts +11 -0
- package/dist/recorder.js +9 -0
- package/dist/recorder.js.map +1 -0
- package/dist/styles.css +6012 -0
- package/dist/voicebox-DCgECemo.d.ts +27 -0
- package/dist/voicebox.d.ts +3 -0
- package/dist/voicebox.js +9 -0
- package/dist/voicebox.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,1933 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
DragHandle,
|
|
4
|
+
EMPTY_ARRAY,
|
|
5
|
+
EMPTY_OBJECT,
|
|
6
|
+
STORAGE_KEYS,
|
|
7
|
+
SafeReact,
|
|
8
|
+
Separator,
|
|
9
|
+
__export,
|
|
10
|
+
apiWorkflowsResponseSchema,
|
|
11
|
+
cn,
|
|
12
|
+
cva,
|
|
13
|
+
error,
|
|
14
|
+
formatErrorMessage,
|
|
15
|
+
useButton,
|
|
16
|
+
useCompositeRootContext,
|
|
17
|
+
useElementTracker,
|
|
18
|
+
useIsoLayoutEffect,
|
|
19
|
+
useMergedRefs,
|
|
20
|
+
useRenderElement,
|
|
21
|
+
useStableCallback
|
|
22
|
+
} from "./chunk-QCWLFL7O.js";
|
|
23
|
+
|
|
24
|
+
// src/voicebox.tsx
|
|
25
|
+
import {
|
|
26
|
+
useSession,
|
|
27
|
+
useAgent,
|
|
28
|
+
useSessionContext as useSessionContext3,
|
|
29
|
+
useEnsureRoom as useEnsureRoom3
|
|
30
|
+
} from "@livekit/components-react";
|
|
31
|
+
import { AnimatePresence as AnimatePresence3, motion as motion3 } from "motion/react";
|
|
32
|
+
import { TokenSource, RpcError as RpcError2 } from "livekit-client";
|
|
33
|
+
import { useDraggable } from "@neodrag/react";
|
|
34
|
+
import {
|
|
35
|
+
useRef as useRef6,
|
|
36
|
+
useState as useState9,
|
|
37
|
+
useMemo as useMemo5,
|
|
38
|
+
useEffect as useEffect5,
|
|
39
|
+
useCallback as useCallback5
|
|
40
|
+
} from "react";
|
|
41
|
+
import { ChatFeedback01Icon, Loading03Icon as Loading03Icon3 } from "@hugeicons/core-free-icons";
|
|
42
|
+
import { HugeiconsIcon as HugeiconsIcon7 } from "@hugeicons/react";
|
|
43
|
+
|
|
44
|
+
// ../../node_modules/.pnpm/@base-ui+react@1.2.0_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/esm/toggle/Toggle.js
|
|
45
|
+
import * as React7 from "react";
|
|
46
|
+
|
|
47
|
+
// ../../node_modules/.pnpm/@base-ui+utils@0.2.5_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/utils/esm/useControlled.js
|
|
48
|
+
import * as React from "react";
|
|
49
|
+
function useControlled({
|
|
50
|
+
controlled,
|
|
51
|
+
default: defaultProp,
|
|
52
|
+
name,
|
|
53
|
+
state = "value"
|
|
54
|
+
}) {
|
|
55
|
+
const {
|
|
56
|
+
current: isControlled
|
|
57
|
+
} = React.useRef(controlled !== void 0);
|
|
58
|
+
const [valueState, setValue] = React.useState(defaultProp);
|
|
59
|
+
const value = isControlled ? controlled : valueState;
|
|
60
|
+
if (process.env.NODE_ENV !== "production") {
|
|
61
|
+
React.useEffect(() => {
|
|
62
|
+
if (isControlled !== (controlled !== void 0)) {
|
|
63
|
+
console.error([`Base UI: A component is changing the ${isControlled ? "" : "un"}controlled ${state} state of ${name} to be ${isControlled ? "un" : ""}controlled.`, "Elements should not switch from uncontrolled to controlled (or vice versa).", `Decide between using a controlled or uncontrolled ${name} element for the lifetime of the component.`, "The nature of the state is determined during the first render. It's considered controlled if the value is not `undefined`.", "More info: https://fb.me/react-controlled-components"].join("\n"));
|
|
64
|
+
}
|
|
65
|
+
}, [state, name, controlled]);
|
|
66
|
+
const {
|
|
67
|
+
current: defaultValue
|
|
68
|
+
} = React.useRef(defaultProp);
|
|
69
|
+
React.useEffect(() => {
|
|
70
|
+
if (!isControlled && JSON.stringify(defaultValue) !== JSON.stringify(defaultProp)) {
|
|
71
|
+
console.error([`Base UI: A component is changing the default ${state} state of an uncontrolled ${name} after being initialized. To suppress this warning opt to use a controlled ${name}.`].join("\n"));
|
|
72
|
+
}
|
|
73
|
+
}, [JSON.stringify(defaultProp)]);
|
|
74
|
+
}
|
|
75
|
+
const setValueIfUncontrolled = React.useCallback((newValue) => {
|
|
76
|
+
if (!isControlled) {
|
|
77
|
+
setValue(newValue);
|
|
78
|
+
}
|
|
79
|
+
}, []);
|
|
80
|
+
return [value, setValueIfUncontrolled];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ../../node_modules/.pnpm/@base-ui+utils@0.2.5_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/utils/esm/useId.js
|
|
84
|
+
import * as React2 from "react";
|
|
85
|
+
var globalId = 0;
|
|
86
|
+
function useGlobalId(idOverride, prefix = "mui") {
|
|
87
|
+
const [defaultId, setDefaultId] = React2.useState(idOverride);
|
|
88
|
+
const id = idOverride || defaultId;
|
|
89
|
+
React2.useEffect(() => {
|
|
90
|
+
if (defaultId == null) {
|
|
91
|
+
globalId += 1;
|
|
92
|
+
setDefaultId(`${prefix}-${globalId}`);
|
|
93
|
+
}
|
|
94
|
+
}, [defaultId, prefix]);
|
|
95
|
+
return id;
|
|
96
|
+
}
|
|
97
|
+
var maybeReactUseId = SafeReact.useId;
|
|
98
|
+
function useId(idOverride, prefix) {
|
|
99
|
+
if (maybeReactUseId !== void 0) {
|
|
100
|
+
const reactId = maybeReactUseId();
|
|
101
|
+
return idOverride ?? (prefix ? `${prefix}-${reactId}` : reactId);
|
|
102
|
+
}
|
|
103
|
+
return useGlobalId(idOverride, prefix);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ../../node_modules/.pnpm/@base-ui+react@1.2.0_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/esm/utils/useBaseUiId.js
|
|
107
|
+
function useBaseUiId(idOverride) {
|
|
108
|
+
return useId(idOverride, "base-ui");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ../../node_modules/.pnpm/@base-ui+react@1.2.0_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/esm/toggle-group/ToggleGroupContext.js
|
|
112
|
+
import * as React3 from "react";
|
|
113
|
+
var ToggleGroupContext = /* @__PURE__ */ React3.createContext(void 0);
|
|
114
|
+
if (process.env.NODE_ENV !== "production") ToggleGroupContext.displayName = "ToggleGroupContext";
|
|
115
|
+
function useToggleGroupContext(optional = true) {
|
|
116
|
+
const context = React3.useContext(ToggleGroupContext);
|
|
117
|
+
if (context === void 0 && !optional) {
|
|
118
|
+
throw new Error(process.env.NODE_ENV !== "production" ? "Base UI: ToggleGroupContext is missing. ToggleGroup parts must be placed within <ToggleGroup>." : formatErrorMessage(7));
|
|
119
|
+
}
|
|
120
|
+
return context;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ../../node_modules/.pnpm/@base-ui+react@1.2.0_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/esm/composite/item/useCompositeItem.js
|
|
124
|
+
import * as React6 from "react";
|
|
125
|
+
|
|
126
|
+
// ../../node_modules/.pnpm/@base-ui+react@1.2.0_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/esm/composite/list/useCompositeListItem.js
|
|
127
|
+
import * as React5 from "react";
|
|
128
|
+
|
|
129
|
+
// ../../node_modules/.pnpm/@base-ui+react@1.2.0_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/esm/composite/list/CompositeListContext.js
|
|
130
|
+
import * as React4 from "react";
|
|
131
|
+
var CompositeListContext = /* @__PURE__ */ React4.createContext({
|
|
132
|
+
register: () => {
|
|
133
|
+
},
|
|
134
|
+
unregister: () => {
|
|
135
|
+
},
|
|
136
|
+
subscribeMapChange: () => {
|
|
137
|
+
return () => {
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
elementsRef: {
|
|
141
|
+
current: []
|
|
142
|
+
},
|
|
143
|
+
nextIndexRef: {
|
|
144
|
+
current: 0
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
if (process.env.NODE_ENV !== "production") CompositeListContext.displayName = "CompositeListContext";
|
|
148
|
+
function useCompositeListContext() {
|
|
149
|
+
return React4.useContext(CompositeListContext);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ../../node_modules/.pnpm/@base-ui+react@1.2.0_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/esm/composite/list/useCompositeListItem.js
|
|
153
|
+
var IndexGuessBehavior = /* @__PURE__ */ (function(IndexGuessBehavior2) {
|
|
154
|
+
IndexGuessBehavior2[IndexGuessBehavior2["None"] = 0] = "None";
|
|
155
|
+
IndexGuessBehavior2[IndexGuessBehavior2["GuessFromOrder"] = 1] = "GuessFromOrder";
|
|
156
|
+
return IndexGuessBehavior2;
|
|
157
|
+
})({});
|
|
158
|
+
function useCompositeListItem(params = {}) {
|
|
159
|
+
const {
|
|
160
|
+
label,
|
|
161
|
+
metadata,
|
|
162
|
+
textRef,
|
|
163
|
+
indexGuessBehavior,
|
|
164
|
+
index: externalIndex
|
|
165
|
+
} = params;
|
|
166
|
+
const {
|
|
167
|
+
register,
|
|
168
|
+
unregister,
|
|
169
|
+
subscribeMapChange,
|
|
170
|
+
elementsRef,
|
|
171
|
+
labelsRef,
|
|
172
|
+
nextIndexRef
|
|
173
|
+
} = useCompositeListContext();
|
|
174
|
+
const indexRef = React5.useRef(-1);
|
|
175
|
+
const [index, setIndex] = React5.useState(externalIndex ?? (indexGuessBehavior === IndexGuessBehavior.GuessFromOrder ? () => {
|
|
176
|
+
if (indexRef.current === -1) {
|
|
177
|
+
const newIndex = nextIndexRef.current;
|
|
178
|
+
nextIndexRef.current += 1;
|
|
179
|
+
indexRef.current = newIndex;
|
|
180
|
+
}
|
|
181
|
+
return indexRef.current;
|
|
182
|
+
} : -1));
|
|
183
|
+
const componentRef = React5.useRef(null);
|
|
184
|
+
const ref = React5.useCallback((node) => {
|
|
185
|
+
componentRef.current = node;
|
|
186
|
+
if (index !== -1 && node !== null) {
|
|
187
|
+
elementsRef.current[index] = node;
|
|
188
|
+
if (labelsRef) {
|
|
189
|
+
const isLabelDefined = label !== void 0;
|
|
190
|
+
labelsRef.current[index] = isLabelDefined ? label : textRef?.current?.textContent ?? node.textContent;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}, [index, elementsRef, labelsRef, label, textRef]);
|
|
194
|
+
useIsoLayoutEffect(() => {
|
|
195
|
+
if (externalIndex != null) {
|
|
196
|
+
return void 0;
|
|
197
|
+
}
|
|
198
|
+
const node = componentRef.current;
|
|
199
|
+
if (node) {
|
|
200
|
+
register(node, metadata);
|
|
201
|
+
return () => {
|
|
202
|
+
unregister(node);
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return void 0;
|
|
206
|
+
}, [externalIndex, register, unregister, metadata]);
|
|
207
|
+
useIsoLayoutEffect(() => {
|
|
208
|
+
if (externalIndex != null) {
|
|
209
|
+
return void 0;
|
|
210
|
+
}
|
|
211
|
+
return subscribeMapChange((map) => {
|
|
212
|
+
const i = componentRef.current ? map.get(componentRef.current)?.index : null;
|
|
213
|
+
if (i != null) {
|
|
214
|
+
setIndex(i);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}, [externalIndex, subscribeMapChange, setIndex]);
|
|
218
|
+
return React5.useMemo(() => ({
|
|
219
|
+
ref,
|
|
220
|
+
index
|
|
221
|
+
}), [index, ref]);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ../../node_modules/.pnpm/@base-ui+react@1.2.0_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/esm/composite/item/useCompositeItem.js
|
|
225
|
+
function useCompositeItem(params = {}) {
|
|
226
|
+
const {
|
|
227
|
+
highlightItemOnHover,
|
|
228
|
+
highlightedIndex,
|
|
229
|
+
onHighlightedIndexChange
|
|
230
|
+
} = useCompositeRootContext();
|
|
231
|
+
const {
|
|
232
|
+
ref,
|
|
233
|
+
index
|
|
234
|
+
} = useCompositeListItem(params);
|
|
235
|
+
const isHighlighted = highlightedIndex === index;
|
|
236
|
+
const itemRef = React6.useRef(null);
|
|
237
|
+
const mergedRef = useMergedRefs(ref, itemRef);
|
|
238
|
+
const compositeProps = React6.useMemo(() => ({
|
|
239
|
+
tabIndex: isHighlighted ? 0 : -1,
|
|
240
|
+
onFocus() {
|
|
241
|
+
onHighlightedIndexChange(index);
|
|
242
|
+
},
|
|
243
|
+
onMouseMove() {
|
|
244
|
+
const item = itemRef.current;
|
|
245
|
+
if (!highlightItemOnHover || !item) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const disabled2 = item.hasAttribute("disabled") || item.ariaDisabled === "true";
|
|
249
|
+
if (!isHighlighted && !disabled2) {
|
|
250
|
+
item.focus();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}), [isHighlighted, onHighlightedIndexChange, index, highlightItemOnHover]);
|
|
254
|
+
return {
|
|
255
|
+
compositeProps,
|
|
256
|
+
compositeRef: mergedRef,
|
|
257
|
+
index
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ../../node_modules/.pnpm/@base-ui+react@1.2.0_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/esm/composite/item/CompositeItem.js
|
|
262
|
+
function CompositeItem(componentProps) {
|
|
263
|
+
const {
|
|
264
|
+
render,
|
|
265
|
+
className,
|
|
266
|
+
state = EMPTY_OBJECT,
|
|
267
|
+
props = EMPTY_ARRAY,
|
|
268
|
+
refs = EMPTY_ARRAY,
|
|
269
|
+
metadata,
|
|
270
|
+
stateAttributesMapping,
|
|
271
|
+
tag = "div",
|
|
272
|
+
...elementProps
|
|
273
|
+
} = componentProps;
|
|
274
|
+
const {
|
|
275
|
+
compositeProps,
|
|
276
|
+
compositeRef
|
|
277
|
+
} = useCompositeItem({
|
|
278
|
+
metadata
|
|
279
|
+
});
|
|
280
|
+
return useRenderElement(tag, componentProps, {
|
|
281
|
+
state,
|
|
282
|
+
ref: [...refs, compositeRef],
|
|
283
|
+
props: [compositeProps, ...props, elementProps],
|
|
284
|
+
stateAttributesMapping
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ../../node_modules/.pnpm/@base-ui+react@1.2.0_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/esm/utils/reason-parts.js
|
|
289
|
+
var reason_parts_exports = {};
|
|
290
|
+
__export(reason_parts_exports, {
|
|
291
|
+
cancelOpen: () => cancelOpen,
|
|
292
|
+
chipRemovePress: () => chipRemovePress,
|
|
293
|
+
clearPress: () => clearPress,
|
|
294
|
+
closePress: () => closePress,
|
|
295
|
+
closeWatcher: () => closeWatcher,
|
|
296
|
+
decrementPress: () => decrementPress,
|
|
297
|
+
disabled: () => disabled,
|
|
298
|
+
drag: () => drag,
|
|
299
|
+
escapeKey: () => escapeKey,
|
|
300
|
+
focusOut: () => focusOut,
|
|
301
|
+
imperativeAction: () => imperativeAction,
|
|
302
|
+
incrementPress: () => incrementPress,
|
|
303
|
+
inputBlur: () => inputBlur,
|
|
304
|
+
inputChange: () => inputChange,
|
|
305
|
+
inputClear: () => inputClear,
|
|
306
|
+
inputPaste: () => inputPaste,
|
|
307
|
+
inputPress: () => inputPress,
|
|
308
|
+
itemPress: () => itemPress,
|
|
309
|
+
keyboard: () => keyboard,
|
|
310
|
+
linkPress: () => linkPress,
|
|
311
|
+
listNavigation: () => listNavigation,
|
|
312
|
+
none: () => none,
|
|
313
|
+
outsidePress: () => outsidePress,
|
|
314
|
+
pointer: () => pointer,
|
|
315
|
+
scrub: () => scrub,
|
|
316
|
+
siblingOpen: () => siblingOpen,
|
|
317
|
+
swipe: () => swipe,
|
|
318
|
+
trackPress: () => trackPress,
|
|
319
|
+
triggerFocus: () => triggerFocus,
|
|
320
|
+
triggerHover: () => triggerHover,
|
|
321
|
+
triggerPress: () => triggerPress,
|
|
322
|
+
wheel: () => wheel,
|
|
323
|
+
windowResize: () => windowResize
|
|
324
|
+
});
|
|
325
|
+
var none = "none";
|
|
326
|
+
var triggerPress = "trigger-press";
|
|
327
|
+
var triggerHover = "trigger-hover";
|
|
328
|
+
var triggerFocus = "trigger-focus";
|
|
329
|
+
var outsidePress = "outside-press";
|
|
330
|
+
var itemPress = "item-press";
|
|
331
|
+
var closePress = "close-press";
|
|
332
|
+
var linkPress = "link-press";
|
|
333
|
+
var clearPress = "clear-press";
|
|
334
|
+
var chipRemovePress = "chip-remove-press";
|
|
335
|
+
var trackPress = "track-press";
|
|
336
|
+
var incrementPress = "increment-press";
|
|
337
|
+
var decrementPress = "decrement-press";
|
|
338
|
+
var inputChange = "input-change";
|
|
339
|
+
var inputClear = "input-clear";
|
|
340
|
+
var inputBlur = "input-blur";
|
|
341
|
+
var inputPaste = "input-paste";
|
|
342
|
+
var inputPress = "input-press";
|
|
343
|
+
var focusOut = "focus-out";
|
|
344
|
+
var escapeKey = "escape-key";
|
|
345
|
+
var closeWatcher = "close-watcher";
|
|
346
|
+
var listNavigation = "list-navigation";
|
|
347
|
+
var keyboard = "keyboard";
|
|
348
|
+
var pointer = "pointer";
|
|
349
|
+
var drag = "drag";
|
|
350
|
+
var wheel = "wheel";
|
|
351
|
+
var scrub = "scrub";
|
|
352
|
+
var cancelOpen = "cancel-open";
|
|
353
|
+
var siblingOpen = "sibling-open";
|
|
354
|
+
var disabled = "disabled";
|
|
355
|
+
var imperativeAction = "imperative-action";
|
|
356
|
+
var swipe = "swipe";
|
|
357
|
+
var windowResize = "window-resize";
|
|
358
|
+
|
|
359
|
+
// ../../node_modules/.pnpm/@base-ui+react@1.2.0_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/esm/utils/createBaseUIEventDetails.js
|
|
360
|
+
function createChangeEventDetails(reason, event, trigger, customProperties) {
|
|
361
|
+
let canceled = false;
|
|
362
|
+
let allowPropagation = false;
|
|
363
|
+
const custom = customProperties ?? EMPTY_OBJECT;
|
|
364
|
+
const details = {
|
|
365
|
+
reason,
|
|
366
|
+
event: event ?? new Event("base-ui"),
|
|
367
|
+
cancel() {
|
|
368
|
+
canceled = true;
|
|
369
|
+
},
|
|
370
|
+
allowPropagation() {
|
|
371
|
+
allowPropagation = true;
|
|
372
|
+
},
|
|
373
|
+
get isCanceled() {
|
|
374
|
+
return canceled;
|
|
375
|
+
},
|
|
376
|
+
get isPropagationAllowed() {
|
|
377
|
+
return allowPropagation;
|
|
378
|
+
},
|
|
379
|
+
trigger,
|
|
380
|
+
...custom
|
|
381
|
+
};
|
|
382
|
+
return details;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// ../../node_modules/.pnpm/@base-ui+react@1.2.0_@types+react@19.2.14_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@base-ui/react/esm/toggle/Toggle.js
|
|
386
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
387
|
+
var Toggle = /* @__PURE__ */ React7.forwardRef(function Toggle2(componentProps, forwardedRef) {
|
|
388
|
+
const {
|
|
389
|
+
className,
|
|
390
|
+
defaultPressed: defaultPressedProp = false,
|
|
391
|
+
disabled: disabledProp = false,
|
|
392
|
+
form,
|
|
393
|
+
// never participates in form validation
|
|
394
|
+
onPressedChange: onPressedChangeProp,
|
|
395
|
+
pressed: pressedProp,
|
|
396
|
+
render,
|
|
397
|
+
type,
|
|
398
|
+
// cannot change button type
|
|
399
|
+
value: valueProp,
|
|
400
|
+
nativeButton = true,
|
|
401
|
+
...elementProps
|
|
402
|
+
} = componentProps;
|
|
403
|
+
const value = useBaseUiId(valueProp || void 0);
|
|
404
|
+
const groupContext = useToggleGroupContext();
|
|
405
|
+
const groupValue = groupContext?.value ?? [];
|
|
406
|
+
const defaultPressed = groupContext ? void 0 : defaultPressedProp;
|
|
407
|
+
const disabled2 = (disabledProp || groupContext?.disabled) ?? false;
|
|
408
|
+
if (process.env.NODE_ENV !== "production") {
|
|
409
|
+
useIsoLayoutEffect(() => {
|
|
410
|
+
if (groupContext && valueProp === void 0 && groupContext.isValueInitialized) {
|
|
411
|
+
error("A `<Toggle>` component rendered in a `<ToggleGroup>` has no explicit `value` prop.", "This will cause issues between the Toggle Group and Toggle values.", "Provide the `<Toggle>` with a `value` prop matching the `<ToggleGroup>` values prop type.");
|
|
412
|
+
}
|
|
413
|
+
}, [groupContext, valueProp, groupContext?.isValueInitialized]);
|
|
414
|
+
}
|
|
415
|
+
const [pressed, setPressedState] = useControlled({
|
|
416
|
+
controlled: groupContext ? value !== void 0 && groupValue.indexOf(value) > -1 : pressedProp,
|
|
417
|
+
default: defaultPressed,
|
|
418
|
+
name: "Toggle",
|
|
419
|
+
state: "pressed"
|
|
420
|
+
});
|
|
421
|
+
const onPressedChange = useStableCallback((nextPressed, eventDetails) => {
|
|
422
|
+
if (value) {
|
|
423
|
+
groupContext?.setGroupValue?.(value, nextPressed, eventDetails);
|
|
424
|
+
}
|
|
425
|
+
onPressedChangeProp?.(nextPressed, eventDetails);
|
|
426
|
+
});
|
|
427
|
+
const {
|
|
428
|
+
getButtonProps,
|
|
429
|
+
buttonRef
|
|
430
|
+
} = useButton({
|
|
431
|
+
disabled: disabled2,
|
|
432
|
+
native: nativeButton
|
|
433
|
+
});
|
|
434
|
+
const state = {
|
|
435
|
+
disabled: disabled2,
|
|
436
|
+
pressed
|
|
437
|
+
};
|
|
438
|
+
const refs = [buttonRef, forwardedRef];
|
|
439
|
+
const props = [{
|
|
440
|
+
"aria-pressed": pressed,
|
|
441
|
+
onClick(event) {
|
|
442
|
+
const nextPressed = !pressed;
|
|
443
|
+
const details = createChangeEventDetails(reason_parts_exports.none, event.nativeEvent);
|
|
444
|
+
onPressedChange(nextPressed, details);
|
|
445
|
+
if (details.isCanceled) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
setPressedState(nextPressed);
|
|
449
|
+
}
|
|
450
|
+
}, elementProps, getButtonProps];
|
|
451
|
+
const element = useRenderElement("button", componentProps, {
|
|
452
|
+
enabled: !groupContext,
|
|
453
|
+
state,
|
|
454
|
+
ref: refs,
|
|
455
|
+
props
|
|
456
|
+
});
|
|
457
|
+
if (groupContext) {
|
|
458
|
+
return /* @__PURE__ */ _jsx(CompositeItem, {
|
|
459
|
+
tag: "button",
|
|
460
|
+
render,
|
|
461
|
+
className,
|
|
462
|
+
state,
|
|
463
|
+
refs,
|
|
464
|
+
props
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
return element;
|
|
468
|
+
});
|
|
469
|
+
if (process.env.NODE_ENV !== "production") Toggle.displayName = "Toggle";
|
|
470
|
+
|
|
471
|
+
// ../ui/src/components/toggle.tsx
|
|
472
|
+
import { jsx } from "react/jsx-runtime";
|
|
473
|
+
var toggleVariants = cva(
|
|
474
|
+
"group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
475
|
+
{
|
|
476
|
+
variants: {
|
|
477
|
+
variant: {
|
|
478
|
+
default: "bg-transparent",
|
|
479
|
+
outline: "border border-input bg-transparent hover:bg-muted"
|
|
480
|
+
},
|
|
481
|
+
size: {
|
|
482
|
+
default: "h-8 min-w-8 px-2",
|
|
483
|
+
sm: "h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-1.5 text-[0.8rem]",
|
|
484
|
+
lg: "h-9 min-w-9 px-2.5"
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
defaultVariants: {
|
|
488
|
+
variant: "default",
|
|
489
|
+
size: "default"
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
);
|
|
493
|
+
function Toggle3({
|
|
494
|
+
className,
|
|
495
|
+
variant = "default",
|
|
496
|
+
size = "default",
|
|
497
|
+
...props
|
|
498
|
+
}) {
|
|
499
|
+
return /* @__PURE__ */ jsx(
|
|
500
|
+
Toggle,
|
|
501
|
+
{
|
|
502
|
+
"data-slot": "toggle",
|
|
503
|
+
className: cn(toggleVariants({ variant, size, className })),
|
|
504
|
+
...props
|
|
505
|
+
}
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// src/lib/schema-serializer.ts
|
|
510
|
+
import { z } from "zod";
|
|
511
|
+
function serializeTools(tools) {
|
|
512
|
+
return tools.map((tool) => ({
|
|
513
|
+
name: tool.name,
|
|
514
|
+
description: tool.description,
|
|
515
|
+
parameters: z.toJSONSchema(tool.parameters, {
|
|
516
|
+
target: "openapi-3.0"
|
|
517
|
+
})
|
|
518
|
+
}));
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/hooks/use-tool-registration.ts
|
|
522
|
+
import { useEffect as useEffect3 } from "react";
|
|
523
|
+
import { RpcError } from "livekit-client";
|
|
524
|
+
import { useEnsureRoom } from "@livekit/components-react";
|
|
525
|
+
function useToolRegistration(tools, onToolComplete) {
|
|
526
|
+
const room = useEnsureRoom();
|
|
527
|
+
useEffect3(() => {
|
|
528
|
+
if (!tools || tools.length === 0) return;
|
|
529
|
+
const cleanups = [];
|
|
530
|
+
for (const tool of tools) {
|
|
531
|
+
const methodName = `tool:${tool.name}`;
|
|
532
|
+
room.registerRpcMethod(methodName, async (data) => {
|
|
533
|
+
try {
|
|
534
|
+
const params = JSON.parse(data.payload);
|
|
535
|
+
const parsed = tool.parameters.parse(params);
|
|
536
|
+
const result = await tool.execute(parsed);
|
|
537
|
+
onToolComplete?.();
|
|
538
|
+
return JSON.stringify(result);
|
|
539
|
+
} catch (error2) {
|
|
540
|
+
const message = error2 instanceof Error ? error2.message : "Tool execution failed";
|
|
541
|
+
throw new RpcError(1, message);
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
cleanups.push(() => {
|
|
545
|
+
room.unregisterRpcMethod(methodName);
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
return () => {
|
|
549
|
+
for (const cleanup of cleanups) {
|
|
550
|
+
cleanup();
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
}, [room, tools, onToolComplete]);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// src/components/agent-session-provider.tsx
|
|
557
|
+
import {
|
|
558
|
+
SessionProvider,
|
|
559
|
+
RoomAudioRenderer
|
|
560
|
+
} from "@livekit/components-react";
|
|
561
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
562
|
+
function AgentSessionProvider({
|
|
563
|
+
session,
|
|
564
|
+
children,
|
|
565
|
+
...roomAudioRendererProps
|
|
566
|
+
}) {
|
|
567
|
+
return /* @__PURE__ */ jsxs(SessionProvider, { session, children: [
|
|
568
|
+
children,
|
|
569
|
+
/* @__PURE__ */ jsx2(RoomAudioRenderer, { ...roomAudioRendererProps })
|
|
570
|
+
] });
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// src/components/agent-audio-visualizer-bar.tsx
|
|
574
|
+
import { Fragment, useMemo as useMemo3 } from "react";
|
|
575
|
+
import {
|
|
576
|
+
useMultibandTrackVolume
|
|
577
|
+
} from "@livekit/components-react";
|
|
578
|
+
|
|
579
|
+
// src/hooks/use-agent-audio-visualizer-bar.ts
|
|
580
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
|
|
581
|
+
function generateConnectingSequenceBar(columns) {
|
|
582
|
+
const seq = [];
|
|
583
|
+
for (let x = 0; x < columns; x++) {
|
|
584
|
+
seq.push([x, columns - 1 - x]);
|
|
585
|
+
}
|
|
586
|
+
return seq;
|
|
587
|
+
}
|
|
588
|
+
function generateListeningSequenceBar(columns) {
|
|
589
|
+
const center = Math.floor(columns / 2);
|
|
590
|
+
return [[center], [-1]];
|
|
591
|
+
}
|
|
592
|
+
function useAgentAudioVisualizerBarAnimator(state, columns, interval) {
|
|
593
|
+
const [index, setIndex] = useState4(0);
|
|
594
|
+
const [sequence, setSequence] = useState4([[]]);
|
|
595
|
+
useEffect4(() => {
|
|
596
|
+
if (state === "thinking") {
|
|
597
|
+
setSequence(generateListeningSequenceBar(columns));
|
|
598
|
+
} else if (state === "connecting" || state === "initializing") {
|
|
599
|
+
setSequence([...generateConnectingSequenceBar(columns)]);
|
|
600
|
+
} else if (state === "listening") {
|
|
601
|
+
setSequence(generateListeningSequenceBar(columns));
|
|
602
|
+
} else if (state === void 0 || state === "speaking") {
|
|
603
|
+
setSequence([new Array(columns).fill(0).map((_, idx) => idx)]);
|
|
604
|
+
} else {
|
|
605
|
+
setSequence([[]]);
|
|
606
|
+
}
|
|
607
|
+
setIndex(0);
|
|
608
|
+
}, [state, columns]);
|
|
609
|
+
const animationFrameId = useRef4(null);
|
|
610
|
+
useEffect4(() => {
|
|
611
|
+
let startTime = performance.now();
|
|
612
|
+
const animate = (time) => {
|
|
613
|
+
if (time - startTime >= interval) {
|
|
614
|
+
setIndex((prev) => prev + 1);
|
|
615
|
+
startTime = time;
|
|
616
|
+
}
|
|
617
|
+
animationFrameId.current = requestAnimationFrame(animate);
|
|
618
|
+
};
|
|
619
|
+
animationFrameId.current = requestAnimationFrame(animate);
|
|
620
|
+
return () => {
|
|
621
|
+
if (animationFrameId.current !== null) {
|
|
622
|
+
cancelAnimationFrame(animationFrameId.current);
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
}, [interval, columns, state, sequence.length]);
|
|
626
|
+
return sequence[index % sequence.length] ?? [];
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// src/components/agent-audio-visualizer-bar.tsx
|
|
630
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
631
|
+
function AgentAudioVisualizerBar({
|
|
632
|
+
state = "connecting",
|
|
633
|
+
barCount = 3,
|
|
634
|
+
audioTrack,
|
|
635
|
+
className,
|
|
636
|
+
children
|
|
637
|
+
}) {
|
|
638
|
+
const sequencerInterval = useMemo3(() => {
|
|
639
|
+
switch (state) {
|
|
640
|
+
case "connecting":
|
|
641
|
+
return 2e3 / barCount;
|
|
642
|
+
case "initializing":
|
|
643
|
+
return 2e3;
|
|
644
|
+
case "listening":
|
|
645
|
+
return 500;
|
|
646
|
+
case "thinking":
|
|
647
|
+
return 150;
|
|
648
|
+
default:
|
|
649
|
+
return 1e3;
|
|
650
|
+
}
|
|
651
|
+
}, [state, barCount]);
|
|
652
|
+
const highlightedIndices = useAgentAudioVisualizerBarAnimator(
|
|
653
|
+
state,
|
|
654
|
+
barCount,
|
|
655
|
+
sequencerInterval
|
|
656
|
+
);
|
|
657
|
+
const volumeBands = useMultibandTrackVolume(audioTrack, {
|
|
658
|
+
bands: barCount,
|
|
659
|
+
loPass: 100,
|
|
660
|
+
hiPass: 200
|
|
661
|
+
});
|
|
662
|
+
const bands = useMemo3(
|
|
663
|
+
() => state === "speaking" ? volumeBands : new Array(barCount).fill(0),
|
|
664
|
+
[state, volumeBands, barCount]
|
|
665
|
+
);
|
|
666
|
+
return /* @__PURE__ */ jsx3(
|
|
667
|
+
"div",
|
|
668
|
+
{
|
|
669
|
+
"data-lk-state": state,
|
|
670
|
+
className: cn(
|
|
671
|
+
"relative flex h-6 items-center justify-center gap-0.5",
|
|
672
|
+
className
|
|
673
|
+
),
|
|
674
|
+
children: bands.map(
|
|
675
|
+
(band, idx) => children ? /* @__PURE__ */ jsx3(Fragment, { children }, idx) : /* @__PURE__ */ jsx3(
|
|
676
|
+
"div",
|
|
677
|
+
{
|
|
678
|
+
"data-lk-index": idx,
|
|
679
|
+
"data-lk-highlighted": highlightedIndices.includes(idx),
|
|
680
|
+
style: { height: `${Math.max(4, band * 24)}px` },
|
|
681
|
+
className: "min-h-1 w-1 rounded-full bg-primary/20 transition-[height] duration-100 ease-linear data-[lk-highlighted=true]:bg-primary"
|
|
682
|
+
},
|
|
683
|
+
idx
|
|
684
|
+
)
|
|
685
|
+
)
|
|
686
|
+
}
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// src/components/agent-track-toggle.tsx
|
|
691
|
+
import { useMemo as useMemo4, useState as useState5 } from "react";
|
|
692
|
+
import { Track } from "livekit-client";
|
|
693
|
+
import {
|
|
694
|
+
MicIcon,
|
|
695
|
+
MicOffIcon,
|
|
696
|
+
Loading03Icon,
|
|
697
|
+
VideoIcon,
|
|
698
|
+
VideoOffIcon
|
|
699
|
+
} from "@hugeicons/core-free-icons";
|
|
700
|
+
import { HugeiconsIcon } from "@hugeicons/react";
|
|
701
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
702
|
+
function getSourceIcon(source, enabled, pending = false) {
|
|
703
|
+
if (pending) return Loading03Icon;
|
|
704
|
+
switch (source) {
|
|
705
|
+
case Track.Source.Microphone:
|
|
706
|
+
return enabled ? MicIcon : MicOffIcon;
|
|
707
|
+
case Track.Source.Camera:
|
|
708
|
+
return enabled ? VideoIcon : VideoOffIcon;
|
|
709
|
+
default:
|
|
710
|
+
return MicIcon;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function AgentTrackToggle({
|
|
714
|
+
source,
|
|
715
|
+
pending = false,
|
|
716
|
+
pressed,
|
|
717
|
+
defaultPressed = false,
|
|
718
|
+
className,
|
|
719
|
+
onPressedChange,
|
|
720
|
+
...props
|
|
721
|
+
}) {
|
|
722
|
+
const [uncontrolledPressed, setUncontrolledPressed] = useState5(
|
|
723
|
+
defaultPressed ?? false
|
|
724
|
+
);
|
|
725
|
+
const isControlled = pressed !== void 0;
|
|
726
|
+
const resolvedPressed = useMemo4(
|
|
727
|
+
() => (isControlled ? pressed : uncontrolledPressed) ?? false,
|
|
728
|
+
[isControlled, pressed, uncontrolledPressed]
|
|
729
|
+
);
|
|
730
|
+
const IconComponent = getSourceIcon(
|
|
731
|
+
source,
|
|
732
|
+
resolvedPressed,
|
|
733
|
+
pending
|
|
734
|
+
);
|
|
735
|
+
const handlePressedChange = (nextPressed) => {
|
|
736
|
+
if (!isControlled) setUncontrolledPressed(nextPressed);
|
|
737
|
+
onPressedChange?.(nextPressed);
|
|
738
|
+
};
|
|
739
|
+
return /* @__PURE__ */ jsxs2(
|
|
740
|
+
Toggle3,
|
|
741
|
+
{
|
|
742
|
+
pressed: isControlled ? pressed : void 0,
|
|
743
|
+
defaultPressed: isControlled ? void 0 : defaultPressed,
|
|
744
|
+
"aria-label": `Toggle ${source}`,
|
|
745
|
+
onPressedChange: handlePressedChange,
|
|
746
|
+
className: cn(
|
|
747
|
+
"size-9 rounded-lg",
|
|
748
|
+
"data-[state=on]:bg-muted data-[state=on]:text-foreground",
|
|
749
|
+
"data-[state=off]:bg-destructive/10 data-[state=off]:text-destructive",
|
|
750
|
+
className
|
|
751
|
+
),
|
|
752
|
+
...props,
|
|
753
|
+
children: [
|
|
754
|
+
/* @__PURE__ */ jsx4(
|
|
755
|
+
HugeiconsIcon,
|
|
756
|
+
{
|
|
757
|
+
icon: IconComponent,
|
|
758
|
+
className: cn(pending && "animate-spin")
|
|
759
|
+
}
|
|
760
|
+
),
|
|
761
|
+
props.children
|
|
762
|
+
]
|
|
763
|
+
}
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// src/components/agent-track-control.tsx
|
|
768
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
769
|
+
function AgentTrackControl({
|
|
770
|
+
source,
|
|
771
|
+
pressed,
|
|
772
|
+
pending,
|
|
773
|
+
disabled: disabled2,
|
|
774
|
+
className,
|
|
775
|
+
audioTrack,
|
|
776
|
+
onPressedChange
|
|
777
|
+
}) {
|
|
778
|
+
return /* @__PURE__ */ jsx5("div", { className: cn("flex items-center gap-0 rounded-md", className), children: /* @__PURE__ */ jsx5(
|
|
779
|
+
AgentTrackToggle,
|
|
780
|
+
{
|
|
781
|
+
source,
|
|
782
|
+
pressed,
|
|
783
|
+
pending,
|
|
784
|
+
disabled: disabled2,
|
|
785
|
+
onPressedChange,
|
|
786
|
+
className: "peer/track group/track focus:z-10 has-[.audiovisualizer]:w-auto has-[.audiovisualizer]:px-3 has-[~_button]:rounded-r-none has-[~_button]:border-r-0 has-[~_button]:pr-2 has-[~_button]:pl-3",
|
|
787
|
+
children: audioTrack && /* @__PURE__ */ jsx5(
|
|
788
|
+
AgentAudioVisualizerBar,
|
|
789
|
+
{
|
|
790
|
+
barCount: 3,
|
|
791
|
+
state: pressed ? "speaking" : "disconnected",
|
|
792
|
+
audioTrack: pressed ? audioTrack : void 0,
|
|
793
|
+
className: "audiovisualizer"
|
|
794
|
+
}
|
|
795
|
+
)
|
|
796
|
+
}
|
|
797
|
+
) });
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// src/components/agent-disconnect-button.tsx
|
|
801
|
+
import { useSessionContext } from "@livekit/components-react";
|
|
802
|
+
import { CallEnd03Icon } from "@hugeicons/core-free-icons";
|
|
803
|
+
import { HugeiconsIcon as HugeiconsIcon2 } from "@hugeicons/react";
|
|
804
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
805
|
+
function AgentDisconnectButton({
|
|
806
|
+
children,
|
|
807
|
+
onClick,
|
|
808
|
+
className,
|
|
809
|
+
...props
|
|
810
|
+
}) {
|
|
811
|
+
const { end } = useSessionContext();
|
|
812
|
+
const handleClick = (event) => {
|
|
813
|
+
onClick?.(event);
|
|
814
|
+
if (typeof end === "function") end();
|
|
815
|
+
};
|
|
816
|
+
return /* @__PURE__ */ jsx6(
|
|
817
|
+
Button,
|
|
818
|
+
{
|
|
819
|
+
variant: "destructive",
|
|
820
|
+
size: "icon",
|
|
821
|
+
onClick: handleClick,
|
|
822
|
+
className,
|
|
823
|
+
...props,
|
|
824
|
+
children: children ?? /* @__PURE__ */ jsx6(HugeiconsIcon2, { icon: CallEnd03Icon })
|
|
825
|
+
}
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// src/components/agent-transcript-panel.tsx
|
|
830
|
+
import { useState as useState6 } from "react";
|
|
831
|
+
import { useChat } from "@livekit/components-react";
|
|
832
|
+
import { HugeiconsIcon as HugeiconsIcon3 } from "@hugeicons/react";
|
|
833
|
+
import { Loading03Icon as Loading03Icon2, Navigation03Icon } from "@hugeicons/core-free-icons";
|
|
834
|
+
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
835
|
+
function AgentTranscriptPanel() {
|
|
836
|
+
const { send } = useChat();
|
|
837
|
+
const [message, setMessage] = useState6("");
|
|
838
|
+
const [isSending, setIsSending] = useState6(false);
|
|
839
|
+
const isDisabled = isSending || message.trim().length === 0;
|
|
840
|
+
const handleSend = async () => {
|
|
841
|
+
if (isDisabled) return;
|
|
842
|
+
try {
|
|
843
|
+
setIsSending(true);
|
|
844
|
+
await send(message.trim());
|
|
845
|
+
setMessage("");
|
|
846
|
+
} catch {
|
|
847
|
+
} finally {
|
|
848
|
+
setIsSending(false);
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
const handleKeyDown = (e) => {
|
|
852
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
853
|
+
e.preventDefault();
|
|
854
|
+
handleSend();
|
|
855
|
+
}
|
|
856
|
+
};
|
|
857
|
+
return /* @__PURE__ */ jsx7("div", { className: "flex h-full flex-col border-b", children: /* @__PURE__ */ jsxs3("div", { className: "flex items-end gap-1 px-2 py-1", children: [
|
|
858
|
+
/* @__PURE__ */ jsx7(
|
|
859
|
+
"textarea",
|
|
860
|
+
{
|
|
861
|
+
autoFocus: true,
|
|
862
|
+
value: message,
|
|
863
|
+
disabled: isSending,
|
|
864
|
+
placeholder: "Type something...",
|
|
865
|
+
onKeyDown: handleKeyDown,
|
|
866
|
+
onChange: (e) => setMessage(e.target.value),
|
|
867
|
+
className: "field-sizing-content max-h-20 min-h-6 flex-1 resize-none py-1 text-xs [scrollbar-width:thin] focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
868
|
+
}
|
|
869
|
+
),
|
|
870
|
+
/* @__PURE__ */ jsx7(
|
|
871
|
+
"button",
|
|
872
|
+
{
|
|
873
|
+
type: "button",
|
|
874
|
+
disabled: isDisabled,
|
|
875
|
+
onClick: handleSend,
|
|
876
|
+
title: isSending ? "Sending\u2026" : "Send",
|
|
877
|
+
className: cn(
|
|
878
|
+
"flex size-6 flex-shrink-0 items-center justify-center self-end rounded",
|
|
879
|
+
isDisabled ? "cursor-not-allowed text-muted-foreground" : "cursor-pointer text-foreground hover:opacity-80"
|
|
880
|
+
),
|
|
881
|
+
children: isSending ? /* @__PURE__ */ jsx7(
|
|
882
|
+
HugeiconsIcon3,
|
|
883
|
+
{
|
|
884
|
+
icon: Loading03Icon2,
|
|
885
|
+
size: 18,
|
|
886
|
+
className: "animate-spin"
|
|
887
|
+
}
|
|
888
|
+
) : /* @__PURE__ */ jsx7(
|
|
889
|
+
HugeiconsIcon3,
|
|
890
|
+
{
|
|
891
|
+
icon: Navigation03Icon,
|
|
892
|
+
size: 18,
|
|
893
|
+
className: "fill-primary/50 text-primary"
|
|
894
|
+
}
|
|
895
|
+
)
|
|
896
|
+
}
|
|
897
|
+
)
|
|
898
|
+
] }) });
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// src/hooks/use-input-controls.ts
|
|
902
|
+
import { useCallback as useCallback3 } from "react";
|
|
903
|
+
import { Track as Track2 } from "livekit-client";
|
|
904
|
+
import {
|
|
905
|
+
useTrackToggle,
|
|
906
|
+
usePersistentUserChoices,
|
|
907
|
+
useLocalParticipantPermissions,
|
|
908
|
+
useSessionContext as useSessionContext2
|
|
909
|
+
} from "@livekit/components-react";
|
|
910
|
+
function useInputControls({
|
|
911
|
+
saveUserChoices = true,
|
|
912
|
+
onDeviceError
|
|
913
|
+
} = {}) {
|
|
914
|
+
const {
|
|
915
|
+
local: { microphoneTrack }
|
|
916
|
+
} = useSessionContext2();
|
|
917
|
+
const microphoneToggle = useTrackToggle({
|
|
918
|
+
source: Track2.Source.Microphone,
|
|
919
|
+
onDeviceError: (error2) => onDeviceError?.({ source: Track2.Source.Microphone, error: error2 })
|
|
920
|
+
});
|
|
921
|
+
const cameraToggle = useTrackToggle({
|
|
922
|
+
source: Track2.Source.Camera,
|
|
923
|
+
onDeviceError: (error2) => onDeviceError?.({ source: Track2.Source.Camera, error: error2 })
|
|
924
|
+
});
|
|
925
|
+
const screenShareToggle = useTrackToggle({
|
|
926
|
+
source: Track2.Source.ScreenShare,
|
|
927
|
+
onDeviceError: (error2) => onDeviceError?.({ source: Track2.Source.ScreenShare, error: error2 })
|
|
928
|
+
});
|
|
929
|
+
const {
|
|
930
|
+
saveAudioInputEnabled,
|
|
931
|
+
saveVideoInputEnabled,
|
|
932
|
+
saveAudioInputDeviceId,
|
|
933
|
+
saveVideoInputDeviceId
|
|
934
|
+
} = usePersistentUserChoices({ preventSave: !saveUserChoices });
|
|
935
|
+
const handleAudioDeviceChange = useCallback3(
|
|
936
|
+
(deviceId) => {
|
|
937
|
+
saveAudioInputDeviceId(deviceId ?? "default");
|
|
938
|
+
},
|
|
939
|
+
[saveAudioInputDeviceId]
|
|
940
|
+
);
|
|
941
|
+
const handleVideoDeviceChange = useCallback3(
|
|
942
|
+
(deviceId) => {
|
|
943
|
+
saveVideoInputDeviceId(deviceId ?? "default");
|
|
944
|
+
},
|
|
945
|
+
[saveVideoInputDeviceId]
|
|
946
|
+
);
|
|
947
|
+
const handleToggleCamera = useCallback3(
|
|
948
|
+
async (enabled) => {
|
|
949
|
+
if (screenShareToggle.enabled) {
|
|
950
|
+
screenShareToggle.toggle(false);
|
|
951
|
+
}
|
|
952
|
+
await cameraToggle.toggle(enabled);
|
|
953
|
+
saveVideoInputEnabled(!cameraToggle.enabled);
|
|
954
|
+
},
|
|
955
|
+
[cameraToggle, screenShareToggle, saveVideoInputEnabled]
|
|
956
|
+
);
|
|
957
|
+
const handleToggleMicrophone = useCallback3(
|
|
958
|
+
async (enabled) => {
|
|
959
|
+
await microphoneToggle.toggle(enabled);
|
|
960
|
+
saveAudioInputEnabled(!microphoneToggle.enabled);
|
|
961
|
+
},
|
|
962
|
+
[microphoneToggle, saveAudioInputEnabled]
|
|
963
|
+
);
|
|
964
|
+
const handleToggleScreenShare = useCallback3(
|
|
965
|
+
async (enabled) => {
|
|
966
|
+
if (cameraToggle.enabled) {
|
|
967
|
+
cameraToggle.toggle(false);
|
|
968
|
+
}
|
|
969
|
+
await screenShareToggle.toggle(enabled);
|
|
970
|
+
},
|
|
971
|
+
[cameraToggle, screenShareToggle]
|
|
972
|
+
);
|
|
973
|
+
const handleMicrophoneDeviceSelectError = useCallback3(
|
|
974
|
+
(error2) => onDeviceError?.({ source: Track2.Source.Microphone, error: error2 }),
|
|
975
|
+
[onDeviceError]
|
|
976
|
+
);
|
|
977
|
+
const handleCameraDeviceSelectError = useCallback3(
|
|
978
|
+
(error2) => onDeviceError?.({ source: Track2.Source.Camera, error: error2 }),
|
|
979
|
+
[onDeviceError]
|
|
980
|
+
);
|
|
981
|
+
return {
|
|
982
|
+
microphoneTrack,
|
|
983
|
+
cameraToggle: {
|
|
984
|
+
...cameraToggle,
|
|
985
|
+
toggle: handleToggleCamera
|
|
986
|
+
},
|
|
987
|
+
microphoneToggle: {
|
|
988
|
+
...microphoneToggle,
|
|
989
|
+
toggle: handleToggleMicrophone
|
|
990
|
+
},
|
|
991
|
+
screenShareToggle: {
|
|
992
|
+
...screenShareToggle,
|
|
993
|
+
toggle: handleToggleScreenShare
|
|
994
|
+
},
|
|
995
|
+
handleAudioDeviceChange,
|
|
996
|
+
handleVideoDeviceChange,
|
|
997
|
+
handleMicrophoneDeviceSelectError,
|
|
998
|
+
handleCameraDeviceSelectError
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// src/components/start-audio-button.tsx
|
|
1003
|
+
import { useEnsureRoom as useEnsureRoom2, useStartAudio } from "@livekit/components-react";
|
|
1004
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1005
|
+
function StartAudioButton({
|
|
1006
|
+
size = "default",
|
|
1007
|
+
variant = "default",
|
|
1008
|
+
label,
|
|
1009
|
+
room,
|
|
1010
|
+
...props
|
|
1011
|
+
}) {
|
|
1012
|
+
const roomEnsured = useEnsureRoom2(room);
|
|
1013
|
+
const { mergedProps } = useStartAudio({ room: roomEnsured, props });
|
|
1014
|
+
return /* @__PURE__ */ jsx8(Button, { size, variant, ...props, ...mergedProps, children: label });
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// src/components/workflow-execution.tsx
|
|
1018
|
+
import { useState as useState7, useCallback as useCallback4, useRef as useRef5 } from "react";
|
|
1019
|
+
import { createPortal } from "react-dom";
|
|
1020
|
+
import {
|
|
1021
|
+
motion,
|
|
1022
|
+
useMotionValue,
|
|
1023
|
+
useSpring,
|
|
1024
|
+
AnimatePresence
|
|
1025
|
+
} from "motion/react";
|
|
1026
|
+
import {
|
|
1027
|
+
Cursor02Icon,
|
|
1028
|
+
ArrowLeft01Icon,
|
|
1029
|
+
ArrowRight01Icon,
|
|
1030
|
+
Tick01Icon
|
|
1031
|
+
} from "@hugeicons/core-free-icons";
|
|
1032
|
+
import { HugeiconsIcon as HugeiconsIcon4 } from "@hugeicons/react";
|
|
1033
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1034
|
+
var SPRING_CONFIG = { stiffness: 120, damping: 20, mass: 0.8 };
|
|
1035
|
+
var CURSOR_OFFSET = 16;
|
|
1036
|
+
function WorkflowExecution({
|
|
1037
|
+
workflow,
|
|
1038
|
+
cursor,
|
|
1039
|
+
onClose,
|
|
1040
|
+
onError
|
|
1041
|
+
}) {
|
|
1042
|
+
const [currentIndex, setCurrentIndex] = useState7(0);
|
|
1043
|
+
const steps = workflow.steps;
|
|
1044
|
+
const currentStep = steps[currentIndex];
|
|
1045
|
+
const isFirstStep = currentIndex === 0;
|
|
1046
|
+
const isLastStep = currentIndex === steps.length - 1;
|
|
1047
|
+
const handleBack = useCallback4(() => {
|
|
1048
|
+
setCurrentIndex((prev) => Math.max(0, prev - 1));
|
|
1049
|
+
}, []);
|
|
1050
|
+
const handleNext = useCallback4(() => {
|
|
1051
|
+
setCurrentIndex((prev) => {
|
|
1052
|
+
if (prev >= steps.length - 1) {
|
|
1053
|
+
onClose();
|
|
1054
|
+
return prev;
|
|
1055
|
+
}
|
|
1056
|
+
return prev + 1;
|
|
1057
|
+
});
|
|
1058
|
+
}, [steps.length, onClose]);
|
|
1059
|
+
if (!currentStep) return null;
|
|
1060
|
+
return /* @__PURE__ */ jsx9(
|
|
1061
|
+
ExecutionOverlay,
|
|
1062
|
+
{
|
|
1063
|
+
step: currentStep,
|
|
1064
|
+
stepIndex: currentIndex,
|
|
1065
|
+
totalSteps: steps.length,
|
|
1066
|
+
isFirstStep,
|
|
1067
|
+
isLastStep,
|
|
1068
|
+
cursor,
|
|
1069
|
+
onBack: handleBack,
|
|
1070
|
+
onNext: handleNext,
|
|
1071
|
+
onError
|
|
1072
|
+
}
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
function ExecutionOverlay({
|
|
1076
|
+
step,
|
|
1077
|
+
stepIndex,
|
|
1078
|
+
totalSteps,
|
|
1079
|
+
isFirstStep,
|
|
1080
|
+
isLastStep,
|
|
1081
|
+
cursor,
|
|
1082
|
+
onBack,
|
|
1083
|
+
onNext,
|
|
1084
|
+
onError
|
|
1085
|
+
}) {
|
|
1086
|
+
const { rect } = useElementTracker({
|
|
1087
|
+
selector: step.elementId,
|
|
1088
|
+
urlPath: step.urlPath,
|
|
1089
|
+
onError: onError ? (msg) => {
|
|
1090
|
+
if (msg !== null) onError(msg);
|
|
1091
|
+
} : void 0,
|
|
1092
|
+
onClick: onNext
|
|
1093
|
+
});
|
|
1094
|
+
const hasAnimated = useRef5(false);
|
|
1095
|
+
const springX = useSpring(useMotionValue(0), SPRING_CONFIG);
|
|
1096
|
+
const springY = useSpring(useMotionValue(0), SPRING_CONFIG);
|
|
1097
|
+
if (rect) {
|
|
1098
|
+
const targetX = rect.right + CURSOR_OFFSET;
|
|
1099
|
+
const targetY = rect.bottom + CURSOR_OFFSET;
|
|
1100
|
+
if (!hasAnimated.current) {
|
|
1101
|
+
springX.jump(targetX);
|
|
1102
|
+
springY.jump(targetY);
|
|
1103
|
+
hasAnimated.current = true;
|
|
1104
|
+
} else {
|
|
1105
|
+
springX.set(targetX);
|
|
1106
|
+
springY.set(targetY);
|
|
1107
|
+
}
|
|
1108
|
+
} else {
|
|
1109
|
+
hasAnimated.current = false;
|
|
1110
|
+
}
|
|
1111
|
+
return createPortal(
|
|
1112
|
+
/* @__PURE__ */ jsx9(AnimatePresence, { children: rect && /* @__PURE__ */ jsxs4(
|
|
1113
|
+
motion.div,
|
|
1114
|
+
{
|
|
1115
|
+
"data-meridial-ui": true,
|
|
1116
|
+
className: "pointer-events-none fixed z-[99999] flex items-start gap-3",
|
|
1117
|
+
style: { top: springY, left: springX },
|
|
1118
|
+
initial: { opacity: 0, scale: 0.6 },
|
|
1119
|
+
animate: { opacity: 1, scale: 1 },
|
|
1120
|
+
exit: { opacity: 0, scale: 0.6 },
|
|
1121
|
+
transition: { duration: 0.2 },
|
|
1122
|
+
children: [
|
|
1123
|
+
cursor ?? /* @__PURE__ */ jsx9(
|
|
1124
|
+
HugeiconsIcon4,
|
|
1125
|
+
{
|
|
1126
|
+
icon: Cursor02Icon,
|
|
1127
|
+
strokeWidth: 2,
|
|
1128
|
+
size: 32,
|
|
1129
|
+
className: "mt-1 shrink-0 fill-primary/60 text-primary"
|
|
1130
|
+
}
|
|
1131
|
+
),
|
|
1132
|
+
/* @__PURE__ */ jsxs4(
|
|
1133
|
+
"div",
|
|
1134
|
+
{
|
|
1135
|
+
"data-meridial-ui": true,
|
|
1136
|
+
className: "pointer-events-auto w-72 overflow-hidden rounded-lg border border-border bg-card shadow-lg",
|
|
1137
|
+
children: [
|
|
1138
|
+
/* @__PURE__ */ jsx9(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsxs4(
|
|
1139
|
+
motion.div,
|
|
1140
|
+
{
|
|
1141
|
+
initial: { opacity: 0, y: 8 },
|
|
1142
|
+
animate: { opacity: 1, y: 0 },
|
|
1143
|
+
exit: { opacity: 0, y: -8 },
|
|
1144
|
+
transition: { duration: 0.2, ease: "easeInOut" },
|
|
1145
|
+
className: "px-4 pt-3 pb-2",
|
|
1146
|
+
children: [
|
|
1147
|
+
/* @__PURE__ */ jsx9("p", { className: "text-sm leading-relaxed text-foreground", children: step.description || /* @__PURE__ */ jsx9("span", { className: "text-muted-foreground italic", children: "No description" }) }),
|
|
1148
|
+
/* @__PURE__ */ jsxs4("span", { className: "mt-1 block text-xs text-muted-foreground", children: [
|
|
1149
|
+
"Step ",
|
|
1150
|
+
stepIndex + 1,
|
|
1151
|
+
" of ",
|
|
1152
|
+
totalSteps
|
|
1153
|
+
] })
|
|
1154
|
+
]
|
|
1155
|
+
},
|
|
1156
|
+
step.id
|
|
1157
|
+
) }),
|
|
1158
|
+
/* @__PURE__ */ jsxs4(
|
|
1159
|
+
"div",
|
|
1160
|
+
{
|
|
1161
|
+
"data-meridial-ui": true,
|
|
1162
|
+
className: "flex justify-end gap-2 border-t px-3 py-2",
|
|
1163
|
+
children: [
|
|
1164
|
+
/* @__PURE__ */ jsxs4(
|
|
1165
|
+
Button,
|
|
1166
|
+
{
|
|
1167
|
+
variant: "outline",
|
|
1168
|
+
size: "sm",
|
|
1169
|
+
"data-meridial-ui": true,
|
|
1170
|
+
onClick: onBack,
|
|
1171
|
+
disabled: isFirstStep,
|
|
1172
|
+
children: [
|
|
1173
|
+
/* @__PURE__ */ jsx9(HugeiconsIcon4, { icon: ArrowLeft01Icon, size: 14 }),
|
|
1174
|
+
"Back"
|
|
1175
|
+
]
|
|
1176
|
+
}
|
|
1177
|
+
),
|
|
1178
|
+
/* @__PURE__ */ jsx9(
|
|
1179
|
+
Button,
|
|
1180
|
+
{
|
|
1181
|
+
variant: "default",
|
|
1182
|
+
size: "sm",
|
|
1183
|
+
"data-meridial-ui": true,
|
|
1184
|
+
onClick: onNext,
|
|
1185
|
+
children: isLastStep ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1186
|
+
"Finish",
|
|
1187
|
+
/* @__PURE__ */ jsx9(HugeiconsIcon4, { icon: Tick01Icon, size: 14 })
|
|
1188
|
+
] }) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1189
|
+
"Next",
|
|
1190
|
+
/* @__PURE__ */ jsx9(HugeiconsIcon4, { icon: ArrowRight01Icon, size: 14 })
|
|
1191
|
+
] })
|
|
1192
|
+
}
|
|
1193
|
+
)
|
|
1194
|
+
]
|
|
1195
|
+
}
|
|
1196
|
+
)
|
|
1197
|
+
]
|
|
1198
|
+
}
|
|
1199
|
+
)
|
|
1200
|
+
]
|
|
1201
|
+
}
|
|
1202
|
+
) }),
|
|
1203
|
+
document.body
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// src/components/outcome-bar.tsx
|
|
1208
|
+
import {
|
|
1209
|
+
ThumbsUpIcon,
|
|
1210
|
+
ThumbsDownIcon,
|
|
1211
|
+
MultiplicationSignIcon
|
|
1212
|
+
} from "@hugeicons/core-free-icons";
|
|
1213
|
+
import { HugeiconsIcon as HugeiconsIcon5 } from "@hugeicons/react";
|
|
1214
|
+
import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1215
|
+
function OutcomeBar({ onSubmit }) {
|
|
1216
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between border-b px-3 py-2", children: [
|
|
1217
|
+
/* @__PURE__ */ jsx10("span", { className: "text-sm text-muted-foreground", children: "Did we help?" }),
|
|
1218
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1", children: [
|
|
1219
|
+
/* @__PURE__ */ jsx10(
|
|
1220
|
+
Button,
|
|
1221
|
+
{
|
|
1222
|
+
variant: "ghost",
|
|
1223
|
+
size: "icon-sm",
|
|
1224
|
+
onClick: () => onSubmit("POSITIVE"),
|
|
1225
|
+
"aria-label": "Yes, this helped",
|
|
1226
|
+
children: /* @__PURE__ */ jsx10(HugeiconsIcon5, { icon: ThumbsUpIcon, className: "size-4" })
|
|
1227
|
+
}
|
|
1228
|
+
),
|
|
1229
|
+
/* @__PURE__ */ jsx10(
|
|
1230
|
+
Button,
|
|
1231
|
+
{
|
|
1232
|
+
variant: "ghost",
|
|
1233
|
+
size: "icon-sm",
|
|
1234
|
+
onClick: () => onSubmit("NEGATIVE"),
|
|
1235
|
+
"aria-label": "No, this didn't help",
|
|
1236
|
+
children: /* @__PURE__ */ jsx10(HugeiconsIcon5, { icon: ThumbsDownIcon, className: "size-4" })
|
|
1237
|
+
}
|
|
1238
|
+
),
|
|
1239
|
+
/* @__PURE__ */ jsx10(
|
|
1240
|
+
Button,
|
|
1241
|
+
{
|
|
1242
|
+
variant: "ghost",
|
|
1243
|
+
size: "icon-sm",
|
|
1244
|
+
onClick: () => onSubmit("NEUTRAL"),
|
|
1245
|
+
"aria-label": "Dismiss",
|
|
1246
|
+
children: /* @__PURE__ */ jsx10(HugeiconsIcon5, { icon: MultiplicationSignIcon, className: "size-4" })
|
|
1247
|
+
}
|
|
1248
|
+
)
|
|
1249
|
+
] })
|
|
1250
|
+
] });
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/components/meridial-logo.tsx
|
|
1254
|
+
import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1255
|
+
function MeridialLogo(props) {
|
|
1256
|
+
return /* @__PURE__ */ jsxs6(
|
|
1257
|
+
"svg",
|
|
1258
|
+
{
|
|
1259
|
+
viewBox: "0 0 120 120",
|
|
1260
|
+
fill: "none",
|
|
1261
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1262
|
+
...props,
|
|
1263
|
+
children: [
|
|
1264
|
+
/* @__PURE__ */ jsx11(
|
|
1265
|
+
"path",
|
|
1266
|
+
{
|
|
1267
|
+
d: "M13.281 74.0881L15.7251 69.8003L37.7371 89.6218L83.4484 98.732L102.441 97.8991L83.2815 101.205L35.3287 95.2458L13.281 74.0881Z",
|
|
1268
|
+
fill: "#009EBC",
|
|
1269
|
+
stroke: "#009EBC",
|
|
1270
|
+
strokeWidth: "0.5"
|
|
1271
|
+
}
|
|
1272
|
+
),
|
|
1273
|
+
/* @__PURE__ */ jsx11(
|
|
1274
|
+
"path",
|
|
1275
|
+
{
|
|
1276
|
+
d: "M88.6986 90.1613L15.4758 69.5032L36.8477 90.3775L83.0111 99.2024L102.445 97.7944L88.6986 90.1613Z",
|
|
1277
|
+
fill: "url(#fl-g0)"
|
|
1278
|
+
}
|
|
1279
|
+
),
|
|
1280
|
+
/* @__PURE__ */ jsx11(
|
|
1281
|
+
"path",
|
|
1282
|
+
{
|
|
1283
|
+
d: "M9.59212 38.8499L12.934 33.2319L34.6462 62.7104L86.7973 80.1624L108.813 81.1994L86.6098 82.9394L33.6228 69.8917L9.59212 38.8499Z",
|
|
1284
|
+
fill: "#009EBC",
|
|
1285
|
+
stroke: "#009EBC",
|
|
1286
|
+
strokeWidth: "0.5"
|
|
1287
|
+
}
|
|
1288
|
+
),
|
|
1289
|
+
/* @__PURE__ */ jsx11(
|
|
1290
|
+
"path",
|
|
1291
|
+
{
|
|
1292
|
+
d: "M94.1734 69.4478L12.7627 33.0524L34.7293 62.7357L86.5626 80.2897L109.003 81.1016L94.1734 69.4478Z",
|
|
1293
|
+
fill: "url(#fl-g1)"
|
|
1294
|
+
}
|
|
1295
|
+
),
|
|
1296
|
+
/* @__PURE__ */ jsx11(
|
|
1297
|
+
"path",
|
|
1298
|
+
{
|
|
1299
|
+
d: "M28.3329 17.4895L33.7347 13.5331L45.9901 38.7534L78.8505 55.5351L93.5023 57.1106L78.3627 57.1381L38.9556 40.7981L28.3329 17.4895Z",
|
|
1300
|
+
fill: "#009EBC",
|
|
1301
|
+
stroke: "#009EBC",
|
|
1302
|
+
strokeWidth: "0.5"
|
|
1303
|
+
}
|
|
1304
|
+
),
|
|
1305
|
+
/* @__PURE__ */ jsx11(
|
|
1306
|
+
"path",
|
|
1307
|
+
{
|
|
1308
|
+
d: "M84.6112 46.7121L33.6396 12.9639L46.1488 38.6786L78.9538 55.4676L93.5814 57.0605L84.6112 46.7121Z",
|
|
1309
|
+
fill: "url(#fl-g2)"
|
|
1310
|
+
}
|
|
1311
|
+
),
|
|
1312
|
+
/* @__PURE__ */ jsxs6("defs", { children: [
|
|
1313
|
+
/* @__PURE__ */ jsxs6(
|
|
1314
|
+
"linearGradient",
|
|
1315
|
+
{
|
|
1316
|
+
id: "fl-g0",
|
|
1317
|
+
x1: "57.7988",
|
|
1318
|
+
y1: "66.4369",
|
|
1319
|
+
x2: "60.2919",
|
|
1320
|
+
y2: "100.848",
|
|
1321
|
+
gradientUnits: "userSpaceOnUse",
|
|
1322
|
+
children: [
|
|
1323
|
+
/* @__PURE__ */ jsx11("stop", { stopColor: "#C3F6FF" }),
|
|
1324
|
+
/* @__PURE__ */ jsx11("stop", { offset: "1", stopColor: "#007B92" })
|
|
1325
|
+
]
|
|
1326
|
+
}
|
|
1327
|
+
),
|
|
1328
|
+
/* @__PURE__ */ jsxs6(
|
|
1329
|
+
"linearGradient",
|
|
1330
|
+
{
|
|
1331
|
+
id: "fl-g1",
|
|
1332
|
+
x1: "61.633",
|
|
1333
|
+
y1: "34.8205",
|
|
1334
|
+
x2: "60.0227",
|
|
1335
|
+
y2: "79.3295",
|
|
1336
|
+
gradientUnits: "userSpaceOnUse",
|
|
1337
|
+
children: [
|
|
1338
|
+
/* @__PURE__ */ jsx11("stop", { stopColor: "#B8F3FF" }),
|
|
1339
|
+
/* @__PURE__ */ jsx11("stop", { offset: "1", stopColor: "#007F99" })
|
|
1340
|
+
]
|
|
1341
|
+
}
|
|
1342
|
+
),
|
|
1343
|
+
/* @__PURE__ */ jsxs6(
|
|
1344
|
+
"linearGradient",
|
|
1345
|
+
{
|
|
1346
|
+
id: "fl-g2",
|
|
1347
|
+
x1: "65.4953",
|
|
1348
|
+
y1: "16.4329",
|
|
1349
|
+
x2: "61.4521",
|
|
1350
|
+
y2: "53.5618",
|
|
1351
|
+
gradientUnits: "userSpaceOnUse",
|
|
1352
|
+
children: [
|
|
1353
|
+
/* @__PURE__ */ jsx11("stop", { stopColor: "#D0F8FF" }),
|
|
1354
|
+
/* @__PURE__ */ jsx11("stop", { offset: "1", stopColor: "#009EBC" })
|
|
1355
|
+
]
|
|
1356
|
+
}
|
|
1357
|
+
)
|
|
1358
|
+
] })
|
|
1359
|
+
]
|
|
1360
|
+
}
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
// src/components/voicebox-menu.tsx
|
|
1365
|
+
import { useState as useState8 } from "react";
|
|
1366
|
+
import { motion as motion2, AnimatePresence as AnimatePresence2 } from "motion/react";
|
|
1367
|
+
import { HugeiconsIcon as HugeiconsIcon6 } from "@hugeicons/react";
|
|
1368
|
+
import {
|
|
1369
|
+
Call02Icon,
|
|
1370
|
+
ViewOffSlashIcon,
|
|
1371
|
+
ArrowDown01Icon,
|
|
1372
|
+
PlayIcon
|
|
1373
|
+
} from "@hugeicons/core-free-icons";
|
|
1374
|
+
import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1375
|
+
var POSITIONS = [
|
|
1376
|
+
"top-left",
|
|
1377
|
+
"top-right",
|
|
1378
|
+
"bottom-left",
|
|
1379
|
+
"bottom-right"
|
|
1380
|
+
];
|
|
1381
|
+
var POSITION_LABELS = {
|
|
1382
|
+
"top-left": "TL",
|
|
1383
|
+
"top-right": "TR",
|
|
1384
|
+
"bottom-left": "BL",
|
|
1385
|
+
"bottom-right": "BR"
|
|
1386
|
+
};
|
|
1387
|
+
var MENU_PLACEMENT = {
|
|
1388
|
+
"bottom-right": {
|
|
1389
|
+
originX: 1,
|
|
1390
|
+
originY: 1,
|
|
1391
|
+
className: "right-0 bottom-full mb-3"
|
|
1392
|
+
},
|
|
1393
|
+
"bottom-left": {
|
|
1394
|
+
originX: 0,
|
|
1395
|
+
originY: 1,
|
|
1396
|
+
className: "left-0 bottom-full mb-3"
|
|
1397
|
+
},
|
|
1398
|
+
"top-right": {
|
|
1399
|
+
originX: 1,
|
|
1400
|
+
originY: 0,
|
|
1401
|
+
className: "right-0 top-full mt-3"
|
|
1402
|
+
},
|
|
1403
|
+
"top-left": {
|
|
1404
|
+
originX: 0,
|
|
1405
|
+
originY: 0,
|
|
1406
|
+
className: "left-0 top-full mt-3"
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
function VoiceboxMenu({
|
|
1410
|
+
favoriteWorkflows,
|
|
1411
|
+
position,
|
|
1412
|
+
onPositionChange,
|
|
1413
|
+
onHide,
|
|
1414
|
+
onAskMeridial,
|
|
1415
|
+
onPlayGuide
|
|
1416
|
+
}) {
|
|
1417
|
+
const placement = MENU_PLACEMENT[position];
|
|
1418
|
+
const [guidesExpanded, setGuidesExpanded] = useState8(false);
|
|
1419
|
+
const hasFavorites = favoriteWorkflows.length > 0;
|
|
1420
|
+
return /* @__PURE__ */ jsxs7(
|
|
1421
|
+
motion2.div,
|
|
1422
|
+
{
|
|
1423
|
+
"data-meridial-ui": true,
|
|
1424
|
+
className: cn(
|
|
1425
|
+
"absolute z-50 w-56 overflow-hidden rounded-lg border border-border bg-card shadow-lg",
|
|
1426
|
+
placement.className
|
|
1427
|
+
),
|
|
1428
|
+
initial: { opacity: 0, scale: 0.9 },
|
|
1429
|
+
animate: { opacity: 1, scale: 1 },
|
|
1430
|
+
exit: { opacity: 0, scale: 0.9 },
|
|
1431
|
+
style: {
|
|
1432
|
+
transformOrigin: `${placement.originX * 100}% ${placement.originY * 100}%`
|
|
1433
|
+
},
|
|
1434
|
+
transition: { duration: 0.15, ease: "easeOut" },
|
|
1435
|
+
children: [
|
|
1436
|
+
hasFavorites && /* @__PURE__ */ jsxs7("div", { "data-meridial-ui": true, className: "border-b border-border", children: [
|
|
1437
|
+
/* @__PURE__ */ jsxs7(
|
|
1438
|
+
"button",
|
|
1439
|
+
{
|
|
1440
|
+
"data-meridial-ui": true,
|
|
1441
|
+
onClick: () => setGuidesExpanded((prev) => !prev),
|
|
1442
|
+
className: "flex w-full cursor-pointer items-center justify-between px-3 py-2",
|
|
1443
|
+
children: [
|
|
1444
|
+
/* @__PURE__ */ jsx12("span", { className: "text-xs font-medium tracking-wider text-muted-foreground", children: "Top Guides" }),
|
|
1445
|
+
/* @__PURE__ */ jsx12(
|
|
1446
|
+
HugeiconsIcon6,
|
|
1447
|
+
{
|
|
1448
|
+
icon: ArrowDown01Icon,
|
|
1449
|
+
size: 14,
|
|
1450
|
+
className: cn(
|
|
1451
|
+
"shrink-0 text-muted-foreground transition-transform duration-200",
|
|
1452
|
+
guidesExpanded && "rotate-180"
|
|
1453
|
+
)
|
|
1454
|
+
}
|
|
1455
|
+
)
|
|
1456
|
+
]
|
|
1457
|
+
}
|
|
1458
|
+
),
|
|
1459
|
+
/* @__PURE__ */ jsx12(AnimatePresence2, { initial: false, children: guidesExpanded && /* @__PURE__ */ jsx12(
|
|
1460
|
+
motion2.div,
|
|
1461
|
+
{
|
|
1462
|
+
initial: { height: 0, opacity: 0 },
|
|
1463
|
+
animate: { height: "auto", opacity: 1 },
|
|
1464
|
+
exit: { height: 0, opacity: 0 },
|
|
1465
|
+
transition: { duration: 0.15, ease: "easeOut" },
|
|
1466
|
+
style: { overflow: "hidden" },
|
|
1467
|
+
children: /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-0.5 px-3 pb-2", children: favoriteWorkflows.map((wf) => /* @__PURE__ */ jsxs7(
|
|
1468
|
+
"button",
|
|
1469
|
+
{
|
|
1470
|
+
"data-meridial-ui": true,
|
|
1471
|
+
onClick: () => onPlayGuide(wf),
|
|
1472
|
+
className: "flex w-full cursor-pointer items-center gap-2 rounded px-1.5 py-1 text-left text-sm text-foreground hover:bg-muted/50",
|
|
1473
|
+
children: [
|
|
1474
|
+
/* @__PURE__ */ jsx12(
|
|
1475
|
+
HugeiconsIcon6,
|
|
1476
|
+
{
|
|
1477
|
+
icon: PlayIcon,
|
|
1478
|
+
size: 16,
|
|
1479
|
+
className: "shrink-0 text-primary fill-primary/60"
|
|
1480
|
+
}
|
|
1481
|
+
),
|
|
1482
|
+
/* @__PURE__ */ jsx12("span", { className: "truncate", children: wf.name })
|
|
1483
|
+
]
|
|
1484
|
+
},
|
|
1485
|
+
wf.id
|
|
1486
|
+
)) })
|
|
1487
|
+
}
|
|
1488
|
+
) })
|
|
1489
|
+
] }),
|
|
1490
|
+
/* @__PURE__ */ jsxs7("div", { "data-meridial-ui": true, className: "border-b border-border px-3 py-2", children: [
|
|
1491
|
+
/* @__PURE__ */ jsx12("span", { className: "text-xs font-medium tracking-wider text-muted-foreground", children: "Position" }),
|
|
1492
|
+
/* @__PURE__ */ jsx12("div", { className: "mt-1 grid grid-cols-4 gap-1", children: POSITIONS.map((pos) => /* @__PURE__ */ jsx12(
|
|
1493
|
+
"button",
|
|
1494
|
+
{
|
|
1495
|
+
"data-meridial-ui": true,
|
|
1496
|
+
onClick: () => onPositionChange(pos),
|
|
1497
|
+
className: cn(
|
|
1498
|
+
"cursor-pointer rounded px-2 py-1 text-xs font-medium transition-colors",
|
|
1499
|
+
pos === position ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-muted/80 hover:text-foreground"
|
|
1500
|
+
),
|
|
1501
|
+
children: POSITION_LABELS[pos]
|
|
1502
|
+
},
|
|
1503
|
+
pos
|
|
1504
|
+
)) })
|
|
1505
|
+
] }),
|
|
1506
|
+
/* @__PURE__ */ jsxs7("div", { className: "px-3 py-2 flex items-center justify-between", children: [
|
|
1507
|
+
/* @__PURE__ */ jsx12("span", { className: "text-xs font-medium uppercase tracking-wider text-muted-foreground", children: "Hide Until Next Visit" }),
|
|
1508
|
+
/* @__PURE__ */ jsx12(Button, { variant: "ghost", size: "sm", "data-meridial-ui": true, onClick: onHide, children: /* @__PURE__ */ jsx12(
|
|
1509
|
+
HugeiconsIcon6,
|
|
1510
|
+
{
|
|
1511
|
+
icon: ViewOffSlashIcon,
|
|
1512
|
+
size: 16,
|
|
1513
|
+
className: "shrink-0"
|
|
1514
|
+
}
|
|
1515
|
+
) })
|
|
1516
|
+
] }),
|
|
1517
|
+
/* @__PURE__ */ jsx12(Separator, {}),
|
|
1518
|
+
/* @__PURE__ */ jsxs7("div", { className: "px-3 py-2 flex items-center justify-between", children: [
|
|
1519
|
+
/* @__PURE__ */ jsx12("span", { className: "text-xs font-medium tracking-wider text-muted-foreground", children: "Ask For Help" }),
|
|
1520
|
+
/* @__PURE__ */ jsx12(
|
|
1521
|
+
Button,
|
|
1522
|
+
{
|
|
1523
|
+
"data-meridial-ui": true,
|
|
1524
|
+
onClick: onAskMeridial,
|
|
1525
|
+
variant: "ghost",
|
|
1526
|
+
size: "sm",
|
|
1527
|
+
children: /* @__PURE__ */ jsx12(
|
|
1528
|
+
HugeiconsIcon6,
|
|
1529
|
+
{
|
|
1530
|
+
icon: Call02Icon,
|
|
1531
|
+
size: 16,
|
|
1532
|
+
className: "shrink-0 fill-primary/60 text-primary"
|
|
1533
|
+
}
|
|
1534
|
+
)
|
|
1535
|
+
}
|
|
1536
|
+
)
|
|
1537
|
+
] })
|
|
1538
|
+
]
|
|
1539
|
+
}
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
// src/voicebox.tsx
|
|
1544
|
+
import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1545
|
+
var BADGE_POSITION_CLASSES = {
|
|
1546
|
+
"bottom-right": "right-6 bottom-6",
|
|
1547
|
+
"bottom-left": "left-6 bottom-6",
|
|
1548
|
+
"top-right": "right-6 top-6",
|
|
1549
|
+
"top-left": "left-6 top-6"
|
|
1550
|
+
};
|
|
1551
|
+
var BAR_POSITION_CLASSES = {
|
|
1552
|
+
"bottom-right": "right-4 bottom-4",
|
|
1553
|
+
"bottom-left": "left-4 bottom-4",
|
|
1554
|
+
"top-right": "right-4 top-4",
|
|
1555
|
+
"top-left": "left-4 top-4"
|
|
1556
|
+
};
|
|
1557
|
+
function VoiceboxBadge({
|
|
1558
|
+
triggerIcon,
|
|
1559
|
+
onClick
|
|
1560
|
+
}) {
|
|
1561
|
+
return /* @__PURE__ */ jsx13(
|
|
1562
|
+
"button",
|
|
1563
|
+
{
|
|
1564
|
+
"data-meridial-ui": true,
|
|
1565
|
+
onClick,
|
|
1566
|
+
"aria-label": "Open assistant menu",
|
|
1567
|
+
className: "group size-10 flex cursor-pointer items-center justify-center rounded-full border border-border bg-background shadow-lg transition-transform duration-200 ease-out hover:scale-110",
|
|
1568
|
+
children: triggerIcon ?? /* @__PURE__ */ jsx13(MeridialLogo, { className: "size-6" })
|
|
1569
|
+
}
|
|
1570
|
+
);
|
|
1571
|
+
}
|
|
1572
|
+
function VoiceboxContent({
|
|
1573
|
+
baseUrl,
|
|
1574
|
+
publishableKey,
|
|
1575
|
+
tools,
|
|
1576
|
+
cursor,
|
|
1577
|
+
barPosition,
|
|
1578
|
+
initialWorkflow,
|
|
1579
|
+
onDisconnect,
|
|
1580
|
+
onError
|
|
1581
|
+
}) {
|
|
1582
|
+
const [showTranscript, setShowTranscript] = useState9(false);
|
|
1583
|
+
const [showOutcomeBar, setShowOutcomeBar] = useState9(false);
|
|
1584
|
+
const [activeWorkflow, setActiveWorkflow] = useState9(initialWorkflow ?? null);
|
|
1585
|
+
const workflowsRef = useRef6([]);
|
|
1586
|
+
const room = useEnsureRoom3();
|
|
1587
|
+
const autoStarted = useRef6(false);
|
|
1588
|
+
const outcomeShown = useRef6(false);
|
|
1589
|
+
const draggableRef = useRef6(null);
|
|
1590
|
+
const triggerOutcomeBar = useCallback5(() => {
|
|
1591
|
+
if (!outcomeShown.current) {
|
|
1592
|
+
outcomeShown.current = true;
|
|
1593
|
+
setShowOutcomeBar(true);
|
|
1594
|
+
}
|
|
1595
|
+
}, []);
|
|
1596
|
+
const handleOutcomeSubmit = useCallback5(
|
|
1597
|
+
async (outcome) => {
|
|
1598
|
+
setShowOutcomeBar(false);
|
|
1599
|
+
try {
|
|
1600
|
+
const sessionId = await room.getSid();
|
|
1601
|
+
await fetch(`${baseUrl}/api/outcome`, {
|
|
1602
|
+
method: "POST",
|
|
1603
|
+
headers: { "Content-Type": "application/json" },
|
|
1604
|
+
body: JSON.stringify({ publishableKey, sessionId, outcome })
|
|
1605
|
+
});
|
|
1606
|
+
} catch {
|
|
1607
|
+
}
|
|
1608
|
+
},
|
|
1609
|
+
[room, baseUrl, publishableKey]
|
|
1610
|
+
);
|
|
1611
|
+
useToolRegistration(tools, triggerOutcomeBar);
|
|
1612
|
+
useDraggable(draggableRef);
|
|
1613
|
+
useEffect5(() => {
|
|
1614
|
+
if (!publishableKey) return;
|
|
1615
|
+
fetch(`${baseUrl}/api/workflows`, {
|
|
1616
|
+
headers: { Authorization: `Bearer ${publishableKey}` }
|
|
1617
|
+
}).then((r) => r.json()).then((data) => {
|
|
1618
|
+
if (data.workflows) workflowsRef.current = data.workflows;
|
|
1619
|
+
}).catch((error2) => {
|
|
1620
|
+
onError?.(
|
|
1621
|
+
error2 instanceof Error ? error2.message : "Workflows fetch failed"
|
|
1622
|
+
);
|
|
1623
|
+
});
|
|
1624
|
+
}, [baseUrl, publishableKey]);
|
|
1625
|
+
useEffect5(() => {
|
|
1626
|
+
room.registerRpcMethod("workflow:execute", async (rpcData) => {
|
|
1627
|
+
try {
|
|
1628
|
+
const { slug } = JSON.parse(rpcData.payload);
|
|
1629
|
+
const apiWf = workflowsRef.current.find((w) => w.slug === slug);
|
|
1630
|
+
if (!apiWf) throw new Error(`Workflow '${slug}' not found`);
|
|
1631
|
+
const workflow = {
|
|
1632
|
+
id: apiWf.id,
|
|
1633
|
+
name: apiWf.name,
|
|
1634
|
+
steps: apiWf.steps,
|
|
1635
|
+
configured: true,
|
|
1636
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1637
|
+
};
|
|
1638
|
+
setActiveWorkflow({ id: apiWf.id, workflow });
|
|
1639
|
+
triggerOutcomeBar();
|
|
1640
|
+
return JSON.stringify({ success: true });
|
|
1641
|
+
} catch (error2) {
|
|
1642
|
+
onError?.(
|
|
1643
|
+
error2 instanceof Error ? error2.message : "Workflow execution failed"
|
|
1644
|
+
);
|
|
1645
|
+
throw new RpcError2(
|
|
1646
|
+
1,
|
|
1647
|
+
error2 instanceof Error ? error2.message : "Workflow execution failed"
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
});
|
|
1651
|
+
return () => {
|
|
1652
|
+
room.unregisterRpcMethod("workflow:execute");
|
|
1653
|
+
};
|
|
1654
|
+
}, [room]);
|
|
1655
|
+
const { state } = useAgent();
|
|
1656
|
+
const { start } = useSessionContext3();
|
|
1657
|
+
useEffect5(() => {
|
|
1658
|
+
if (!autoStarted.current) {
|
|
1659
|
+
autoStarted.current = true;
|
|
1660
|
+
start();
|
|
1661
|
+
}
|
|
1662
|
+
}, [start]);
|
|
1663
|
+
const isConnecting = state === "connecting" || state === "initializing" || state === "pre-connect-buffering";
|
|
1664
|
+
const isDisconnected = state === "disconnected" || state === "failed";
|
|
1665
|
+
const { microphoneTrack, microphoneToggle } = useInputControls();
|
|
1666
|
+
const handleDisconnect = () => {
|
|
1667
|
+
onDisconnect();
|
|
1668
|
+
};
|
|
1669
|
+
return /* @__PURE__ */ jsxs8(Fragment4, { children: [
|
|
1670
|
+
activeWorkflow && /* @__PURE__ */ jsx13(
|
|
1671
|
+
WorkflowExecution,
|
|
1672
|
+
{
|
|
1673
|
+
workflow: activeWorkflow.workflow,
|
|
1674
|
+
cursor,
|
|
1675
|
+
onClose: () => setActiveWorkflow(null),
|
|
1676
|
+
onError
|
|
1677
|
+
}
|
|
1678
|
+
),
|
|
1679
|
+
/* @__PURE__ */ jsxs8(
|
|
1680
|
+
"div",
|
|
1681
|
+
{
|
|
1682
|
+
ref: draggableRef,
|
|
1683
|
+
"data-meridial-ui": true,
|
|
1684
|
+
className: cn(
|
|
1685
|
+
"fixed z-50 w-96 items-stretch rounded border border-border bg-card shadow-md",
|
|
1686
|
+
BAR_POSITION_CLASSES[barPosition]
|
|
1687
|
+
),
|
|
1688
|
+
children: [
|
|
1689
|
+
/* @__PURE__ */ jsx13(AnimatePresence3, { initial: false, children: showTranscript && /* @__PURE__ */ jsx13(
|
|
1690
|
+
motion3.div,
|
|
1691
|
+
{
|
|
1692
|
+
initial: { height: 0, opacity: 0 },
|
|
1693
|
+
animate: { height: "auto", opacity: 1 },
|
|
1694
|
+
exit: { height: 0, opacity: 0 },
|
|
1695
|
+
transition: { duration: 0.24, ease: "easeOut" },
|
|
1696
|
+
style: { overflow: "hidden" },
|
|
1697
|
+
children: /* @__PURE__ */ jsx13(AgentTranscriptPanel, {})
|
|
1698
|
+
}
|
|
1699
|
+
) }),
|
|
1700
|
+
/* @__PURE__ */ jsx13(AnimatePresence3, { initial: false, children: showOutcomeBar && /* @__PURE__ */ jsx13(
|
|
1701
|
+
motion3.div,
|
|
1702
|
+
{
|
|
1703
|
+
initial: { height: 0, opacity: 0 },
|
|
1704
|
+
animate: { height: "auto", opacity: 1 },
|
|
1705
|
+
exit: { height: 0, opacity: 0 },
|
|
1706
|
+
transition: { duration: 0.24, ease: "easeOut" },
|
|
1707
|
+
style: { overflow: "hidden" },
|
|
1708
|
+
children: /* @__PURE__ */ jsx13(OutcomeBar, { onSubmit: handleOutcomeSubmit })
|
|
1709
|
+
}
|
|
1710
|
+
) }),
|
|
1711
|
+
/* @__PURE__ */ jsxs8("div", { "data-meridial-ui": true, className: "flex h-12 items-center", children: [
|
|
1712
|
+
/* @__PURE__ */ jsx13(DragHandle, { className: "px-2" }),
|
|
1713
|
+
/* @__PURE__ */ jsx13(Separator, { orientation: "vertical", className: "h-full" }),
|
|
1714
|
+
/* @__PURE__ */ jsxs8("div", { className: "relative flex h-full flex-1 items-center justify-between px-2", children: [
|
|
1715
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
|
|
1716
|
+
/* @__PURE__ */ jsx13(
|
|
1717
|
+
AgentTrackControl,
|
|
1718
|
+
{
|
|
1719
|
+
source: "microphone",
|
|
1720
|
+
pressed: microphoneToggle.enabled,
|
|
1721
|
+
pending: microphoneToggle.pending,
|
|
1722
|
+
disabled: microphoneToggle.pending,
|
|
1723
|
+
audioTrack: microphoneTrack,
|
|
1724
|
+
onPressedChange: microphoneToggle.toggle
|
|
1725
|
+
}
|
|
1726
|
+
),
|
|
1727
|
+
/* @__PURE__ */ jsx13(
|
|
1728
|
+
Toggle3,
|
|
1729
|
+
{
|
|
1730
|
+
disabled: isDisconnected || isConnecting,
|
|
1731
|
+
pressed: showTranscript,
|
|
1732
|
+
"aria-label": "Toggle transcript",
|
|
1733
|
+
onPressedChange: setShowTranscript,
|
|
1734
|
+
className: cn(
|
|
1735
|
+
"size-9 rounded-lg",
|
|
1736
|
+
showTranscript ? "bg-muted text-foreground" : "bg-transparent text-muted-foreground hover:text-foreground"
|
|
1737
|
+
),
|
|
1738
|
+
children: /* @__PURE__ */ jsx13(HugeiconsIcon7, { icon: ChatFeedback01Icon })
|
|
1739
|
+
}
|
|
1740
|
+
),
|
|
1741
|
+
/* @__PURE__ */ jsx13(
|
|
1742
|
+
AgentAudioVisualizerBar,
|
|
1743
|
+
{
|
|
1744
|
+
barCount: 12,
|
|
1745
|
+
state,
|
|
1746
|
+
audioTrack: microphoneTrack,
|
|
1747
|
+
className: "mx-4"
|
|
1748
|
+
}
|
|
1749
|
+
)
|
|
1750
|
+
] }),
|
|
1751
|
+
isConnecting ? /* @__PURE__ */ jsxs8(Button, { variant: "outline", children: [
|
|
1752
|
+
/* @__PURE__ */ jsx13("span", { children: "Connecting" }),
|
|
1753
|
+
/* @__PURE__ */ jsx13(HugeiconsIcon7, { icon: Loading03Icon3, className: "animate-spin" })
|
|
1754
|
+
] }) : /* @__PURE__ */ jsx13(AgentDisconnectButton, { onClick: handleDisconnect }),
|
|
1755
|
+
/* @__PURE__ */ jsx13(StartAudioButton, { label: "Start Audio" })
|
|
1756
|
+
] })
|
|
1757
|
+
] })
|
|
1758
|
+
]
|
|
1759
|
+
}
|
|
1760
|
+
)
|
|
1761
|
+
] });
|
|
1762
|
+
}
|
|
1763
|
+
function Voicebox({
|
|
1764
|
+
baseUrl = "",
|
|
1765
|
+
publishableKey,
|
|
1766
|
+
identifier,
|
|
1767
|
+
tools,
|
|
1768
|
+
triggerIcon,
|
|
1769
|
+
cursor,
|
|
1770
|
+
firstMessage,
|
|
1771
|
+
instructions,
|
|
1772
|
+
metadata,
|
|
1773
|
+
onError
|
|
1774
|
+
}) {
|
|
1775
|
+
const [view, setView] = useState9("badge");
|
|
1776
|
+
const [hidden, setHidden] = useState9(false);
|
|
1777
|
+
const [badgePosition, setBadgePosition] = useState9(() => {
|
|
1778
|
+
if (typeof window === "undefined") return "bottom-right";
|
|
1779
|
+
return localStorage.getItem(STORAGE_KEYS.triggerPos) || "bottom-right";
|
|
1780
|
+
});
|
|
1781
|
+
const [favoriteWorkflows, setFavoriteWorkflows] = useState9([]);
|
|
1782
|
+
const [initialWorkflow, setInitialWorkflow] = useState9(null);
|
|
1783
|
+
const [guideWorkflow, setGuideWorkflow] = useState9(null);
|
|
1784
|
+
useEffect5(() => {
|
|
1785
|
+
if (sessionStorage.getItem(STORAGE_KEYS.hidden) === "true") {
|
|
1786
|
+
setHidden(true);
|
|
1787
|
+
}
|
|
1788
|
+
}, []);
|
|
1789
|
+
useEffect5(() => {
|
|
1790
|
+
if (!publishableKey) return;
|
|
1791
|
+
fetch(`${baseUrl}/api/workflows`, {
|
|
1792
|
+
headers: { Authorization: `Bearer ${publishableKey}` }
|
|
1793
|
+
}).then((r) => r.json()).then((data) => {
|
|
1794
|
+
const parsed = apiWorkflowsResponseSchema.safeParse(data);
|
|
1795
|
+
if (parsed.success && parsed.data.workflows) {
|
|
1796
|
+
setFavoriteWorkflows(
|
|
1797
|
+
parsed.data.workflows.filter((w) => w.isFavorite)
|
|
1798
|
+
);
|
|
1799
|
+
}
|
|
1800
|
+
}).catch(() => {
|
|
1801
|
+
});
|
|
1802
|
+
}, [baseUrl, publishableKey]);
|
|
1803
|
+
const serializedTools = useMemo5(
|
|
1804
|
+
() => tools && tools.length > 0 ? serializeTools(tools) : [],
|
|
1805
|
+
[tools]
|
|
1806
|
+
);
|
|
1807
|
+
const tokenSource = useMemo5(
|
|
1808
|
+
() => TokenSource.custom(async () => {
|
|
1809
|
+
const response = await fetch(`${baseUrl}/api/auth/livekit`, {
|
|
1810
|
+
method: "POST",
|
|
1811
|
+
body: JSON.stringify({
|
|
1812
|
+
publishableKey,
|
|
1813
|
+
identifier,
|
|
1814
|
+
firstMessage,
|
|
1815
|
+
instructions,
|
|
1816
|
+
tools: serializedTools,
|
|
1817
|
+
metadata
|
|
1818
|
+
})
|
|
1819
|
+
});
|
|
1820
|
+
if (!response.ok) {
|
|
1821
|
+
const body = await response.json().catch(() => ({}));
|
|
1822
|
+
onError?.(body.error ?? `Token request failed (${response.status})`);
|
|
1823
|
+
return null;
|
|
1824
|
+
}
|
|
1825
|
+
return response.json();
|
|
1826
|
+
}),
|
|
1827
|
+
[
|
|
1828
|
+
baseUrl,
|
|
1829
|
+
publishableKey,
|
|
1830
|
+
identifier,
|
|
1831
|
+
firstMessage,
|
|
1832
|
+
instructions,
|
|
1833
|
+
serializedTools,
|
|
1834
|
+
metadata,
|
|
1835
|
+
onError
|
|
1836
|
+
]
|
|
1837
|
+
);
|
|
1838
|
+
const session = useSession(tokenSource);
|
|
1839
|
+
const handleDisconnect = () => {
|
|
1840
|
+
setView("badge");
|
|
1841
|
+
setInitialWorkflow(null);
|
|
1842
|
+
};
|
|
1843
|
+
const handlePositionChange = (pos) => {
|
|
1844
|
+
setBadgePosition(pos);
|
|
1845
|
+
localStorage.setItem(STORAGE_KEYS.triggerPos, pos);
|
|
1846
|
+
};
|
|
1847
|
+
const handleHide = () => {
|
|
1848
|
+
sessionStorage.setItem(STORAGE_KEYS.hidden, "true");
|
|
1849
|
+
setHidden(true);
|
|
1850
|
+
setView("badge");
|
|
1851
|
+
};
|
|
1852
|
+
const handleAskMeridial = () => {
|
|
1853
|
+
setInitialWorkflow(null);
|
|
1854
|
+
setView("call");
|
|
1855
|
+
};
|
|
1856
|
+
const handlePlayGuide = (wf) => {
|
|
1857
|
+
setGuideWorkflow({
|
|
1858
|
+
id: wf.id,
|
|
1859
|
+
name: wf.name,
|
|
1860
|
+
steps: wf.steps,
|
|
1861
|
+
configured: true,
|
|
1862
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1863
|
+
});
|
|
1864
|
+
setView("guide");
|
|
1865
|
+
};
|
|
1866
|
+
const handleBadgeClick = () => {
|
|
1867
|
+
setView((prev) => prev === "menu" ? "badge" : "menu");
|
|
1868
|
+
};
|
|
1869
|
+
if (hidden) return null;
|
|
1870
|
+
return /* @__PURE__ */ jsx13(AgentSessionProvider, { session, children: view === "call" ? /* @__PURE__ */ jsx13(
|
|
1871
|
+
VoiceboxContent,
|
|
1872
|
+
{
|
|
1873
|
+
baseUrl,
|
|
1874
|
+
publishableKey,
|
|
1875
|
+
tools,
|
|
1876
|
+
cursor,
|
|
1877
|
+
barPosition: badgePosition,
|
|
1878
|
+
initialWorkflow,
|
|
1879
|
+
onDisconnect: handleDisconnect,
|
|
1880
|
+
onError
|
|
1881
|
+
}
|
|
1882
|
+
) : view === "guide" && guideWorkflow ? /* @__PURE__ */ jsxs8(Fragment4, { children: [
|
|
1883
|
+
/* @__PURE__ */ jsx13(
|
|
1884
|
+
WorkflowExecution,
|
|
1885
|
+
{
|
|
1886
|
+
workflow: guideWorkflow,
|
|
1887
|
+
cursor,
|
|
1888
|
+
onClose: () => {
|
|
1889
|
+
setGuideWorkflow(null);
|
|
1890
|
+
setView("badge");
|
|
1891
|
+
},
|
|
1892
|
+
onError
|
|
1893
|
+
}
|
|
1894
|
+
),
|
|
1895
|
+
/* @__PURE__ */ jsx13(
|
|
1896
|
+
"div",
|
|
1897
|
+
{
|
|
1898
|
+
className: cn("fixed z-50", BADGE_POSITION_CLASSES[badgePosition]),
|
|
1899
|
+
children: /* @__PURE__ */ jsx13(
|
|
1900
|
+
VoiceboxBadge,
|
|
1901
|
+
{
|
|
1902
|
+
triggerIcon,
|
|
1903
|
+
onClick: handleBadgeClick
|
|
1904
|
+
}
|
|
1905
|
+
)
|
|
1906
|
+
}
|
|
1907
|
+
)
|
|
1908
|
+
] }) : /* @__PURE__ */ jsxs8(
|
|
1909
|
+
"div",
|
|
1910
|
+
{
|
|
1911
|
+
className: cn("fixed z-50", BADGE_POSITION_CLASSES[badgePosition]),
|
|
1912
|
+
children: [
|
|
1913
|
+
/* @__PURE__ */ jsx13(AnimatePresence3, { children: view === "menu" && /* @__PURE__ */ jsx13(
|
|
1914
|
+
VoiceboxMenu,
|
|
1915
|
+
{
|
|
1916
|
+
favoriteWorkflows,
|
|
1917
|
+
position: badgePosition,
|
|
1918
|
+
onPositionChange: handlePositionChange,
|
|
1919
|
+
onHide: handleHide,
|
|
1920
|
+
onAskMeridial: handleAskMeridial,
|
|
1921
|
+
onPlayGuide: handlePlayGuide
|
|
1922
|
+
}
|
|
1923
|
+
) }),
|
|
1924
|
+
/* @__PURE__ */ jsx13(VoiceboxBadge, { triggerIcon, onClick: handleBadgeClick })
|
|
1925
|
+
]
|
|
1926
|
+
}
|
|
1927
|
+
) });
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
export {
|
|
1931
|
+
Voicebox
|
|
1932
|
+
};
|
|
1933
|
+
//# sourceMappingURL=chunk-WCRZUGN4.js.map
|