UNPKG

@hoosei/voxweave-react

Version:

A customizable and interactive voice UI component for React applications

425 lines (424 loc) 16.9 kB
import { useRef as w, useState as x, useCallback as $, useEffect as Y } from "react"; import { Button as Se } from "./index6.js"; import { cn as m } from "./index8.js"; import { motion as P, AnimatePresence as at } from "framer-motion"; import { PrivacyDialog as st } from "./index9.js"; import { LanguageSelector as lt, languages as ct } from "./index10.js"; /* empty css */ import { VoxweaveMinimal as ut } from "./index11.js"; import { ErrorBoundary as xt } from "./index12.js"; import { ThreeWrapper as vt } from "./index13.js"; import { validateLicenseKey as dt } from "./index14.js"; import { ClientOnly as ft } from "./index15.js"; import { getConfig as mt } from "./index5.js"; import gt from "./index16.js"; import { jsx as n, jsxs as y, Fragment as ht } from "react/jsx-runtime"; import Le from "./index17.js"; import ke from "./index18.js"; import pt from "./index19.js"; function yt({ animation: l, validatedSize: D, isListening: i, audioData: b, isVertical: T, agentAudioData: g, finalListeningColors: c, finalNotListeningColors: C, agentStatus: S, darkMode: L, containerStyle: h }) { return l || console.warn("No animation specified for Voxweave component. Using BlobAnimation as default."), /* @__PURE__ */ n(vt, { size: D, animation: l || gt, isListening: i, audioData: b, isVertical: T, agentAudioData: g, listeningColors: c, notListeningColors: C, agentStatus: S, darkMode: L, containerPosition: h }); } const Ee = "voxweave-privacy-consent", Z = ({ message: l, isDev: D, onClose: i }) => /* @__PURE__ */ n("div", { className: "vox:fixed vox:bottom-4 vox:left-4 vox:z-50", children: /* @__PURE__ */ y("div", { className: "vox:flex vox:items-start vox:gap-2 vox:p-3 vox:rounded-lg vox:bg-red-50 vox:text-red-900 vox:shadow-lg vox:border vox:border-red-100 vox:max-w-[365px]", children: [/* @__PURE__ */ n(pt, { className: "vox:w-4 vox:h-4 vox:text-red-500 vox:mt-0.5" }), /* @__PURE__ */ y("div", { className: "vox:flex-1", children: [/* @__PURE__ */ n("p", { className: "vox:text-sm vox:font-medium", children: l }), D && /* @__PURE__ */ n("p", { className: "vox:text-xs vox:mt-1 vox:text-red-600", children: "Check the console for more details" })] }), i && /* @__PURE__ */ n("button", { onClick: i, className: "vox:text-red-500 hover:vox:text-red-700 vox:mt-0.5", children: /* @__PURE__ */ n(ke, { className: "vox:w-4 vox:h-4" }) })] }) }), bt = ["#4F46E5", "#818CF8"], wt = ["#6B7280", "#9CA3AF"], Mt = (l) => { const { className: D, isListening: i = !1, agentStatus: b = "idle", statusLabel: T, size: g = 250, useAsButton: c = !1, showControls: C = !0, listeningColors: S, notListeningColors: L, theme: h = "default", animation: J, darkMode: f = !1, hideCloseButton: De = !1, backgroundColor: V, disableBackground: M = !1, disableShadow: Ve = !1, isVisible: F = !1, isAnimated: Q = !0, disableFixed: r = !1, isExpanded: p = !1, isCompact: ee = !1, privacyMode: W = !1, privacyPolicyUrl: Be = "#", companyName: ze = "our service", languageMode: Re = !1, defaultLanguage: $e = "en", startCallLabel: q = "Call", endCallLabel: j = "End", onConsentAccept: Te, onLanguageChange: Fe, containerStyle: o = "center", position: t = "right", isVertical: A = !1, expandLabel: Ie = "Chat with AI" } = l, u = g !== void 0 && !(r === !0 && C === !1) && (g < 75 || g > 250) ? (console.warn(`Voxweave: When disableFixed is not true or showControls is true, size must be between 75 and 250. Current values: disableFixed=${r}, showControls=${C}, size=${g}. Defaulting to ${g < 75 ? 75 : 250}.`), g < 75 ? 75 : 250) : g || 180, a = w(null), N = w(null), B = w(null), z = w(null), s = w(null), I = w(null), [G, te] = x(!1), [oe, ne] = x(!0), [O, re] = x(null), [Oe, ie] = x(!1), [Ue, K] = x(!1), [ae, _e] = x(() => typeof window > "u" || !W ? !0 : localStorage.getItem(Ee) === "true"), [Pe, Me] = x($e), [H, se] = x(i), [le, ce] = x(null), [ue, X] = x(null), [, xe] = x(!1), We = w(null), [E, qe] = x(!1), ve = w(null), de = 250, fe = 82, je = () => u <= de ? "4rem" : `${4 * (de / u)}rem`, Ge = () => { qe(!E); }, me = $(async () => { if (!z.current) try { const e = await navigator.mediaDevices.getUserMedia({ audio: !0 }); if (z.current = e, a.current || (a.current = new (window.AudioContext || window.webkitAudioContext)()), !a.current) return; const v = a.current.createMediaStreamSource(e); N.current = a.current.createAnalyser(), N.current.fftSize = 256, v.connect(N.current), B.current = new Uint8Array(N.current.frequencyBinCount), ge(); } catch (e) { console.error("Error accessing microphone:", e), alert("Unable to access microphone. Please check your browser settings and try again."); } }, []), ge = $(() => { if (N.current && B.current) { const e = performance.now(), v = ve.current || 0; e - v > 33 && (N.current.getByteFrequencyData(B.current), ce(new Uint8Array(B.current)), ve.current = e); } I.current = requestAnimationFrame(ge); }, []), he = $(() => { if (!a.current) return; const e = a.current.createAnalyser(); e.fftSize = 256, s.current = a.current.createBufferSource(); const v = a.current.createBuffer(1, a.current.sampleRate * 3, a.current.sampleRate), d = v.getChannelData(0); for (let k = 0; k < d.length; k++) d[k] = Math.random() * 2 - 1; s.current.buffer = v, s.current.loop = !0, s.current.connect(e), s.current.start(); const R = new Uint8Array(e.frequencyBinCount); let Ae = 0; const Ne = () => { const k = performance.now(); k - Ae > 33 && (e.getByteFrequencyData(R), X(new Uint8Array(R)), Ae = k), requestAnimationFrame(Ne); }; Ne(); }, []), pe = $(() => { z.current && (z.current.getTracks().forEach((e) => e.stop()), z.current = null), a.current && (a.current.close(), a.current = null), s.current && (s.current.stop(), s.current.disconnect(), s.current = null), I.current && (cancelAnimationFrame(I.current), I.current = null), N.current = null, B.current = null, ce(null), X(null); }, []); Y(() => { let e = !0; async function v() { try { const d = mt(), R = await dt(d.licenseKey); e && (te(R), R || re("Voxweave widget is currently unavailable."), ne(!1)); } catch (d) { console.error("License validation failed:", d), e && (te(!1), re("Unable to initialize the widget."), ne(!1)); } } return v(), () => { e = !1; }; }, []), Y(() => { se(i), i ? me() : pe(); }, [i, me, pe]), Y(() => { b === "speaking" ? he() : s.current && (s.current.stop(), s.current.disconnect(), s.current = null, X(null)); }, [b, he]); const U = () => { xe(!1), setTimeout(() => { l.onClose(); }, 500); }, _ = () => { xe(!0), l.onStartCall(); }, ye = () => l.onButtonClick(), be = $(() => { if (W && !ae) { K(!0); return; } c ? l.onButtonClick?.() : H ? U?.() : _?.(), se(!H); }, [H, W, ae, c, _, U]), Ke = () => { K(!1), _e(!0), localStorage.setItem(Ee, "true"), Te?.(), _(); }, He = () => { K(!1); }, we = () => { "useAsButton" in l && l.useAsButton && ye ? ye() : be(); }, Xe = (e) => { Me(e), Fe?.(e); }, Ye = m("vox:flex vox:items-center vox:gap-2 vox:rounded-2xl", r ? "vox:relative" : "vox:fixed", r ? "" : "vox:p-2", D, { "vox:bg-white": !f && !V && !M, "vox:bg-gray-900": f && !V && !M, "vox:shadow-lg": !Ve, "vox:flex-row": o === "bottom", "vox:flex-col": o === "center" || A, "vox:bottom-4": !r && o === "bottom" && (t === "left" || t === "right"), "vox:left-14": !r && o === "bottom" && t === "left", "vox:right-14": !r && o === "bottom" && t === "right", "vox:left-3": !r && o !== "bottom" && t === "left", "vox:right-3": !r && o !== "bottom" && t === "right", "vox:top-[5%] vox:-translate-y-[10%]": !r && o !== "bottom" && (t === "left" || t === "right") }), Ze = m("vox:flex vox:items-center vox:transform-none", { "vox:flex-row": o === "bottom" && A, "vox:flex-col": o === "center", "vox:bottom-4": !r && o === "bottom" && (t === "left" || t === "right"), "vox:left-3": !r && t?.includes("left") || !r && t === "left", "vox:right-3": !r && t?.includes("right") || !r && t === "right", "vox:top-[38%] vox:-translate-y-[38%]": !r && o !== "bottom" && (t === "left" || t === "right") }), Je = { ...V && !M ? { backgroundColor: V } : {}, width: h === "minimal" ? "auto" : `${o === "bottom" ? `calc(${u}px * 1.6)` : `${u}px`}`, height: h === "minimal" || A ? void 0 : "auto" }, Qe = { width: o !== "center" && t === "right" || o === "center" && t === "right" ? void 0 : `calc(${u}px * 2 ${o === "bottom" && !p ? "+ 3rem" : ""})`, height: h === "minimal" ? void 0 : o === "center" ? `calc(${u}px * 1.15 + ${je()})` : `calc(${u}px * 2 + 2rem)`, minHeight: h === "minimal" ? void 0 : u }, et = { marginTop: o === "center" ? `${ee ? fe - 24 : fe}px` : "1rem", position: "relative", zIndex: 50 }, tt = m("vox:inline-flex vox:justify-center vox:grow vox:items-center vox:w-full vox:gap-1 vox:px-3! vox:py-2! vox:rounded-full! vox:w-full vox:text-xs vow-max-w-[320px] vox:transition vox:duration-300 vox:ease-in-out", { "vox:bg-black! vox:text-white! vox:border-b-4! vox:border-gray-700! vox:hover:bg-gray-700!": !f, "vox:bg-[#383838]! vox:border-b-4! vox:border-[#292929]! vox:hover:bg-[#292929]! vox:text-white!": f }), ot = (() => { const e = o === "center" && (t === "left" || t === "right"), v = t === "left" ? -1 : 1, d = { type: "spring", stiffness: 1300, damping: 120, duration: 1.5, mass: 1.2 }; return e ? { hidden: { x: `${100 * v}%`, opacity: 0, transition: d }, visible: { x: 0, opacity: 1, transition: d } } : { hidden: { y: "100%", opacity: 0, transition: d }, visible: { y: 0, opacity: 1, transition: d } }; })(), nt = Array.isArray(S) && S.length >= 2 ? [S[0], S[1]] : bt, rt = Array.isArray(L) && L.length >= 2 ? [L[0], L[1]] : wt, it = p ? E : F; if (oe) return /* @__PURE__ */ n("div", { className: "vox:sr-only", children: "Voxweave is loading..." }); if (!G || O) { const e = process.env.NODE_ENV === "development" || !1; return /* @__PURE__ */ n(Z, { message: O || "Widget unavailable", isDev: e }); } const Ce = process.env.NODE_ENV === "development" || !1; return /* @__PURE__ */ n(xt, { FallbackComponent: ({}) => /* @__PURE__ */ n(Z, { message: "Something went wrong", isDev: Ce, onClose: () => ie(!1) }), children: /* @__PURE__ */ y(ft, { children: [(!G || O) && Oe && /* @__PURE__ */ n(Z, { message: O || "Voxweave widget unavailable", isDev: Ce, onClose: () => ie(!1) }), /* @__PURE__ */ y(P.div, { animate: !Q, id: "voxweave-primary-container", className: m(Ze, r ? "" : "vox:fixed"), style: Qe, children: [/* @__PURE__ */ n(P.div, { className: m(r ? "vox:relative" : "vox:fixed vox:overflow-hidden", " vox:relative"), initial: { height: h === "minimal" ? void 0 : `calc(${u}px ${o === "bottom" ? "/ 1.6" : "* 2"})` }, animate: { height: u }, transition: { duration: 0.6, ease: [0.4, 0, 0.2, 1] }, children: it && /* @__PURE__ */ y(P.div, { ref: We, className: m(Ye, c && "vox:cursor-pointer"), style: Je, onClick: c ? we : void 0, role: c ? "button" : "region", "aria-label": c ? "Open Voxweave" : "Voxweave audio visualization", tabIndex: c ? 0 : void 0, onKeyDown: (e) => { c && (e.key === "Enter" || e.key === " ") && (e.preventDefault(), we()); }, initial: "hidden", animate: Q ? "visible" : !1, exit: "hidden", variants: ot, children: [Re && /* @__PURE__ */ n(lt, { value: Pe, onValueChange: Xe, languages: ct, className: "vox:mr-2", darkMode: f, backgroundColor: V }), h === "minimal" ? h === "minimal" && C && /* @__PURE__ */ n(ut, { isListening: i, onStartCall: _, onClose: U, audioData: le, agentAudioData: ue, agentStatus: b, darkMode: f, startCallLabel: q, endCallLabel: j }) : /* @__PURE__ */ y(ht, { children: [/* @__PURE__ */ n(yt, { animation: J, validatedSize: u, isListening: i, audioData: le, isVertical: A, agentAudioData: ue, finalListeningColors: nt, finalNotListeningColors: rt, agentStatus: b, darkMode: f, containerStyle: o }), C && !c && /* @__PURE__ */ y("div", { style: { width: p && !A ? u : "100%" }, className: m(A ? "vox:px-1.5! vox:pb-1!" : "", "vox:relative vox:flex vox:flex-col vox:items-center vox:justify-start vox:z-10 vox:flex vox:w-full", { "vox:gap-2": o === "bottom" || A, "vox:gap-1": o === "center" }), children: [!ee && /* @__PURE__ */ n("div", { className: m(f ? "vox:text-white/90" : "vox:text-black/90", "vox:relative vox:z-10 vox:px-2! vox:pb-1! vox:text-sm vox:font-light vox:text-left"), children: i && b === "speaking" ? "Talk to interrupt" : i && b === "listening" ? "Listening" : `${T || "Need help?"}` }), /* @__PURE__ */ y(Se, { className: tt, size: "sm", onClick: be, "aria-label": i ? j : q, disabled: !G, children: [/* @__PURE__ */ n(Le, { className: "vox:w-3 vox:h-3", "aria-hidden": "true" }), i ? j : q] })] })] })] }) }), /* @__PURE__ */ n(at, { children: (!F && p || F) && !oe && C && !De && !c && /* @__PURE__ */ n(P.div, { initial: { opacity: 0, x: t === "left" ? -20 : t === "right" ? 20 : 0, y: 20 }, animate: { opacity: 1, x: 0, y: 0 }, exit: { opacity: 0, x: t === "left" ? -20 : t === "right" ? 20 : 0, y: 20 }, transition: { duration: 0.3, ease: "easeInOut" }, style: et, className: m("vox:relative vox:z-50", { "vox:self-start": o !== "center" && t === "left" || o === "center", "vox:self-end": o !== "center" && (t === "left" || t === "right") }), children: /* @__PURE__ */ y(Se, { className: m("vox:p-3! vox:rounded-full! vox:transition vox:duration-300 vox:ease-in-out", { "vox:bg-gray-900/90! vox:hover:bg-gray-300/80! vox:text-gray-800!": !f, "vox:bg-gray-900/90! vox:hover:bg-gray-900/80! vox:text-white!": f, "vox:flex vox:items-center vox:gap-1": p && !E }), size: "icon", onClick: p ? Ge : U, "aria-label": p && E ? "Close Voxweave" : "Expand Voxweave", children: [p && E || F ? /* @__PURE__ */ n(ke, { className: "vox:w-3 vox:h-3", "aria-hidden": "true" }) : /* @__PURE__ */ n(Le, { className: "vox:w-3 vox:h-3", "aria-hidden": "true" }), " ", p && !E ? /* @__PURE__ */ n("span", { className: "vox:text-sm vox:font-light", children: Ie }) : null] }) }, "close-button") })] }), Ue && /* @__PURE__ */ n(st, { onAccept: Ke, onCancel: He, privacyPolicyUrl: Be, companyName: ze })] }) }); }; export { Mt as default }; //# sourceMappingURL=index4.js.map