UNPKG

@codedevin/dify-chat

Version:

A beautiful and configurable chatbot widget for Dify integration with multiple display modes

1,414 lines 66.3 kB
var Re = Object.defineProperty; var je = (e, r, s) => r in e ? Re(e, r, { enumerable: !0, configurable: !0, writable: !0, value: s }) : e[r] = s; var fe = (e, r, s) => je(e, typeof r != "symbol" ? r + "" : r, s); import { jsx as t, jsxs as u, Fragment as xe } from "react/jsx-runtime"; import De, { useState as T, useRef as Y, useCallback as W, memo as ve, useEffect as q, useMemo as Be, isValidElement as $e, Suspense as He, forwardRef as qe, useImperativeHandle as Oe } from "react"; import { createPortal as Ue } from "react-dom"; import { clsx as V } from "clsx"; import { AnimatePresence as ne, motion as X } from "framer-motion"; import { Check as We, Clipboard as Ke, X as Ce, RotateCcw as ze, Paperclip as Ve, ArrowUp as Je, Minimize2 as Xe, Maximize2 as Ye, ArrowDown as Ge, MessageCircle as Te } from "lucide-react"; import { marked as Qe } from "marked"; import Pe from "react-markdown"; import Ie from "remark-gfm"; class Ze { constructor(r) { fe(this, "config"); fe(this, "conversationId", null); fe(this, "currentTaskId", null); this.config = r; } getHeaders() { return { Authorization: `Bearer ${this.config.apiKey}`, "Content-Type": "application/json" }; } setConversationId(r) { this.conversationId = r; } getConversationId() { return this.conversationId; } async sendMessage(r, s) { const o = { inputs: this.config.inputs || {}, query: r, user: this.config.userId || "anonymous", conversation_id: this.conversationId, response_mode: "blocking", files: (s == null ? void 0 : s.map((p) => ({ type: p.type, transfer_method: p.id ? "local_file" : "remote_url", upload_file_id: p.id, url: p.url }))) || [] }, n = await fetch(`${this.config.baseUrl}/chat-messages`, { method: "POST", headers: this.getHeaders(), body: JSON.stringify(o) }); if (!n.ok) { const p = await n.json().catch(() => ({ message: "Request failed" })); throw new Error(p.message || "Failed to send message"); } const l = await n.json(); return l.conversation_id && !this.conversationId && (this.conversationId = l.conversation_id), l; } async sendMessageStream(r, s, o) { var _; const n = { inputs: this.config.inputs || {}, query: r, user: this.config.userId || "anonymous", conversation_id: this.conversationId, response_mode: "streaming", files: (s == null ? void 0 : s.map((g) => ({ type: g.type, transfer_method: g.id ? "local_file" : "remote_url", upload_file_id: g.id, url: g.url }))) || [] }, l = await fetch(`${this.config.baseUrl}/chat-messages`, { method: "POST", headers: this.getHeaders(), body: JSON.stringify(n) }); if (!l.ok) { const g = await l.json().catch(() => ({ message: "Request failed" })); throw new Error(g.message || "Failed to send message"); } const p = (_ = l.body) == null ? void 0 : _.getReader(); if (!p) throw new Error("Failed to get response stream"); const y = new TextDecoder(); let F = ""; try { for (; ; ) { const { done: g, value: N } = await p.read(); if (g) break; F += y.decode(N, { stream: !0 }); const P = F.split(` `); F = P.pop() || ""; for (const I of P) if (I.trim() !== "") { if (I.startsWith("data: ")) { const M = I.slice(6).trim(); if (M === "[DONE]") break; try { const x = JSON.parse(M); x.conversation_id && !this.conversationId && (this.conversationId = x.conversation_id), x.task_id && (this.currentTaskId = x.task_id), o == null || o(x); } catch { } } else if (I.startsWith("event: ")) continue; } } } finally { p.releaseLock(); } } async uploadFile(r) { const s = new FormData(); s.append("file", r), s.append("user", this.config.userId || "anonymous"); const o = await fetch(`${this.config.baseUrl}/files/upload`, { method: "POST", headers: { Authorization: `Bearer ${this.config.apiKey}` }, body: s }); if (!o.ok) { const l = await o.json().catch(() => ({ message: "Upload failed" })); throw new Error(l.message || "Failed to upload file"); } return { id: (await o.json()).id, type: this.getFileType(r.type), name: r.name, size: r.size }; } getFileType(r) { return r.startsWith("image/") ? "image" : r.startsWith("audio/") ? "audio" : r.startsWith("video/") ? "video" : "document"; } async stopMessage() { if (!this.currentTaskId) throw new Error("No active task to stop"); const r = { user: this.config.userId || "anonymous" }, s = await fetch( `${this.config.baseUrl}/chat-messages/${this.currentTaskId}/stop`, { method: "POST", headers: this.getHeaders(), body: JSON.stringify(r) } ); if (!s.ok) { const o = await s.json().catch(() => ({ message: "Failed to stop message" })); throw new Error(o.message || "Failed to stop message"); } this.currentTaskId = null; } getCurrentTaskId() { return this.currentTaskId; } resetConversation() { this.conversationId = null, this.currentTaskId = null; } } const et = ({ config: e, onMessage: r, onError: s, enableStreaming: o = !0 }) => { const [n, l] = T([]), [p, y] = T(!1), [F, _] = T(null), [g, N] = T(!1), P = Y(new Ze(e)), I = () => Math.random().toString(36).substring(2, 15), M = (h) => { try { const f = h.match( /'error':\s*\{\s*'message':\s*"([^"]+)"/ ); if (f && f[1]) return f[1]; const v = h.match(/"message":\s*"([^"]+)"/); return v && v[1] ? v[1] : h; } catch { return h; } }, x = W( (h) => { l((f) => [...f, h]), r == null || r(h); }, [r] ), S = W( (h) => { l((f) => { const v = [...f], c = v[v.length - 1]; if (c && c.role === "assistant") { const m = { ...c, content: h, timestamp: Date.now() // Update timestamp to ensure change detection }; v[v.length - 1] = m, r == null || r(m); } return v; }); }, [r] ), R = W( async (h, f) => { if (!h.trim() && !(f != null && f.length)) return; _(null), y(!0); const v = { id: I(), role: "user", content: h.trim(), timestamp: Date.now(), attachments: f }; x(v); try { if (o) { let c = "", m = !1; await P.current.sendMessageStream( h, f, (i) => { var w; switch (i.event) { case "message": if (i.answer) { if (!m) { const O = { id: I(), role: "assistant", content: "", timestamp: Date.now() }; x(O), m = !0; } c += i.answer, S(c); } break; case "agent_message": if (i.answer) { if (!m) { const O = { id: I(), role: "assistant", content: "", timestamp: Date.now() }; x(O), m = !0; } c += i.answer, S(c); } break; case "message_end": (w = i.metadata) != null && w.usage; break; case "workflow_started": break; case "workflow_finished": break; case "node_started": break; case "node_finished": break; case "error": y(!1); const b = M( i.message || "Stream error occurred" ), C = new Error(b); throw _(C), s == null || s(C), C; default: break; } } ), y(!1); } else { const c = await P.current.sendMessage( h, f ), m = { id: I(), role: "assistant", content: c.answer, timestamp: Date.now() }; x(m), y(!1); } } catch (c) { const m = c instanceof Error ? c : new Error("Unknown error"); _(m), s == null || s(m), y(!1), l((i) => { const w = i[i.length - 1]; return w && w.role === "assistant" && !w.content.trim() ? i.slice(0, -1) : i; }); } }, [o, x, S, s] ), j = W( async (h) => { try { return await P.current.uploadFile(h); } catch (f) { const v = f instanceof Error ? f : new Error("Upload failed"); throw _(v), s == null || s(v), v; } }, [s] ), z = W(() => { l([]), _(null); }, []), E = W(() => { P.current.resetConversation(), z(); }, [z]), B = W(async () => { if (!(!p || g)) { N(!0); try { await P.current.stopMessage(), y(!1); } catch (h) { const f = h instanceof Error ? h : new Error("Failed to stop message"); _(f), s == null || s(f); } finally { N(!1); } } }, [p, g, s]); return { messages: n, isLoading: p, error: F, isStopping: g, sendMessage: R, stopMessage: B, uploadFile: j, clearMessages: z, resetConversation: E }; }, we = ({ size: e = 16, className: r = "" }) => /* @__PURE__ */ t( "svg", { width: e, height: e, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: r, children: /* @__PURE__ */ t( "path", { d: "M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 00-2.456 2.456zM16.894 20.567L16.5 22.5l-.394-1.933a2.25 2.25 0 00-1.423-1.423L12.75 18.75l1.933-.394a2.25 2.25 0 001.423-1.423L16.5 15l.394 1.933a2.25 2.25 0 001.423 1.423l1.933.394-1.933.394a2.25 2.25 0 00-1.423 1.423z", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" } ) } ); function Z(...e) { return V(e); } const Se = ({ className: e }) => /* @__PURE__ */ u( "svg", { className: e, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ t("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }), /* @__PURE__ */ t("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" }) ] } ), tt = ({ className: e }) => /* @__PURE__ */ t( "svg", { className: e, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ t("path", { d: "M20 6 9 17l-5-5" }) } ), ue = (e) => typeof e == "string" ? e : Array.isArray(e) ? e.map(ue).join("") : $e(e) ? ue(e.props.children) : "", Fe = ve( ({ children: e, className: r, language: s, ...o }) => { const [n, l] = T(null), [p, y] = T(!1), F = ue(e), _ = async () => { try { await navigator.clipboard.writeText(F), y(!0), setTimeout(() => y(!1), 2e3); } catch (N) { console.error("Failed to copy code:", N); } }; q(() => { (async () => { try { const { codeToTokens: P, bundledLanguages: I } = await import("shiki"), M = ue(e); if (!(s in I)) { l( /* @__PURE__ */ t( "pre", { ...o, className: Z( "my-0 overflow-x-auto w-full rounded-b-xl bg-[#f9f9f9] text-zinc-900 dark:bg-zinc-950 dark:text-zinc-50 border-0 p-4", r ), children: /* @__PURE__ */ t("code", { className: "whitespace-pre-wrap", children: e }) } ) ); return; } const { tokens: x } = await P(M, { lang: s, themes: { light: "github-light", dark: "github-dark" } }); l( /* @__PURE__ */ t( "pre", { ...o, className: Z( "my-0 overflow-x-auto w-full rounded-b-xl bg-[#f9f9f9] text-zinc-900 dark:bg-zinc-950 dark:text-zinc-50 border-0 p-4", r ), children: /* @__PURE__ */ t("code", { className: "whitespace-pre-wrap", children: x.map((S, R) => /* @__PURE__ */ u( "span", { children: [ S.map((j, z) => { const E = typeof j.htmlStyle == "string" ? void 0 : j.htmlStyle; return /* @__PURE__ */ t( "span", { style: E, children: j.content }, `token-${// biome-ignore lint/suspicious/noArrayIndexKey: Needed for react key z}` ); }), R !== x.length - 1 && ` ` ] }, `line-${// biome-ignore lint/suspicious/noArrayIndexKey: Needed for react key R}` )) }) } ) ); } catch (P) { console.error("Failed to highlight code:", P), l( /* @__PURE__ */ t( "pre", { ...o, className: Z( "my-0 overflow-x-auto w-full rounded-b-xl bg-[#f9f9f9] text-zinc-900 dark:bg-zinc-950 dark:text-zinc-50 border-0 p-4", r ), children: /* @__PURE__ */ t("code", { className: "whitespace-pre-wrap", children: e }) } ) ); } })(); }, [e, s]); const g = /* @__PURE__ */ t( "pre", { ...o, className: Z( "my-0 overflow-x-auto w-full rounded-b-xl bg-[#f9f9f9] text-zinc-900 dark:bg-zinc-950 dark:text-zinc-50 border-0 p-4", r ), children: /* @__PURE__ */ t("code", { className: "whitespace-pre-wrap", children: e }) } ); return /* @__PURE__ */ u("div", { className: "relative group my-4 w-full", children: [ /* @__PURE__ */ u("div", { className: "flex items-center justify-between px-4 py-2 bg-[#f9f9f9] text-zinc-900 dark:bg-zinc-950 dark:text-zinc-50 rounded-t-xl w-full", children: [ /* @__PURE__ */ t("span", { className: "text-sm text-zinc-600 dark:text-zinc-300 font-medium", children: s }), /* @__PURE__ */ t( "button", { onClick: _, className: "flex items-center gap-2 px-2 py-1 text-xs text-zinc-500 hover:text-zinc-700 hover:bg-zinc-200 dark:text-zinc-400 dark:hover:text-zinc-200 dark:hover:bg-zinc-800 rounded transition-colors", title: p ? "Copied!" : "Copy code", children: p ? /* @__PURE__ */ u(xe, { children: [ /* @__PURE__ */ t(tt, { className: "w-3 h-3" }), "Copied" ] }) : /* @__PURE__ */ u(xe, { children: [ /* @__PURE__ */ t(Se, { className: "w-3 h-3" }), "Copy" ] }) } ) ] }), n || g ] }); } ); Fe.displayName = "HighlightedPre"; const _e = ({ children: e, language: r, className: s, ...o }) => /* @__PURE__ */ t( He, { fallback: /* @__PURE__ */ u("div", { className: "relative group my-4 w-full", children: [ /* @__PURE__ */ u("div", { className: "flex items-center justify-between px-4 py-2 bg-[#f9f9f9] text-zinc-900 dark:bg-zinc-950 dark:text-zinc-50 rounded-t-xl w-full", children: [ /* @__PURE__ */ t("span", { className: "text-sm text-zinc-600 dark:text-zinc-300 font-medium", children: r }), /* @__PURE__ */ u( "button", { className: "flex items-center gap-2 px-2 py-1 text-xs text-zinc-500 dark:text-zinc-400 rounded", disabled: !0, children: [ /* @__PURE__ */ t(Se, { className: "w-3 h-3" }), "Copy" ] } ) ] }), /* @__PURE__ */ t( "pre", { ...o, className: Z( "my-0 overflow-x-auto w-full rounded-b-xl bg-[#f9f9f9] text-zinc-900 dark:bg-zinc-950 dark:text-zinc-50 border-0 p-4", s ), children: /* @__PURE__ */ t("code", { className: "whitespace-pre-wrap", children: e }) } ) ] }), children: /* @__PURE__ */ t(Fe, { language: r, className: s, ...o, children: e }) } ); _e.displayName = "CodeBlock"; const Me = { h1: ({ children: e, ...r }) => /* @__PURE__ */ t("h1", { className: "mt-2 scroll-m-20 text-4xl font-bold", ...r, children: e }), h2: ({ children: e, ...r }) => /* @__PURE__ */ t( "h2", { className: "mt-8 scroll-m-20 border-b pb-2 text-2xl font-semibold tracking-tight first:mt-0", ...r, children: e } ), h3: ({ children: e, ...r }) => /* @__PURE__ */ t( "h3", { className: "mt-4 scroll-m-20 text-xl font-semibold tracking-tight", ...r, children: e } ), h4: ({ children: e, ...r }) => /* @__PURE__ */ t( "h4", { className: "mt-4 scroll-m-20 text-lg font-semibold tracking-tight", ...r, children: e } ), h5: ({ children: e, ...r }) => /* @__PURE__ */ t( "h5", { className: "mt-4 scroll-m-20 text-lg font-semibold tracking-tight", ...r, children: e } ), h6: ({ children: e, ...r }) => /* @__PURE__ */ t( "h6", { className: "mt-4 scroll-m-20 text-base font-semibold tracking-tight", ...r, children: e } ), p: ({ children: e, ...r }) => /* @__PURE__ */ t("p", { className: "leading-6 [&:not(:first-child)]:mt-4", ...r, children: e }), strong: ({ children: e, ...r }) => /* @__PURE__ */ t("span", { className: "font-semibold", ...r, children: e }), a: ({ children: e, ...r }) => /* @__PURE__ */ t( "a", { className: "font-medium underline underline-offset-4", target: "_blank", rel: "noreferrer", ...r, children: e } ), ol: ({ children: e, ...r }) => /* @__PURE__ */ t("ol", { className: "my-4 ml-6 list-decimal", ...r, children: e }), ul: ({ children: e, ...r }) => /* @__PURE__ */ t("ul", { className: "my-4 ml-6 list-disc", ...r, children: e }), li: ({ children: e, ...r }) => /* @__PURE__ */ t("li", { className: "mt-2", ...r, children: e }), blockquote: ({ children: e, ...r }) => /* @__PURE__ */ t("blockquote", { className: "mt-4 border-l-2 pl-6 italic", ...r, children: e }), hr: (e) => /* @__PURE__ */ t("hr", { className: "my-4 md:my-8", ...e }), table: ({ children: e, ...r }) => /* @__PURE__ */ t("div", { className: "my-6 w-full overflow-y-auto", children: /* @__PURE__ */ t( "table", { className: "relative w-full overflow-hidden border-none text-sm", ...r, children: e } ) }), tr: ({ children: e, ...r }) => /* @__PURE__ */ t("tr", { className: "last:border-b-none m-0 border-b", ...r, children: e }), th: ({ children: e, ...r }) => /* @__PURE__ */ t( "th", { className: "px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right", ...r, children: e } ), td: ({ children: e, ...r }) => /* @__PURE__ */ t( "td", { className: "px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right", ...r, children: e } ), img: ({ alt: e, ...r }) => ( // biome-ignore lint/a11y/useAltText: alt is not required /* @__PURE__ */ t("img", { className: "rounded-md", alt: e, ...r }) ), code: ({ children: e, node: r, className: s, ...o }) => { const n = /language-(\w+)/.exec(s || ""); return n ? /* @__PURE__ */ t(_e, { language: n[1], className: s, ...o, children: e }) : /* @__PURE__ */ t( "code", { className: Z( "rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm", s ), ...o, children: e } ); }, pre: ({ children: e }) => /* @__PURE__ */ t(xe, { children: e }) }; function rt(e) { return e ? Qe.lexer(e).map((s) => s.raw) : []; } const Ae = ve( ({ content: e, className: r }) => /* @__PURE__ */ t("div", { className: r, children: /* @__PURE__ */ t(Pe, { remarkPlugins: [Ie], components: Me, children: e }) }), (e, r) => e.content === r.content ); Ae.displayName = "MemoizedMarkdownBlock"; const st = ve( ({ content: e, id: r, className: s }) => Be( () => rt(e || ""), [e] ).map((n, l) => /* @__PURE__ */ t( Ae, { content: n, className: s }, `${r}-block_${// biome-ignore lint/suspicious/noArrayIndexKey: <explanation> l}` )) ); st.displayName = "MarkdownContent"; const ot = ({ children: e }) => /* @__PURE__ */ t("div", { className: "markdown-content", children: /* @__PURE__ */ t(Pe, { remarkPlugins: [Ie], components: Me, children: e }) }), nt = ({ message: e, showAvatar: r = !0, assistantAvatar: s, userAvatar: o }) => { const [n, l] = T(!1), p = async () => { try { await navigator.clipboard.writeText(e.content), l(!0), setTimeout(() => l(!1), 2e3); } catch { } }; return /* @__PURE__ */ t(ne, { children: /* @__PURE__ */ t( X.div, { "data-testid": `message-${e.role}`, className: "w-full mx-auto max-w-3xl px-4 group/message", initial: { y: 5, opacity: 0 }, animate: { y: 0, opacity: 1 }, "data-role": e.role, children: /* @__PURE__ */ u( "div", { className: V( "flex gap-4 w-full group-data-[role=user]/message:ml-auto group-data-[role=user]/message:max-w-2xl", { "group-data-[role=user]/message:w-fit": !0 } ), children: [ e.role === "assistant" && r && /* @__PURE__ */ t("div", { className: "size-8 flex items-center rounded-full justify-center ring-1 shrink-0 ring-border bg-background", children: /* @__PURE__ */ t("div", { className: "translate-y-px", children: s ? /* @__PURE__ */ t( "img", { src: s, alt: "Assistant", className: "h-4 w-4 rounded-full object-cover" } ) : /* @__PURE__ */ t(we, { size: 14, className: "text-muted-foreground" }) }) }), /* @__PURE__ */ u("div", { className: "flex flex-col gap-4 w-full", children: [ e.attachments && e.attachments.length > 0 && /* @__PURE__ */ t( "div", { "data-testid": "message-attachments", className: "flex flex-row justify-end gap-2", children: e.attachments.map((y, F) => /* @__PURE__ */ u( "div", { className: "flex items-center gap-2 text-xs bg-muted rounded-lg px-3 py-2", children: [ /* @__PURE__ */ t("span", { children: "📎" }), /* @__PURE__ */ t("span", { className: "truncate max-w-[120px]", children: y.name }), /* @__PURE__ */ u("span", { className: "text-xs opacity-60", children: [ "(", Math.round(y.size / 1024), "KB)" ] }) ] }, F )) } ), /* @__PURE__ */ t("div", { className: "flex flex-row gap-2 items-start", children: /* @__PURE__ */ t( "div", { "data-testid": "message-content", className: V("flex flex-col gap-4 message-text", { "bg-muted px-3 py-2 rounded-xl": e.role === "user", "": e.role === "assistant" }), children: /* @__PURE__ */ t(ot, { children: e.content }) } ) }), e.role === "assistant" && /* @__PURE__ */ t("div", { className: "flex flex-row gap-2", children: /* @__PURE__ */ t( "button", { className: "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border border-input bg-background hover:bg-accent hover:text-accent-foreground py-1 px-2 h-fit text-muted-foreground", onClick: p, title: n ? "Copied!" : "Copy message", "data-state": "closed", children: n ? /* @__PURE__ */ t(We, { className: "h-4 w-4 " }) : /* @__PURE__ */ t(Ke, { className: "h-4 w-4" }) } ) }) ] }) ] } ) } ) }); }, at = ({ showAvatar: e = !0, assistantAvatar: r }) => /* @__PURE__ */ t( X.div, { "data-testid": "message-assistant-loading", className: "w-full mx-auto max-w-3xl px-4 group/message", initial: { y: 5, opacity: 0 }, animate: { y: 0, opacity: 1, transition: { delay: 0.5 } }, "data-role": "assistant", children: /* @__PURE__ */ u("div", { className: "flex gap-4 w-full", children: [ e && /* @__PURE__ */ t("div", { className: "size-8 flex items-center rounded-full justify-center ring-1 shrink-0 ring-border bg-background", children: /* @__PURE__ */ t("div", { className: "translate-y-px", children: r ? /* @__PURE__ */ t( "img", { src: r, alt: "Assistant", className: "h-4 w-4 rounded-full object-cover" } ) : /* @__PURE__ */ t(we, { size: 14, className: " text-muted-foreground" }) }) }), /* @__PURE__ */ t("div", { className: "flex flex-col gap-4 w-full mt-[0.7rem]", children: /* @__PURE__ */ t("div", { className: "flex flex-row gap-2 items-center", children: /* @__PURE__ */ u("div", { className: "flex space-x-1 items-center", children: [ /* @__PURE__ */ t("div", { className: "w-2 h-2 bg-muted-foreground rounded-full animate-pulse" }), /* @__PURE__ */ t( "div", { className: "w-2 h-2 bg-muted-foreground rounded-full animate-pulse", style: { animationDelay: "0.2s" } } ), /* @__PURE__ */ t( "div", { className: "w-2 h-2 bg-muted-foreground rounded-full animate-pulse", style: { animationDelay: "0.4s" } } ) ] }) }) }) ] }) } ), it = ({ size: e = 14 }) => /* @__PURE__ */ t( "svg", { width: e, height: e, viewBox: "0 0 24 24", fill: "currentColor", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ t("rect", { x: "6", y: "6", width: "12", height: "12", rx: "1", ry: "1" }) } ), lt = ({ onSendMessage: e, onUploadFile: r, placeholder: s = "Type your message...", disabled: o = !1, allowFileUpload: n = !1, allowedFileTypes: l, maxFileSize: p = 15 * 1024 * 1024, // 15MB status: y = "ready", onStop: F, autoFocus: _ = !1, initialMessage: g = "", showResetButton: N = !1, onReset: P }) => { const I = n ? l || ["image/png", "image/jpeg"] : [], [M, x] = T(g), [S, R] = T([]), [j, z] = T(!1), E = Y(null), B = Y(null), h = W(async () => { !M.trim() && S.length === 0 || o || y !== "ready" || (e(M.trim(), S), x(""), R([]), B.current && (B.current.style.height = "auto")); }, [M, S, o, y, e]), f = (i) => { i.key === "Enter" && !i.shiftKey && !i.nativeEvent.isComposing && (i.preventDefault(), y !== "ready" || h()); }, v = async (i) => { const w = Array.from(i.target.files || []); if (!(!w.length || !r)) { z(!0); try { for (const b of w) { if (b.size > p) throw new Error( `File ${b.name} is too large. Maximum size is ${Math.round( p / 1024 / 1024 )}MB` ); if (!I.some((d) => d.startsWith(".") ? b.name.toLowerCase().endsWith(d.toLowerCase()) : b.type.match(d.replace("*", ".*")))) throw new Error(`File type ${b.type} is not allowed`); const O = await r(b); R((d) => [...d, O]); } } catch { } finally { z(!1), E.current && (E.current.value = ""); } } }, c = (i) => { R((w) => w.filter((b, C) => C !== i)); }, m = (i) => { x(i.target.value); const w = i.target; w.style.height = "auto", w.style.height = Math.min(w.scrollHeight, 120) + "px"; }; return /* @__PURE__ */ u("div", { className: "relative w-full flex flex-col gap-4", children: [ /* @__PURE__ */ t( "input", { type: "file", className: "fixed -top-4 -left-4 size-0.5 opacity-0 pointer-events-none", ref: E, multiple: !0, accept: I.join(","), onChange: v, tabIndex: -1 } ), S.length > 0 && /* @__PURE__ */ t("div", { className: "flex flex-wrap gap-2 px-4", children: S.map((i, w) => /* @__PURE__ */ u( "div", { className: "flex items-center gap-2 bg-muted rounded-lg px-3 py-2 text-sm", children: [ /* @__PURE__ */ t("span", { children: "📎" }), /* @__PURE__ */ t("span", { className: "truncate max-w-[120px]", children: i.name }), /* @__PURE__ */ t( "button", { onClick: () => c(w), className: "text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ t(Ce, { className: "h-3 w-3" }) } ) ] }, w )) }), /* @__PURE__ */ u("div", { className: "relative", children: [ /* @__PURE__ */ t( "textarea", { "data-testid": "multimodal-input", ref: B, placeholder: s, value: M, onChange: m, onKeyDown: f, className: V( "flex w-full border border-input px-3 py-2 ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 min-h-[24px] max-h-[calc(75dvh)] overflow-hidden resize-none rounded-2xl bg-muted pb-10 text-sm text-foreground input-text overflow-y-auto" ), rows: 2, autoFocus: _, disabled: o } ), /* @__PURE__ */ u("div", { className: "absolute bottom-0 p-2 w-fit flex flex-row justify-start gap-1", children: [ N && P && /* @__PURE__ */ t( "button", { "data-testid": "reset-button", className: "inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground py-1 px-2 h-fit text-muted-foreground rounded-md button-text", disabled: y !== "ready" || o, onClick: P, title: "Reset conversation", "data-state": "closed", children: /* @__PURE__ */ t(ze, { size: 14 }) } ), n && r && /* @__PURE__ */ t( "button", { "data-testid": "attachments-button", className: "inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground py-1 px-2 h-fit text-muted-foreground rounded-md rounded-bl-lg button-text", disabled: y !== "ready" || o, onClick: () => { var i; return (i = E.current) == null ? void 0 : i.click(); }, title: "Attach file", "data-state": "closed", children: /* @__PURE__ */ t(Ve, { size: 14, className: "-rotate-45" }) } ) ] }), /* @__PURE__ */ t("div", { className: "absolute bottom-0 right-0 p-2 w-fit flex flex-row justify-end", children: y === "submitted" || y === "streaming" ? /* @__PURE__ */ t( "button", { "data-testid": "stop-button", className: "inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 bg-primary text-primary-foreground hover:bg-primary/90 rounded-full p-1.5 h-fit border dark:border-zinc-600 button-text", onClick: F, title: "Stop generation", "data-state": "closed", children: /* @__PURE__ */ t(it, { size: 14 }) } ) : /* @__PURE__ */ t( "button", { "data-testid": "send-button", className: "inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-primary text-primary-foreground hover:bg-primary/90 rounded-full p-1.5 h-fit border dark:border-zinc-600 button-text", onClick: h, disabled: o || !M.trim() && S.length === 0 || j, title: "Send message", "data-state": "closed", children: /* @__PURE__ */ t(Je, { size: 14 }) } ) }) ] }), j && /* @__PURE__ */ t("div", { className: "px-4 text-sm text-muted-foreground", children: "Uploading files..." }) ] }); }, ke = qe( ({ config: e, theme: r, displayMode: s = "embedded", placeholder: o = "Type your message...", title: n = "Chat Assistant", subtitle: l, avatar: p, className: y, style: F, onMessage: _, onError: g, onFullscreenChange: N, maxHeight: P = 500, maxWidth: I = 400, showHeader: M = !0, showAvatar: x = !0, allowFileUpload: S = !1, allowedFileTypes: R, maxFileSize: j = 15 * 1024 * 1024, autoFocus: z = !1, disabled: E = !1, initialMessage: B = "" }, h) => { var Ne; const f = Y(null), v = Y(null), c = Y(null), m = S ? R || ["image/png", "image/jpeg"] : [], [i, w] = T(!1), [b, C] = T(!1), O = () => C((a) => !a); q(() => { N == null || N(b); }, [b, N]); const { messages: d, isLoading: $, error: K, sendMessage: pe, stopMessage: ae, uploadFile: ie, resetConversation: Q } = et({ config: e, onMessage: _, onError: g, enableStreaming: !0 }), [G, ee] = T(0), [te, le] = T(!0); q(() => { const a = c.current; if (!a || !te) return; const L = d.length > G; ($ || L) && requestAnimationFrame(() => { a.scrollTop = a.scrollHeight; }), ee(d.length); }, [d, $, te]), q(() => { const a = c.current; if (!a) return; const L = () => { const { scrollTop: se, scrollHeight: oe, clientHeight: ye } = a, be = oe - se - ye < 50; w(!be && d.length > 0); }; return a.addEventListener("scroll", L, { passive: !0 }), () => a.removeEventListener("scroll", L); }, [d.length]); const ce = () => { const a = c.current; if (!a) return { scrollTop: 0, scrollHeight: 0, clientHeight: 0, isAtTop: !0, isAtBottom: !0, isNearBottom: !0 }; const { scrollTop: L, scrollHeight: se, clientHeight: oe } = a, ye = L === 0, be = L + oe >= se, Ee = se - L - oe < 50; return { scrollTop: L, scrollHeight: se, clientHeight: oe, isAtTop: ye, isAtBottom: be, isNearBottom: Ee }; }, de = () => { requestAnimationFrame(() => { const a = c.current; a && (a.scrollTop = 0); }); }, re = () => { requestAnimationFrame(() => { const a = c.current; a && (a.scrollTop = a.scrollHeight); }); }, he = (a) => { requestAnimationFrame(() => { const L = c.current; L && (L.scrollTop = a); }); }, k = (a) => { requestAnimationFrame(() => { const L = c.current; L && (L.scrollTop += a); }); }, A = () => { le(!0); }, H = () => { le(!1); }, U = () => te; Oe(h, () => ({ getScrollInfo: ce, scrollToTop: de, scrollToBottom: re, scrollTo: he, scrollBy: k, enableAutoScroll: A, disableAutoScroll: H, isAutoScrollEnabled: U })); const D = (r == null ? void 0 : r.background) && (r.background.includes("9%") || r.background.includes("4.9%") || r.background.includes("3.9%")); q(() => { if (v.current) { const a = v.current; D ? a.classList.add("dark") : a.classList.remove("dark"), r && (r.primary && a.style.setProperty("--primary", r.primary), r.secondary && a.style.setProperty("--secondary", r.secondary), r.background && a.style.setProperty("--background", r.background), r.text && a.style.setProperty("--foreground", r.text), r.border && a.style.setProperty("--border", r.border), r.borderRadius && a.style.setProperty("--radius", r.borderRadius), r.fontFamily && (a.style.fontFamily = r.fontFamily)); } }, [r, D]), q(() => { const a = (L) => { L.key === "Escape" && b && C(!1); }; return document.addEventListener("keydown", a), () => { document.removeEventListener("keydown", a); }; }, [b]); const J = { ...F, ...b ? { zIndex: 999999, width: "100vw", height: "100vh", maxHeight: "none", maxWidth: "none", position: "fixed", top: 0, left: 0, right: 0, bottom: 0 } : { maxHeight: P, maxWidth: I } }, ge = /* @__PURE__ */ u( "div", { ref: v, className: V( "dify-chatbot dify-chatbot-container flex flex-col min-w-0 bg-background p-[2px]", { "h-full border border-border rounded-lg": !b && s === "embedded", dark: D }, y ), style: J, children: [ M && /* @__PURE__ */ u("div", { className: "flex items-center justify-between p-4 border-b bg-card", children: [ /* @__PURE__ */ u("div", { className: "flex items-center gap-3", children: [ x && p && /* @__PURE__ */ t( "img", { src: p, alt: "Assistant", className: "h-8 w-8 rounded-full object-cover" } ), /* @__PURE__ */ u("div", { children: [ /* @__PURE__ */ t("h3", { className: "font-semibold text-card-foreground", children: n }), l && /* @__PURE__ */ t("p", { className: "text-sm text-muted-foreground", children: l }) ] }) ] }), /* @__PURE__ */ u("div", { className: "flex items-center gap-1", children: [ /* @__PURE__ */ t( "button", { onClick: Q, title: "Reset conversation", className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground p-1 rounded-md", children: /* @__PURE__ */ t(ze, { className: "h-4 w-4" }) } ), /* @__PURE__ */ t( "button", { onClick: O, title: b ? "Exit fullscreen" : "Enter fullscreen", className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground p-1 rounded-md", children: b ? /* @__PURE__ */ t(Xe, { className: "h-4 w-4" }) : /* @__PURE__ */ t(Ye, { className: "h-4 w-4" }) } ) ] }) ] }), /* @__PURE__ */ u( "div", { ref: c, className: "dify-chatbot-messages flex flex-col min-w-0 gap-6 flex-1 overflow-y-auto pt-4 relative", style: { overscrollBehavior: "contain", touchAction: "pan-y" }, children: [ d.length === 0 && /* @__PURE__ */ t("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ u("div", { className: "text-center text-muted-foreground", children: [ /* @__PURE__ */ t("div", { className: "mb-4", children: /* @__PURE__ */ t( we, { size: 48, className: "mx-auto text-muted-foreground/30" } ) }), /* @__PURE__ */ t("p", { className: "text-lg font-medium text-foreground", children: "How can I help you today?" }), l && /* @__PURE__ */ t("p", { className: "text-sm mt-2 text-muted-foreground", children: l }) ] }) }), d.map((a) => /* @__PURE__ */ t( nt, { message: a, showAvatar: x, assistantAvatar: p }, a.id )), $ && d.length > 0 && ((Ne = d[d.length - 1]) == null ? void 0 : Ne.role) === "user" && /* @__PURE__ */ t( at, { showAvatar: x, assistantAvatar: p } ), K && /* @__PURE__ */ t("div", { className: "w-full mx-auto max-w-3xl px-4", children: /* @__PURE__ */ u("div", { className: "text-center text-destructive text-sm py-2 bg-destructive/10 rounded-md", children: [ "Error: ", K.message ] }) }), /* @__PURE__ */ t( "div", { ref: f, className: "shrink-0 min-w-[24px] min-h-[24px]" } ) ] } ), /* @__PURE__ */ t( "form", { className: "flex mx-auto px-4 bg-background pb-4 md:pb-6 gap-2 w-full md:max-w-3xl", onSubmit: (a) => a.preventDefault(), children: /* @__PURE__ */ u("div", { className: "relative w-full flex flex-col gap-4", children: [ i && /* @__PURE__ */ t("div", { className: "absolute left-1/2 bottom-28 -translate-x-1/2 z-50", children: /* @__PURE__ */ t( "button", { onClick: re, className: "inline-flex items-center justify-center border border-input bg-background hover:bg-accent hover:text-accent-foreground h-8 w-8 rounded-full", children: /* @__PURE__ */ t(Ge, { className: "h-4 w-4" }) } ) }), /* @__PURE__ */ t( lt, { onSendMessage: pe, onUploadFile: S ? ie : void 0, placeholder: o, disabled: E, allowFileUpload: S, allowedFileTypes: m, maxFileSize: j, autoFocus: z, initialMessage: B, status: $ ? "streaming" : "ready", onStop: ae, showResetButton: !M, onReset: Q } ) ] }) } ) ] } ); return b ? Ue(ge, document.body) : ge; } ); ke.displayName = "DifyChatbot"; const ct = { default: "bg-primary text-primary-foreground hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline" }, dt = { default: "h-10 px-4 py-2", sm: "h-9 rounded-md px-3", lg: "h-11 rounded-md px-8", icon: "h-10 w-10" }, Le = De.forwardRef( ({ className: e, variant: r = "default", size: s = "default", ...o }, n) => /* @__PURE__ */ t( "button", { className: V( "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", ct[r], dt[s], e ), ref: n, ...o } ) ); Le.displayName = "Button"; const kt = ({ config: e, theme: r, placeholder: s = "Type your message...", title: o = "Chat Assistant", subtitle: n, avatar: l, className: p, style: y, onMessage: F, onError: _, maxHeight: g = 500, maxWidth: N = 400, showHeader: P = !0, showAvatar: I = !0, allowFileUpload: M = !1, allowedFileTypes: x, maxFileSize: S = 15 * 1024 * 1024, autoFocus: R = !1, disabled: j = !1, position: z = "bottom-right", offset: E = { x: 0, y: 0 }, trigger: B, defaultOpen: h = !1, onOpenChange: f, triggerIcon: v, triggerText: c }) => { const [m, i] = T(h); q(() => { f == null || f(m); }, [m, f]); const w = () => { i(!m); }, b = () => "dify-chatbot dify-floating-chatbot ", C = () => { const d = E.x, $ = E.y; switch (z) { case "bottom-right": return { bottom: 20 + $, right: 20 + d, zIndex: 999999999, position: "fixed" }; case "bottom-left": return { bottom: 20 + $, left: 20 + d, zIndex: 999999999, position: "fixed" }; case "top-right": return { top: 20 + $, right: 20 + d, zIndex: 999999999, position: "fixed" }; case "top-left": return { top: 20 + $, left: 20 + d, zIndex: 999999999, position: "fixed" }; default: return { bottom: 20, right: 20, zIndex: 999999999, position: "fixed" }; } }, O = () => { switch (z) { case "bottom-right": case "bottom-left": return { y: 20, opacity: 0 }; case "top-right": case "top-left": return { y: -20, opacity: 0 }; default: return { y: 20, opacity: 0 }; } }; return /* @__PURE__ */ u("div", { className: b(), style: C(), children: [ /* @__PURE__ */ t(ne, { children: m && /* @__PURE__ */ t( X.div, { initial: O(), animate: { y: 0, opacity: 1 }, exit: O(), transition: { duration: 0.2, ease: "easeOut" }, className: V("mb-4", z.includes("top") && "mb-0 mt-4"), style: { width: N, height: g }, children: /* @__PURE__ */ t( ke, { config: e, theme: r, displayMode: "embedded", placeholder: s, title: o, subtitle: n, avatar: l, className: p, style: y, onMessage: F, onError: _, maxHeight: g, maxWidth: N, showHeader: P, showAvatar: I, allowFileUpload: M, allowedFileTypes: x, maxFileSize: S, autoFocus: R, disabled: j, preventExternalScroll: !0 } ) } ) })