@klever-one/web-sdk
Version:
Web SDK for integrating real-time room management and streaming functionality into web applications
674 lines (673 loc) • 21.9 kB
JavaScript
"use client";
import * as R from "react";
import O, { useRef as T, useState as j, useEffect as H, useCallback as g, useLayoutEffect as oe, useMemo as P } from "react";
import { K as se, _ as ie, a as ae } from "../index-DncH3OIr.js";
import '../react-lib.css';let W = 0;
function ce(n) {
const { apiKey: t, container: s, callbacks: i, conversation: a, ...c } = n, r = T(i);
r.current = i;
const e = T(null), [f, u] = j({
connection: "disconnected",
recording: "idle",
isStreaming: !1,
isStreamingReady: !1,
isConversationReady: !1,
lastUpdated: /* @__PURE__ */ new Date()
}), [h, p] = j({
reconnectAttempts: 0,
messagesSent: 0,
messagesReceived: 0,
recordingSessions: 0,
errorCount: 0
}), [m, S] = j({
messages: [],
isStreaming: !1,
isCompleted: !1
}), [L, b] = j([]);
H(() => {
try {
const l = new se({
apiKey: t,
container: s,
conversation: a,
...c,
callbacks: {
...r.current,
// 기존 콜백과 상태 업데이트를 결합
onConnectionStatusChange: (d) => {
u((k) => ({ ...k, connection: d })), r.current?.onConnectionStatusChange?.(d);
},
onRecordingStatusChange: (d) => {
u((k) => ({ ...k, recording: d })), r.current?.onRecordingStatusChange?.(d);
},
onStreamingStatusChange: (d) => {
u((k) => ({ ...k, isStreaming: d })), r.current?.onStreamingStatusChange?.(d);
},
onMessageReceived: (d) => {
b((k) => {
const B = k.findIndex((N) => N.id === d.id);
if (B >= 0) {
const N = [...k];
return N[B] = d, N;
} else
return [...k, d];
}), r.current?.onMessageReceived?.(d);
},
onReady: () => {
u((d) => ({
...d,
isStreamingReady: !0,
isConversationReady: !0
})), r.current?.onReady?.();
},
onDisconnect: () => {
u({
connection: "disconnected",
recording: "idle",
isStreaming: !1,
isStreamingReady: !1,
isConversationReady: !1,
lastUpdated: /* @__PURE__ */ new Date()
}), b([]), r.current?.onDisconnect?.();
},
onError: (d) => {
p((k) => ({
...k,
errorCount: k.errorCount + 1,
lastErrorTime: /* @__PURE__ */ new Date()
})), r.current?.onError?.(d);
},
onRoomInfoUpdated: (d) => {
r.current?.onRoomInfoUpdated?.(d);
},
onLog: (d) => {
r.current?.onLog?.(d);
}
}
});
e.current = l, W++, u(l.getState()), p(l.getMetrics()), S(l.getConversationState()), b(l.getMessages());
} catch {
}
return () => {
W--, W === 0 && e.current && e.current.getState().connection !== "disconnected" && e.current.disconnect();
};
}, [t, s]), H(() => {
if (!e.current) return;
const d = setInterval(() => {
e.current && e.current.getState().connection !== "disconnected" && (u(e.current.getState()), p(e.current.getMetrics()), S(e.current.getConversationState()), b(e.current.getMessages()));
}, 1e3);
return () => clearInterval(d);
}, []);
const y = g(async () => {
if (e.current)
return e.current.connect();
throw new Error("KleverOneClient not initialized");
}, []), A = g(() => {
e.current && e.current.disconnect();
}, []), M = g(async () => {
if (e.current)
return e.current.reconnect();
throw new Error("KleverOneClient not initialized");
}, []), v = g(async (l) => {
if (e.current)
return e.current.sendText(l);
throw new Error("KleverOneClient not initialized");
}, []), z = g(async () => {
if (e.current)
return e.current.startRecording();
throw new Error("KleverOneClient not initialized");
}, []), I = g(async () => {
if (e.current)
return e.current.stopRecording();
throw new Error("KleverOneClient not initialized");
}, []), w = g(() => e.current ? e.current.isReady() : !1, []), F = g(() => e.current ? e.current.isRecording() : !1, []), V = g((l, d) => {
e.current && e.current.resize(l, d);
}, []), X = g((l) => {
e.current && e.current.sendAvatarNum(l);
}, []), Z = g((l) => {
e.current && e.current.sendAvatarAppearanceChange(l);
}, []), Q = g((l) => {
e.current && e.current.sendPlaceBackgroundChange(l);
}, []), ee = g((l) => {
e.current && e.current.sendVoiceSetting(l);
}, []), ne = g(
(l) => {
e.current && e.current.sendMessage(l);
},
[]
), te = g(
async (l) => {
if (e.current)
return e.current.sendDataInChunks(l);
throw new Error("KleverOneClient not initialized");
},
[]
), re = g(
((l) => {
e.current && (Array.isArray(l), e.current.speak(l));
}),
[]
);
return {
client: e.current,
state: f,
metrics: h,
conversationState: m,
messages: L,
connect: y,
disconnect: A,
reconnect: M,
sendText: v,
speak: re,
startRecording: z,
stopRecording: I,
isReady: w,
isRecording: F,
// 고급 기능들
resize: V,
sendAvatarNum: X,
sendAvatarAppearanceChange: Z,
sendPlaceBackgroundChange: Q,
sendVoiceSetting: ee,
sendMessage: ne,
sendDataInChunks: te
};
}
var $ = { exports: {} }, E = {};
var D;
function le() {
if (D) return E;
D = 1;
var n = Symbol.for("react.transitional.element"), t = Symbol.for("react.fragment");
function s(i, a, c) {
var r = null;
if (c !== void 0 && (r = "" + c), a.key !== void 0 && (r = "" + a.key), "key" in a) {
c = {};
for (var e in a)
e !== "key" && (c[e] = a[e]);
} else c = a;
return a = c.ref, {
$$typeof: n,
type: i,
key: r,
ref: a !== void 0 ? a : null,
props: c
};
}
return E.Fragment = t, E.jsx = s, E.jsxs = s, E;
}
var U;
function de() {
return U || (U = 1, $.exports = /* @__PURE__ */ le()), $.exports;
}
var o = /* @__PURE__ */ de();
const C = ({
size: n = 24,
color: t = "currentColor",
stroke: s = 2,
className: i = "",
style: a,
children: c,
name: r
}) => /* @__PURE__ */ o.jsx(
"svg",
{
width: n,
height: n,
viewBox: "0 0 24 24",
fill: "none",
stroke: t,
strokeWidth: s,
strokeLinecap: "round",
strokeLinejoin: "round",
className: i,
style: a,
role: "img",
"aria-label": r,
children: c
}
), ue = (n) => /* @__PURE__ */ o.jsxs(
C,
{
name: "Send",
...n,
children: [
/* @__PURE__ */ o.jsx("path", { d: "M10 14l11 -11" }),
/* @__PURE__ */ o.jsx("path", { d: "M21 3l-6.5 18a.55 .55 0 0 1 -1 0l-3.5 -7l-7 -3.5a.55 .55 0 0 1 0 -1l18 -6.5" })
]
}
), fe = (n) => /* @__PURE__ */ o.jsxs(
C,
{
name: "Microphone",
...n,
children: [
/* @__PURE__ */ o.jsx("path", { d: "M9 2m0 3a3 3 0 0 1 3 -3h0a3 3 0 0 1 3 3v5a3 3 0 0 1 -3 3h0a3 3 0 0 1 -3 -3z" }),
/* @__PURE__ */ o.jsx("path", { d: "M5 10a7 7 0 0 0 14 0" }),
/* @__PURE__ */ o.jsx("path", { d: "M8 21l8 0" }),
/* @__PURE__ */ o.jsx("path", { d: "M12 17l0 4" })
]
}
), he = (n) => /* @__PURE__ */ o.jsx(
C,
{
name: "PlayerStop",
...n,
children: /* @__PURE__ */ o.jsx("path", { d: "M5 5m0 2a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2z" })
}
), ge = (n) => /* @__PURE__ */ o.jsxs(
C,
{
name: "PhoneCall",
...n,
children: [
/* @__PURE__ */ o.jsx("path", { d: "M5 4h4l2 5l-2.5 1.5a11 11 0 0 0 5 5l1.5 -2.5l5 2v4a2 2 0 0 1 -2 2a16 16 0 0 1 -15 -15a2 2 0 0 1 2 -2" }),
/* @__PURE__ */ o.jsx("path", { d: "M15 7a2 2 0 0 1 2 2" }),
/* @__PURE__ */ o.jsx("path", { d: "M15 3a6 6 0 0 1 6 6" })
]
}
), xe = (n) => /* @__PURE__ */ o.jsxs(
C,
{
name: "PhoneOff",
...n,
children: [
/* @__PURE__ */ o.jsx("path", { d: "M3 21l18 -18" }),
/* @__PURE__ */ o.jsx("path", { d: "M5.831 14.161a15.946 15.946 0 0 1 -2.831 -8.161a2 2 0 0 1 2 -2h4l2 5l-2.5 1.5c.108 .22 .223 .435 .345 .645m1.751 2.277c.843 .84 1.822 1.544 2.904 2.078l1.5 -2.5l5 2v4a2 2 0 0 1 -2 2a15.963 15.963 0 0 1 -10.344 -4.657" })
]
}
), ke = (n) => /* @__PURE__ */ o.jsxs(
C,
{
name: "Wifi",
...n,
children: [
/* @__PURE__ */ o.jsx("path", { d: "M12 18l.01 0" }),
/* @__PURE__ */ o.jsx("path", { d: "M9.172 15.172a4 4 0 0 1 5.656 0" }),
/* @__PURE__ */ o.jsx("path", { d: "M6.343 12.343a8 8 0 0 1 11.314 0" }),
/* @__PURE__ */ o.jsx("path", { d: "M3.515 9.515c4.686 -4.687 12.284 -4.687 17 0" })
]
}
), pe = (n) => /* @__PURE__ */ o.jsxs(
C,
{
name: "WifiOff",
...n,
children: [
/* @__PURE__ */ o.jsx("path", { d: "M12 18l.01 0" }),
/* @__PURE__ */ o.jsx("path", { d: "M9.172 15.172a4 4 0 0 1 5.656 0" }),
/* @__PURE__ */ o.jsx("path", { d: "M6.343 12.343a7.963 7.963 0 0 1 3.864 -2.14m4.163 .155a7.965 7.965 0 0 1 3.287 2" }),
/* @__PURE__ */ o.jsx("path", { d: "M3.515 9.515a12 12 0 0 1 3.544 -2.455m3.101 -.92a12 12 0 0 1 10.325 3.374" }),
/* @__PURE__ */ o.jsx("path", { d: "M3 3l18 18" })
]
}
), ve = ({ state: n }) => {
const t = n.connection === "connected", s = n.connection === "connecting", i = t ? "kos:text-green-400" : s ? "kos:text-yellow-400" : "kos:text-red-400", a = t ? "연결됨" : s ? "연결 중..." : "연결 안됨", c = t || s ? ke : pe;
return /* @__PURE__ */ o.jsxs(
"div",
{
className: `kos:flex kos:items-center kos:gap-2 kos:px-3 kos:py-1 kos:bg-black kos:bg-opacity-50 kos:rounded-full kos:text-white kos:text-xs ${i}`,
children: [
/* @__PURE__ */ o.jsx(
c,
{
className: `kos:w-4 kos:h-4 ${s ? "kos:animate-pulse" : ""}`
}
),
/* @__PURE__ */ o.jsx("span", { children: a }),
n.isStreaming && /* @__PURE__ */ o.jsx("span", { className: "kos:text-sm kos:text-blue-400", children: "응답 생성 중..." })
]
}
);
}, me = ({
state: n,
connect: t,
disconnect: s
}) => {
const i = n.connection === "connected", a = n.connection === "connecting", c = () => {
i || a ? s() : t();
};
return /* @__PURE__ */ o.jsx(
"button",
{
onClick: c,
disabled: a,
className: `kos:p-3 kos:rounded-full kos:transition-colors ${i ? "kos:bg-red-500 kos:hover:bg-red-600 kos:text-white kos:cursor-pointer" : a ? "kos:bg-yellow-500 kos:text-white kos:animate-pulse kos:cursor-not-allowed" : "kos:bg-green-500 kos:hover:bg-green-600 kos:text-white kos:cursor-pointer"}`,
children: i || a ? /* @__PURE__ */ o.jsx(xe, { size: 20 }) : /* @__PURE__ */ o.jsx(ge, { size: 20 })
}
);
};
var be = oe, Se = function(t) {
var s = O.useRef(t);
return be(function() {
s.current = t;
}), s;
}, q = function(t, s) {
if (typeof t == "function") {
t(s);
return;
}
t.current = s;
}, ye = function(t, s) {
var i = O.useRef();
return O.useCallback(function(a) {
t.current = a, i.current && q(i.current, null), i.current = s, s && q(s, a);
}, [s]);
}, K = {
"min-height": "0",
"max-height": "none",
height: "0",
visibility: "hidden",
overflow: "hidden",
position: "absolute",
"z-index": "-1000",
top: "0",
right: "0",
display: "block"
}, Re = function(t) {
Object.keys(K).forEach(function(s) {
t.style.setProperty(s, K[s], "important");
});
}, J = Re, x = null, Y = function(t, s) {
var i = t.scrollHeight;
return s.sizingStyle.boxSizing === "border-box" ? i + s.borderSize : i - s.paddingSize;
};
function je(n, t, s, i) {
s === void 0 && (s = 1), i === void 0 && (i = 1 / 0), x || (x = document.createElement("textarea"), x.setAttribute("tabindex", "-1"), x.setAttribute("aria-hidden", "true"), J(x)), x.parentNode === null && document.body.appendChild(x);
var a = n.paddingSize, c = n.borderSize, r = n.sizingStyle, e = r.boxSizing;
Object.keys(r).forEach(function(m) {
var S = m;
x.style[S] = r[S];
}), J(x), x.value = t;
var f = Y(x, n);
x.value = t, f = Y(x, n), x.value = "x";
var u = x.scrollHeight - a, h = u * s;
e === "border-box" && (h = h + a + c), f = Math.max(h, f);
var p = u * i;
return e === "border-box" && (p = p + a + c), f = Math.min(p, f), [f, u];
}
var G = function() {
}, Ce = function(t, s) {
return t.reduce(function(i, a) {
return i[a] = s[a], i;
}, {});
}, we = [
"borderBottomWidth",
"borderLeftWidth",
"borderRightWidth",
"borderTopWidth",
"boxSizing",
"fontFamily",
"fontSize",
"fontStyle",
"fontWeight",
"letterSpacing",
"lineHeight",
"paddingBottom",
"paddingLeft",
"paddingRight",
"paddingTop",
// non-standard
"tabSize",
"textIndent",
// non-standard
"textRendering",
"textTransform",
"width",
"wordBreak",
"wordSpacing",
"scrollbarGutter"
], Me = !!document.documentElement.currentStyle, ze = function(t) {
var s = window.getComputedStyle(t);
if (s === null)
return null;
var i = Ce(we, s), a = i.boxSizing;
if (a === "")
return null;
Me && a === "border-box" && (i.width = parseFloat(i.width) + parseFloat(i.borderRightWidth) + parseFloat(i.borderLeftWidth) + parseFloat(i.paddingRight) + parseFloat(i.paddingLeft) + "px");
var c = parseFloat(i.paddingBottom) + parseFloat(i.paddingTop), r = parseFloat(i.borderBottomWidth) + parseFloat(i.borderTopWidth);
return {
sizingStyle: i,
paddingSize: c,
borderSize: r
};
}, Ee = ze;
function _(n, t, s) {
var i = Se(s);
R.useLayoutEffect(function() {
var a = function(r) {
return i.current(r);
};
if (n)
return n.addEventListener(t, a), function() {
return n.removeEventListener(t, a);
};
}, []);
}
var Te = function(t, s) {
_(document.body, "reset", function(i) {
t.current.form === i.target && s(i);
});
}, Le = function(t) {
_(window, "resize", t);
}, Ie = function(t) {
_(document.fonts, "loadingdone", t);
}, Ne = ["cacheMeasurements", "maxRows", "minRows", "onChange", "onHeightChange"], He = function(t, s) {
var i = t.cacheMeasurements, a = t.maxRows, c = t.minRows, r = t.onChange, e = r === void 0 ? G : r, f = t.onHeightChange, u = f === void 0 ? G : f, h = ie(t, Ne), p = h.value !== void 0, m = R.useRef(null), S = ye(m, s), L = R.useRef(0), b = R.useRef(), y = function() {
var v = m.current, z = i && b.current ? b.current : Ee(v);
if (z) {
b.current = z;
var I = je(z, v.value || v.placeholder || "x", c, a), w = I[0], F = I[1];
L.current !== w && (L.current = w, v.style.setProperty("height", w + "px", "important"), u(w, {
rowHeight: F
}));
}
}, A = function(v) {
p || y(), e(v);
};
return R.useLayoutEffect(y), Te(m, function() {
if (!p) {
var M = m.current.value;
requestAnimationFrame(function() {
var v = m.current;
v && M !== v.value && y();
});
}
}), Le(y), Ie(y), /* @__PURE__ */ R.createElement("textarea", ae({}, h, {
onChange: A,
ref: S
}));
}, Ae = /* @__PURE__ */ R.forwardRef(He);
const Fe = ({
state: n,
isReady: t,
sendText: s,
startRecording: i,
stopRecording: a
}) => {
const c = T(null), [r, e] = j(""), f = async (h) => {
if (h.preventDefault(), !(!r.trim() || n.isStreaming))
try {
await s(r), e("");
} catch {
}
}, u = async () => {
try {
n.recording === "recording" ? await a() : await i();
} catch {
}
};
return /* @__PURE__ */ o.jsxs(
"form",
{
onSubmit: f,
className: "kos:flex-1 kos:flex kos:items-center kos:gap-4",
children: [
/* @__PURE__ */ o.jsx(
Ae,
{
ref: c,
className: "kos:flex-1 kos:bg-gray-700 kos:text-white kos:p-3 kos:rounded-lg kos:resize-none kos:outline-none focus:kos:ring-2 focus:kos:ring-blue-500 kos:transition-all",
placeholder: "메시지를 입력하세요...",
value: r,
onChange: (h) => e(h.target.value),
disabled: n.isStreaming,
maxRows: 5,
minRows: 1,
onKeyDown: (h) => {
h.key === "Enter" && !h.shiftKey && (h.preventDefault(), f(h));
}
}
),
/* @__PURE__ */ o.jsxs("div", { className: "kos:flex kos:items-center kos:gap-2", children: [
/* @__PURE__ */ o.jsx(
"button",
{
type: "button",
onClick: u,
disabled: n.isStreaming || !t(),
className: `kos:p-3 kos:rounded-full kos:transition-colors ${n.recording === "recording" ? "kos:bg-red-500 kos:text-white kos:animate-pulse kos:cursor-pointer" : "kos:bg-gray-700 kos:hover:bg-gray-600 kos:text-white kos:cursor-pointer"} kos:disabled:opacity-50 kos:disabled:cursor-not-allowed! kos:disabled:hover:bg-gray-700`,
children: n.recording === "recording" ? /* @__PURE__ */ o.jsx(he, { size: 20 }) : /* @__PURE__ */ o.jsx(fe, { size: 20 })
}
),
/* @__PURE__ */ o.jsx(
"button",
{
type: "submit",
disabled: !r.trim() || n.isStreaming || !t(),
className: "kos:p-3 kos:bg-blue-500 kos:text-white kos:rounded-full kos:cursor-pointer kos:hover:bg-blue-600 kos:disabled:opacity-50 kos:disabled:cursor-not-allowed! kos:disabled:hover:bg-blue-500 kos:transition-colors",
children: /* @__PURE__ */ o.jsx(ue, { size: 20 })
}
)
] })
]
}
);
}, We = ({ messages: n }) => {
const t = T(null), [s, i] = j(!1), a = P(() => {
const r = /* @__PURE__ */ new Map();
return n.forEach((e) => {
e.id ? r.set(e.id, e) : r.set(Symbol("unique-msg"), e);
}), Array.from(r.values());
}, [n]);
H(() => {
t.current && !s && (t.current.scrollTop = t.current.scrollHeight);
}, [a, s]);
const c = () => {
if (t.current) {
const { scrollTop: r, scrollHeight: e, clientHeight: f } = t.current, u = e - r - f < 10;
i(!u);
}
};
return /* @__PURE__ */ o.jsx(
"div",
{
ref: t,
onScroll: c,
className: "kos:flex-1 kos:overflow-y-auto kos:space-y-4 kos:pr-2 kos:scroll-smooth",
children: a.map((r, e) => /* @__PURE__ */ o.jsx(
"div",
{
className: `kos:flex kos:items-end kos:gap-2 ${r.role === "user" ? "kos:justify-end" : "kos:justify-start"}`,
children: /* @__PURE__ */ o.jsx(
"div",
{
className: `kos:px-4 kos:py-2 kos:rounded-2xl kos:max-w-md ${r.role === "user" ? "kos:bg-blue-600 kos:text-white kos:rounded-br-none" : "kos:bg-gray-700 kos:text-white kos:rounded-bl-none"}`,
children: /* @__PURE__ */ o.jsx("span", { className: "kos:text-sm", children: r.fullContent || r.content })
}
)
},
r.id || `msg-${e}`
))
}
);
}, $e = ({
apiKey: n,
width: t = 800,
className: s = ""
}) => {
const i = T(null), a = P(() => document.createElement("div"), []), c = P(
() => ({
onReady: () => /* @__PURE__ */ console.log("디지털 휴먼 준비 완료!"),
onMessageReceived: (u) => /* @__PURE__ */ console.log("받은 메시지:", u),
onError: (u) => /* @__PURE__ */ console.error("오류:", u)
}),
[]
), r = ce({
apiKey: n,
container: i.current || a,
callbacks: c
});
H(() => () => {
r.client && r.disconnect();
}, [r.client]);
const e = async () => {
if (i.current && r.client)
try {
await r.connect();
} catch {
}
}, f = () => {
r.client && r.disconnect();
};
return /* @__PURE__ */ o.jsxs("div", { className: `kos:relative kos:w-full kos:flex kos:gap-4 ${s}`, children: [
/* @__PURE__ */ o.jsxs("div", { className: "kos:relative kos:flex-1", children: [
/* @__PURE__ */ o.jsx(
"div",
{
ref: i,
className: "kos:w-full kos:h-96 kos:bg-black kos:rounded-lg",
style: { width: `${t}px`, height: "400px" }
}
),
/* @__PURE__ */ o.jsxs("div", { className: "kos:absolute kos:top-0 kos:left-0 kos:w-full kos:h-full kos:p-4 kos:flex kos:flex-col kos:justify-between kos:z-10 pointer-events-none", children: [
/* @__PURE__ */ o.jsxs("div", { className: "kos:flex kos:justify-between kos:items-start", children: [
/* @__PURE__ */ o.jsx(ve, { state: r.state }),
/* @__PURE__ */ o.jsx(
me,
{
state: r.state,
connect: e,
disconnect: f
}
)
] }),
/* @__PURE__ */ o.jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ o.jsx(
Fe,
{
state: r.state,
isReady: r.isReady,
sendText: r.sendText,
startRecording: r.startRecording,
stopRecording: r.stopRecording
}
) })
] })
] }),
/* @__PURE__ */ o.jsx("div", { className: "kos:w-80 kos:h-96 kos:bg-gray-100 kos:rounded-lg kos:p-4 kos:flex kos:flex-col", children: /* @__PURE__ */ o.jsx(We, { messages: r.messages }) })
] });
}, _e = ({
apiKey: n
}) => {
const [t, s] = j(!1);
return /* @__PURE__ */ o.jsx("div", { className: "kos:p-4", children: /* @__PURE__ */ o.jsx(
$e,
{
apiKey: n,
width: 800,
isFullscreen: t,
onToggleFullscreen: () => s(!t),
className: "kos:max-w-4xl kos:mx-auto"
}
) });
};
export {
ve as ConnectionStatusIndicator,
me as ConnectionToggleButton,
se as KleverOneClient,
Fe as MessageInput,
We as MessageList,
$e as UnifiedConversation,
_e as UnifiedConversationExample,
ce as useKleverOneClient
};