UNPKG

llmasaservice-ui

Version:

Prebuilt UI components for LLMAsAService.io

1,260 lines (1,257 loc) 60.8 kB
var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/ChatPanel.tsx import { useLLM } from "llmasaservice-client"; import React2, { useEffect, useRef, useState as useState2 } from "react"; import ReactMarkdown from "react-markdown"; import rehypeRaw from "rehype-raw"; import ReactDOMServer from "react-dom/server"; import remarkGfm from "remark-gfm"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import materialDark from "react-syntax-highlighter/dist/esm/styles/prism/material-dark.js"; import materialLight from "react-syntax-highlighter/dist/esm/styles/prism/material-light.js"; // src/EmailModal.tsx import React, { useState } from "react"; var EmailModal = ({ isOpen, onClose, onSend, defaultEmail }) => { const [email, setEmail] = useState(""); const [emailFrom, setEmailFrom] = useState(defaultEmail || ""); const handleSend = () => { onSend(email, emailFrom); onClose(); }; if (!isOpen) return null; return /* @__PURE__ */ React.createElement("div", { className: "modal-overlay" }, /* @__PURE__ */ React.createElement("div", { className: "modal-content" }, /* @__PURE__ */ React.createElement("p", { className: "modal-text" }, "Email Addresses", /* @__PURE__ */ React.createElement("br", null), " (If multiple, comma separate them)"), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement( "input", { type: "email", width: "100%", value: email, onChange: (e) => setEmail(e.target.value), placeholder: "To email address" } )), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement( "input", { type: "email", width: "100%", value: emailFrom, onChange: (e) => setEmailFrom(e.target.value), placeholder: "From email address (optional)" } )), /* @__PURE__ */ React.createElement("div", { className: "modal-buttons" }, /* @__PURE__ */ React.createElement("button", { onClick: onClose }, "Cancel"), /* @__PURE__ */ React.createElement("button", { onClick: handleSend }, "Send")))); }; var EmailModal_default = EmailModal; // src/ChatPanel.tsx var ChatPanel = ({ project_id, initialPrompt = "", title = "", placeholder = "Type a message", hideInitialPrompt = true, customer = {}, messages = [], data = [], thumbsUpClick, thumbsDownClick, theme = "light", cssUrl = "", markdownClass = null, width = "300px", height = "100vh", url = null, scrollToEnd = false, initialMessage = "", prismStyle = theme === "light" ? materialLight : materialDark, service = null, historyChangedCallback = null, responseCompleteCallback = null, promptTemplate = "", actions = [], showSaveButton = true, showEmailButton = true, followOnQuestions = [], clearFollowOnQuestionsNextPrompt = false, followOnPrompt = "", showPoweredBy = true, agent = null, conversation = null, showCallToAction = false, callToActionButtonText = "Submit", callToActionEmailAddress = "", callToActionEmailSubject = "Agent CTA submitted", initialHistory = {}, hideRagContextInPrompt = true, createConversationOnFirstChat = true, customerEmailCaptureMode = "HIDE", customerEmailCapturePlaceholder = "Please enter your email..." }) => { var _a; const isEmailAddress = (email) => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); }; const [nextPrompt, setNextPrompt] = useState2(""); const [lastController, setLastController] = useState2(new AbortController()); const [history, setHistory] = useState2(initialHistory); const [isLoading, setIsLoading] = useState2(false); const [lastPrompt, setLastPrompt] = useState2(null); const [lastKey, setLastKey] = useState2(null); const [hasScroll, setHasScroll] = useState2(false); const bottomRef = useRef(null); const bottomPanelRef = useRef(null); const [isAtBottom, setIsAtBottom] = useState2(true); const [isEmailModalOpen, setIsEmailModalOpen] = useState2(false); const [currentConversation, setCurrentConversation] = useState2( conversation ); const [emailInput, setEmailInput] = useState2( (_a = customer == null ? void 0 : customer.customer_user_email) != null ? _a : "" ); const [emailInputSet, setEmailInputSet] = useState2( isEmailAddress(emailInput) ); const [emailValid, setEmailValid] = useState2(true); const [showEmailPanel, setShowEmailPanel] = useState2( customerEmailCaptureMode !== "HIDE" ); const [callToActionSent, setCallToActionSent] = useState2(false); const [CTAClickedButNoEmail, setCTAClickedButNoEmail] = useState2(false); const [emailSent, setEmailSent] = useState2(false); const [emailClickedButNoEmail, setEmailClickedButNoEmail] = useState2(false); const handleSendEmail = (to, from) => { sendConversationsViaEmail(to, `Conversation History from ${title}`, from); interactionClicked(lastCallId, "email", to); setEmailSent(true); }; const [currentCustomer, setCurrentCustomer] = useState2(customer); const { send, response, idle, stop, lastCallId } = useLLM({ project_id, customer: currentCustomer, url, agent }); const responseAreaRef = useRef(null); let publicAPIUrl = "https://api.llmasaservice.io"; if (window.location.hostname === "localhost" || window.location.hostname === "dev.llmasaservice.io") { publicAPIUrl = "https://8ftw8droff.execute-api.us-east-1.amazonaws.com/dev"; } useEffect(() => { setShowEmailPanel(customerEmailCaptureMode !== "HIDE"); if (customerEmailCaptureMode === "REQUIRED") { if (!isEmailAddress(emailInput)) { setEmailValid(false); } } }, [customerEmailCaptureMode]); useEffect(() => { if (responseCompleteCallback) { if (lastCallId && lastCallId !== "" && idle) responseCompleteCallback(lastCallId, lastPrompt != null ? lastPrompt : "", response); } }, [idle]); useEffect(() => { if (Object.keys(initialHistory).length === 0) return; setHistory(initialHistory); }, [initialHistory]); useEffect(() => { if (!conversation || conversation === "") return; setCurrentConversation(conversation); setHistory(initialHistory); }, [conversation]); useEffect(() => { const existingLinks = document.querySelectorAll( 'link[data-source="llmasaservice-ui"]' ); existingLinks.forEach((link) => { var _a2; return (_a2 = link.parentNode) == null ? void 0 : _a2.removeChild(link); }); const existingStyles = document.querySelectorAll( 'style[data-source="llmasaservice-ui"]' ); existingStyles.forEach((style) => { var _a2; return (_a2 = style.parentNode) == null ? void 0 : _a2.removeChild(style); }); if (cssUrl) { if (cssUrl.startsWith("http://") || cssUrl.startsWith("https://")) { const link = document.createElement("link"); link.href = cssUrl; link.rel = "stylesheet"; link.setAttribute("data-source", "llmasaservice-ui"); document.head.appendChild(link); console.log("Added CSS link", link); } else { const style = document.createElement("style"); style.textContent = cssUrl; style.setAttribute("data-source", "llmasaservice-ui"); document.head.appendChild(style); console.log("Added inline CSS"); } } return () => { const links = document.querySelectorAll( 'link[data-source="llmasaservice-ui"]' ); links.forEach((link) => { var _a2; return (_a2 = link.parentNode) == null ? void 0 : _a2.removeChild(link); }); const styles = document.querySelectorAll( 'style[data-source="llmasaservice-ui"]' ); styles.forEach((style) => { var _a2; return (_a2 = style.parentNode) == null ? void 0 : _a2.removeChild(style); }); }; }, [cssUrl]); useEffect(() => { if (response && response.length > 0) { setIsLoading(false); let newResponse = response; if (actions && actions.length > 0) { actions.forEach((action, index) => { const regex = new RegExp(action.pattern, "gmi"); newResponse = newResponse.replace(regex, (match, ...groups) => { var _a2, _b; console.log("action match", match, groups); const matchIndex = groups[groups.length - 2]; const buttonId = `button-${messages.length}-${index}-${matchIndex}`; let html = match; if (action.type === "button" || action.type === "callback") { html = ` <button id="${buttonId}" ${action.style ? 'class=" ' + action.style + '"' : ""}>${(_a2 = action.markdown) != null ? _a2 : match}</button>`; } else if (action.type === "markdown" || action.type === "html") { html = (_b = action.markdown) != null ? _b : ""; } html = html.replace(new RegExp("\\$match", "g"), match); groups.forEach((group, index2) => { html = html.replace(new RegExp(`\\$${index2 + 1}`, "g"), group); }); setTimeout(() => { const button = document.getElementById(buttonId); if (button) { if (!button.onclick) { button.onclick = () => { if (action.callback) { action.callback(match, groups); } if (action.clickCode) { try { const func = new Function("match", action.clickCode); func(match); interactionClicked(lastCallId, "action"); } catch (error) { console.error("Error executing clickCode:", error); } } }; } } }, 0); return html; }); }); } setHistory((prevHistory) => { return __spreadProps(__spreadValues({}, prevHistory), { [lastKey != null ? lastKey : ""]: { content: newResponse, callId: lastCallId } }); }); } }, [response]); function hasVerticalScrollbar(element) { return element.scrollHeight > element.clientHeight; } const getBrowserInfo = () => { try { return { currentTimeUTC: (/* @__PURE__ */ new Date()).toISOString(), userTimezone: typeof Intl !== "undefined" && Intl.DateTimeFormat().resolvedOptions().timeZone || "unknown", userLanguage: typeof navigator !== "undefined" && (navigator.language || navigator.language) || "unknown" }; } catch (e) { console.warn("Error getting browser info:", e); return { currentTimeUTC: (/* @__PURE__ */ new Date()).toISOString(), userTimezone: "unknown", userLanguage: "unknown" }; } }; useEffect(() => { if (initialPrompt && initialPrompt !== "") { if (initialPrompt !== lastPrompt) { setIsLoading(true); ensureConversation().then((convId) => { var _a2, _b, _c, _d; if (lastController) stop(lastController); const controller = new AbortController(); const browserInfo = getBrowserInfo(); send( initialPrompt, messages, [ ...data, { key: "--customer_id", data: (_a2 = currentCustomer == null ? void 0 : currentCustomer.customer_id) != null ? _a2 : "" }, { key: "--customer_name", data: (_b = currentCustomer == null ? void 0 : currentCustomer.customer_name) != null ? _b : "" }, { key: "--customer_user_id", data: (_c = currentCustomer == null ? void 0 : currentCustomer.customer_user_id) != null ? _c : "" }, { key: "--customer_user_email", data: (_d = currentCustomer == null ? void 0 : currentCustomer.customer_user_email) != null ? _d : "" }, { key: "--email", data: emailInput != null ? emailInput : "" }, { key: "--emailValid", data: emailValid ? "true" : "false" }, { key: "--emailInputSet", data: emailInputSet ? "true" : "false" }, { key: "--emailPanelShowing", data: showEmailPanel ? "true" : "false" }, { key: "--callToActionSent", data: callToActionSent ? "true" : "false" }, { key: "--CTAClickedButNoEmail", data: CTAClickedButNoEmail ? "true" : "false" }, { key: "--emailSent", data: emailSent ? "true" : "false" }, { key: "--emailClickedButNoEmail", data: emailClickedButNoEmail ? "true" : "false" }, { key: "--messages", data: messages.length.toString() }, { key: "--currentTimeUTC", data: browserInfo == null ? void 0 : browserInfo.currentTimeUTC }, { key: "--userTimezone", data: browserInfo == null ? void 0 : browserInfo.userTimezone }, { key: "--userLanguage", data: browserInfo == null ? void 0 : browserInfo.userLanguage } ], true, true, service, convId, controller ); setLastPrompt(initialPrompt); setLastKey(initialPrompt); setLastController(controller); setHistory({}); }); } } }, [initialPrompt]); useEffect(() => { var _a2; if (scrollToEnd) { if (window.top !== window.self) { const responseArea = responseAreaRef.current; responseArea.scrollTo({ top: responseArea.scrollHeight, behavior: "smooth" }); } else { (_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "end" }); } } else { const responseArea = responseAreaRef.current; if (responseArea) { setHasScroll(hasVerticalScrollbar(responseArea)); const handleScroll = () => { const isScrolledToBottom = responseArea.scrollHeight - responseArea.scrollTop === responseArea.clientHeight; setIsAtBottom(isScrolledToBottom); }; handleScroll(); responseArea.addEventListener("scroll", handleScroll); return () => responseArea.removeEventListener("scroll", handleScroll); } } }, [response, history]); useEffect(() => { if (historyChangedCallback) { historyChangedCallback(history); } }, [history, historyChangedCallback]); useEffect(() => { console.log("followOnPromptChanged", followOnPrompt); if (followOnPrompt && followOnPrompt !== "") { continueChat(followOnPrompt); } }, [followOnPrompt]); const setCookie = (name, value, days = 30) => { const date = /* @__PURE__ */ new Date(); date.setTime(date.getTime() + days * 24 * 60 * 60 * 1e3); const expires = `; expires=${date.toUTCString()}`; const sameSite = "; SameSite=Lax"; const secure = window.location.protocol === "https:" ? "; Secure" : ""; document.cookie = `${name}=${value}${expires}; path=/${sameSite}${secure}`; }; const getCookie = (name) => { var _a2; const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) { return (_a2 = parts.pop()) == null ? void 0 : _a2.split(";").shift(); } return void 0; }; const ensureConversation = () => { var _a2, _b; if ((!currentConversation || currentConversation === "") && createConversationOnFirstChat) { const browserInfo = getBrowserInfo(); return fetch(`${publicAPIUrl}/conversations`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ project_id: project_id != null ? project_id : "", agentId: agent, customerId: (_a2 = currentCustomer == null ? void 0 : currentCustomer.customer_id) != null ? _a2 : null, customerEmail: (_b = currentCustomer == null ? void 0 : currentCustomer.customer_user_email) != null ? _b : null, timezone: browserInfo == null ? void 0 : browserInfo.userTimezone, language: browserInfo == null ? void 0 : browserInfo.userLanguage }) }).then((res) => __async(void 0, null, function* () { if (!res.ok) { const errorText = yield res.text(); throw new Error( `HTTP error! status: ${res.status}, message: ${errorText}` ); } return res.json(); })).then((newConvo) => { if (newConvo == null ? void 0 : newConvo.id) { console.log("new conversation created", newConvo.id); setCurrentConversation(newConvo.id); return newConvo.id; } return ""; }).catch((error) => { console.error("Error creating new conversation", error); return ""; }); } return Promise.resolve(currentConversation); }; useEffect(() => { var _a2, _b, _c; console.log("currentCustomer effect", currentCustomer); const isEmpty = (value) => { return !value || value.trim() === "" || value.trim() === "undefined"; }; let updatedValues = __spreadValues({}, currentCustomer); let needsUpdate = false; if (!isEmpty(currentCustomer == null ? void 0 : currentCustomer.customer_id)) { setCookie("llmasaservice-panel-customer-id", currentCustomer.customer_id); } if (!isEmpty(currentCustomer == null ? void 0 : currentCustomer.customer_user_email) && isEmailAddress((_a2 = currentCustomer.customer_user_email) != null ? _a2 : "")) { setCookie( "llmasaservice-panel-customer-user-email", (_b = currentCustomer == null ? void 0 : currentCustomer.customer_user_email) != null ? _b : "" ); setEmailInput((_c = currentCustomer.customer_user_email) != null ? _c : ""); setEmailInputSet(true); setEmailValid(true); } if (isEmpty(currentCustomer == null ? void 0 : currentCustomer.customer_id)) { const cookieId = getCookie("llmasaservice-panel-customer-id"); if (!isEmpty(cookieId)) { updatedValues.customer_id = cookieId != null ? cookieId : ""; needsUpdate = true; } } if (isEmpty(currentCustomer == null ? void 0 : currentCustomer.customer_user_email)) { const cookieEmail = getCookie("llmasaservice-panel-customer-user-email"); if (!isEmpty(cookieEmail) && isEmailAddress(cookieEmail != null ? cookieEmail : "")) { updatedValues.customer_user_email = cookieEmail; needsUpdate = true; } } if (needsUpdate) { ensureConversation().then((convId) => { if (convId && convId !== "") { console.log( "updating conversation with customer id and email", convId, currentCustomer ); const r = fetch(`${publicAPIUrl}/conversations/${convId}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ project_id: project_id != null ? project_id : "", customerId: updatedValues.customer_id, customerEmail: updatedValues.customer_user_email }) }); } }); setCurrentCustomer(updatedValues); } }, [currentCustomer]); const continueChat = (suggestion) => { console.log("continueChat", suggestion); ensureConversation().then((convId) => { var _a2, _b, _c, _d, _e, _f, _g, _h; console.log("current customer", currentCustomer); if (!idle) { stop(lastController); setHistory((prevHistory) => { return __spreadProps(__spreadValues({}, prevHistory), { [lastKey != null ? lastKey : ""]: { content: response + "\n\n(response cancelled)", callId: lastCallId } }); }); return; } if (clearFollowOnQuestionsNextPrompt) { followOnQuestions = []; const suggestionsContainer = document.querySelector( ".suggestions-container" ); if (suggestionsContainer) { suggestionsContainer.innerHTML = ""; } } if (suggestion && suggestion !== "" || nextPrompt && nextPrompt !== "") { setIsLoading(true); const messagesAndHistory = messages; Object.entries(history).forEach(([prompt, response2]) => { var _a3, _b2, _c2; let promptToSend = prompt; if (promptTemplate && promptTemplate !== "") { promptToSend = promptTemplate.replace("{{prompt}}", promptToSend); for (let i = 0; i < data.length; i++) { promptToSend = promptToSend.replace( "{{" + ((_a3 = data[i]) == null ? void 0 : _a3.key) + "}}", (_c2 = (_b2 = data[i]) == null ? void 0 : _b2.data) != null ? _c2 : "" ); } } messagesAndHistory.push({ role: "user", content: promptToSend }); messagesAndHistory.push({ role: "assistant", content: response2.content }); }); let nextPromptToSend = suggestion != null ? suggestion : nextPrompt; let promptKey = nextPromptToSend != null ? nextPromptToSend : ""; let lastPromptKeyCharacter = (_a2 = promptKey[promptKey.length - 1]) != null ? _a2 : ""; const count = Object.keys(history).filter((key) => { return key.startsWith(promptKey) && promptKey.length > 0 && (key.endsWith(lastPromptKeyCharacter) || key.endsWith(")")); }).length; if (count > 0) { promptKey += ` (${count + 1})`; } setHistory((prevHistory) => { return __spreadProps(__spreadValues({}, prevHistory), { [promptKey != null ? promptKey : ""]: { content: "", callId: "" } }); }); if (initialPrompt && initialPrompt !== "" && Object.keys(history).length === 1 || (!initialPrompt || initialPrompt === "") && Object.keys(history).length === 0) { if (promptTemplate && promptTemplate !== "") { nextPromptToSend = promptTemplate.replace( "{{prompt}}", nextPromptToSend ); for (let i = 0; i < data.length; i++) { nextPromptToSend = nextPromptToSend.replace( "{{" + ((_b = data[i]) == null ? void 0 : _b.key) + "}}", (_d = (_c = data[i]) == null ? void 0 : _c.data) != null ? _d : "" ); } } } console.log("Sending for conversation", convId); const browserInfo = getBrowserInfo(); const controller = new AbortController(); send( nextPromptToSend, messagesAndHistory, [ ...data, { key: "--customer_id", data: (_e = currentCustomer == null ? void 0 : currentCustomer.customer_id) != null ? _e : "" }, { key: "--customer_name", data: (_f = currentCustomer == null ? void 0 : currentCustomer.customer_name) != null ? _f : "" }, { key: "--customer_user_id", data: (_g = currentCustomer == null ? void 0 : currentCustomer.customer_user_id) != null ? _g : "" }, { key: "--customer_user_email", data: (_h = currentCustomer == null ? void 0 : currentCustomer.customer_user_email) != null ? _h : "" }, { key: "--email", data: emailInput != null ? emailInput : "" }, { key: "--emailValid", data: emailValid ? "true" : "false" }, { key: "--emailInputSet", data: emailInputSet ? "true" : "false" }, { key: "--emailPanelShowing", data: showEmailPanel ? "true" : "false" }, { key: "--callToActionSent", data: callToActionSent ? "true" : "false" }, { key: "--CTAClickedButNoEmail", data: CTAClickedButNoEmail ? "true" : "false" }, { key: "--emailSent", data: emailSent ? "true" : "false" }, { key: "--emailClickedButNoEmail", data: emailClickedButNoEmail ? "true" : "false" }, { key: "--messages", data: messagesAndHistory.length.toString() }, { key: "--currentTimeUTC", data: browserInfo == null ? void 0 : browserInfo.currentTimeUTC }, { key: "--userTimezone", data: browserInfo == null ? void 0 : browserInfo.userTimezone }, { key: "--userLanguage", data: browserInfo == null ? void 0 : browserInfo.userLanguage } ], true, true, service, convId, controller ); setLastPrompt(nextPromptToSend); setLastKey(promptKey); setLastController(controller); setNextPrompt(""); } }); }; const replaceHistory = (newHistory) => { setHistory(newHistory); }; function copyToClipboard(text) { navigator.clipboard.writeText(text); interactionClicked(lastCallId, "copy"); } const scrollToBottom = () => { var _a2; if (window.top !== window.self) { const responseArea = responseAreaRef.current; responseArea.scrollTo({ top: responseArea.scrollHeight, behavior: "smooth" }); } else { (_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" }); } }; const CodeBlock = (_b) => { var _c = _b, { node, className, children, style } = _c, props = __objRest(_c, ["node", "className", "children", "style"]); const match = /language-(\w+)/.exec(className || ""); return match ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement( "div", { style: { border: 0, padding: 0, height: "16px", display: "flex", justifyContent: "space-between", alignItems: "center" } }, /* @__PURE__ */ React2.createElement("span", null, match ? match[1] : "Unknown"), /* @__PURE__ */ React2.createElement( "button", { onClick: () => copyToClipboard(children), className: "copy-button" }, /* @__PURE__ */ React2.createElement( "svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 320 320", fill: "currentColor", className: "icon-svg" }, /* @__PURE__ */ React2.createElement( "path", { d: "M35,270h45v45c0,8.284,6.716,15,15,15h200c8.284,0,15-6.716,15-15V75c0-8.284-6.716-15-15-15h-45V15\r\n c0-8.284-6.716-15-15-15H35c-8.284,0-15,6.716-15,15v240C20,263.284,26.716,270,35,270z M280,300H110V90h170V300z M50,30h170v30H95\r\n c-8.284,0-15,6.716-15,15v165H50V30z" } ), /* @__PURE__ */ React2.createElement("path", { d: "M155,120c-8.284,0-15,6.716-15,15s6.716,15,15,15h80c8.284,0,15-6.716,15-15s-6.716-15-15-15H155z" }), /* @__PURE__ */ React2.createElement("path", { d: "M235,180h-80c-8.284,0-15,6.716-15,15s6.716,15,15,15h80c8.284,0,15-6.716,15-15S243.284,180,235,180z" }), /* @__PURE__ */ React2.createElement( "path", { d: "M235,240h-80c-8.284,0-15,6.716-15,15c0,8.284,6.716,15,15,15h80c8.284,0,15-6.716,15-15C250,246.716,243.284,240,235,240z\r\n " } ) ) ) ), /* @__PURE__ */ React2.createElement( SyntaxHighlighter, __spreadValues({ style: prismStyle, PreTag: "div", language: match[1] }, props), String(children).replace(/\n$/, "") )) : /* @__PURE__ */ React2.createElement("code", __spreadValues({ className: className ? className : "" }, props), children); }; const CustomLink = (_d) => { var _e = _d, { href, children } = _e, props = __objRest(_e, ["href", "children"]); return /* @__PURE__ */ React2.createElement("a", __spreadValues({ href, target: "_blank", rel: "noopener noreferrer" }, props), children); }; const convertMarkdownToHTML = (markdown) => { const html = ReactDOMServer.renderToStaticMarkup( /* @__PURE__ */ React2.createElement( ReactMarkdown, { className: markdownClass, remarkPlugins: [remarkGfm], rehypePlugins: [rehypeRaw] }, markdown ) ); return html; }; const convertHistoryToHTML = (history2) => { const stylesheet = ` <style> .conversation-history { font-family: Arial, sans-serif; line-height: 1.5; /* Slightly increase line height for readability */ } .history-entry { margin-bottom: 15px; } .prompt-container, .response-container { margin-bottom: 10px; /* Adjusted spacing */ } .prompt, .response { display: block; /* Ensure they take up the full row */ margin: 5px 0; /* Add vertical spacing */ padding: 10px; /* Increase padding for better spacing */ border-radius: 5px; max-width: 80%; /* Keep width constrained */ } .prompt { background-color: #efefef; margin-left: 0; /* Align to the left */ } .response { background-color: #f0fcfd; margin-left: 25px; /* Indent slightly for visual differentiation */ } </style> `; let html = ` <html> <head> ${stylesheet} </head> <body> <h1>Conversation History (${(/* @__PURE__ */ new Date()).toLocaleString()})</h1> <div class="conversation-history"> `; Object.entries(history2).forEach(([prompt, response2], index) => { if (hideInitialPrompt && index === 0) { html += ` <div class="history-entry"> <div class="response-container"> <div class="response">${convertMarkdownToHTML( response2.content )}</div> </div> </div> `; } else { html += ` <div class="history-entry"> <div class="prompt-container"> <div class="prompt">${convertMarkdownToHTML( formatPromptForDisplay(prompt) )}</div> </div> <div class="response-container"> <div class="response">${convertMarkdownToHTML(response2.content)}</div> </div> </div> `; } }); html += ` </div> </body> </html> `; return html; }; const saveHTMLToFile = (html, filename) => { const blob = new Blob([html], { type: "text/html" }); const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); }; const shareViaEmail = (html, subject) => { const mailtoLink = `mailto:?subject=${encodeURIComponent( subject )}&body=${encodeURIComponent(html)}`; window.location.href = mailtoLink; }; const handleSuggestionClick = (suggestion) => { continueChat(suggestion); interactionClicked(lastCallId, "suggestion"); }; const sendConversationsViaEmail = (to, subject = `Conversation History from ${title}`, from = "") => { fetch(`${publicAPIUrl}/share/email`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ to, from, subject, html: convertHistoryToHTML(history), project_id: project_id != null ? project_id : "", customer: currentCustomer, history, title }) }); interactionClicked(lastCallId, "email", from); }; const sendCallToActionEmail = (from) => __async(void 0, null, function* () { const r = yield fetch(`${publicAPIUrl}/share/email`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ to: callToActionEmailAddress, from, subject: `${callToActionEmailSubject} from ${from}`, html: convertHistoryToHTML(history), project_id: project_id != null ? project_id : "", customer: currentCustomer, history, title }) }); interactionClicked(lastCallId, "cta", from); setCallToActionSent(true); }); const interactionClicked = (callId, action, emailaddress = "", comment = "") => __async(void 0, null, function* () { console.log(`Interaction clicked: ${action} for callId: ${callId}`); ensureConversation().then((convId) => { var _a2, _b; if (!callId || callId === "") callId = convId; const email = emailaddress && emailaddress !== "" ? emailaddress : isEmailAddress((_a2 = currentCustomer == null ? void 0 : currentCustomer.customer_user_email) != null ? _a2 : "") ? currentCustomer == null ? void 0 : currentCustomer.customer_user_email : isEmailAddress((_b = currentCustomer == null ? void 0 : currentCustomer.customer_id) != null ? _b : "") ? currentCustomer == null ? void 0 : currentCustomer.customer_id : ""; fetch(`${publicAPIUrl}/feedback/${callId}/${action}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ project_id: project_id != null ? project_id : "", conversation_id: convId != null ? convId : "", email, comment }) }); }); }); const formatPromptForDisplay = (prompt) => { if (!prompt) { return ""; } if (hideRagContextInPrompt && prompt.includes("CONTEXT:")) { const parts = prompt.split("CONTEXT:"); const withoutContext = parts.length > 0 ? parts[0] : ""; if (withoutContext.includes("PROMPT:")) { const promptParts = withoutContext.split("PROMPT:"); return promptParts.length > 1 ? (promptParts[1] || "").trim() : withoutContext.trim(); } return withoutContext.trim(); } return prompt; }; const isDisabledDueToNoEmail = () => { const valid = isEmailAddress(emailInput); if (valid) return false; if (customerEmailCaptureMode === "REQUIRED") return true; }; return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement( "div", { style: { width, height }, className: "llm-panel" + (theme === "light" ? "" : " dark-theme") }, title && title !== "" ? /* @__PURE__ */ React2.createElement("div", { className: "title" }, title) : null, /* @__PURE__ */ React2.createElement("div", { className: "responseArea", ref: responseAreaRef }, initialMessage && initialMessage !== "" ? /* @__PURE__ */ React2.createElement("div", { className: "history-entry" }, /* @__PURE__ */ React2.createElement("div", { className: "response" }, /* @__PURE__ */ React2.createElement( ReactMarkdown, { className: markdownClass, remarkPlugins: [remarkGfm], rehypePlugins: [rehypeRaw] }, initialMessage ))) : null, Object.entries(history).map(([prompt, response2], index) => /* @__PURE__ */ React2.createElement("div", { className: "history-entry", key: index }, hideInitialPrompt && index === 0 ? null : /* @__PURE__ */ React2.createElement("div", { className: "prompt" }, formatPromptForDisplay(prompt)), /* @__PURE__ */ React2.createElement("div", { className: "response" }, index === Object.keys(history).length - 1 && isLoading ? /* @__PURE__ */ React2.createElement("div", { className: "loading-text" }, "loading...") : null, /* @__PURE__ */ React2.createElement( ReactMarkdown, { className: markdownClass, remarkPlugins: [remarkGfm], rehypePlugins: [rehypeRaw], components: { /*a: CustomLink,*/ code: CodeBlock } }, response2.content ), /* @__PURE__ */ React2.createElement("div", { className: "button-container" }, /* @__PURE__ */ React2.createElement( "button", { className: "copy-button", onClick: () => { copyToClipboard(response2.content); }, disabled: isDisabledDueToNoEmail() }, /* @__PURE__ */ React2.createElement( "svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 320 320", fill: "currentColor", className: "icon-svg" }, /* @__PURE__ */ React2.createElement( "path", { d: "M35,270h45v45c0,8.284,6.716,15,15,15h200c8.284,0,15-6.716,15-15V75c0-8.284-6.716-15-15-15h-45V15\r\n c0-8.284-6.716-15-15-15H35c-8.284,0-15,6.716-15,15v240C20,263.284,26.716,270,35,270z M280,300H110V90h170V300z M50,30h170v30H95\r\n c-8.284,0-15,6.716-15,15v165H50V30z" } ), /* @__PURE__ */ React2.createElement("path", { d: "M155,120c-8.284,0-15,6.716-15,15s6.716,15,15,15h80c8.284,0,15-6.716,15-15s-6.716-15-15-15H155z" }), /* @__PURE__ */ React2.createElement("path", { d: "M235,180h-80c-8.284,0-15,6.716-15,15s6.716,15,15,15h80c8.284,0,15-6.716,15-15S243.284,180,235,180z" }), /* @__PURE__ */ React2.createElement( "path", { d: "M235,240h-80c-8.284,0-15,6.716-15,15c0,8.284,6.716,15,15,15h80c8.284,0,15-6.716,15-15C250,246.716,243.284,240,235,240z\r\n " } ) ) ), /* @__PURE__ */ React2.createElement( "button", { className: "thumbs-button", onClick: () => { if (thumbsUpClick) thumbsUpClick(response2.callId); interactionClicked(response2.callId, "thumbsup"); }, disabled: isDisabledDueToNoEmail() }, /* @__PURE__ */ React2.createElement( "svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: "icon-svg" }, /* @__PURE__ */ React2.createElement("path", { d: "M20.22 9.55C19.79 9.04 19.17 8.75 18.5 8.75H14.47V6C14.47 4.48 13.24 3.25 11.64 3.25C10.94 3.25 10.31 3.67 10.03 4.32L7.49 10.25H5.62C4.31 10.25 3.25 11.31 3.25 12.62V18.39C3.25 19.69 4.32 20.75 5.62 20.75H17.18C18.27 20.75 19.2 19.97 19.39 18.89L20.71 11.39C20.82 10.73 20.64 10.06 20.21 9.55H20.22ZM5.62 19.25C5.14 19.25 4.75 18.86 4.75 18.39V12.62C4.75 12.14 5.14 11.75 5.62 11.75H7.23V19.25H5.62ZM17.92 18.63C17.86 18.99 17.55 19.25 17.18 19.25H8.74V11.15L11.41 4.9C11.45 4.81 11.54 4.74 11.73 4.74C12.42 4.74 12.97 5.3 12.97 5.99V10.24H18.5C18.73 10.24 18.93 10.33 19.07 10.5C19.21 10.67 19.27 10.89 19.23 11.12L17.91 18.62L17.92 18.63Z" }) ) ), /* @__PURE__ */ React2.createElement( "button", { className: "thumbs-button", onClick: () => { if (thumbsDownClick) thumbsDownClick(response2.callId); interactionClicked(response2.callId, "thumbsdown"); }, disabled: isDisabledDueToNoEmail() }, /* @__PURE__ */ React2.createElement( "svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: "icon-svg" }, /* @__PURE__ */ React2.createElement("path", { d: "M18.38 3.25H6.81C5.72 3.25 4.79 4.03 4.6 5.11L3.29 12.61C3.18 13.27 3.36 13.94 3.78 14.45C4.21 14.96 4.83 15.25 5.5 15.25H9.53V18C9.53 19.52 10.76 20.75 12.36 20.75C13.06 20.75 13.69 20.33 13.97 19.68L16.51 13.75H18.39C19.7 13.75 20.76 12.69 20.76 11.38V5.61C20.76 4.31 19.7 3.25 18.39 3.25H18.38ZM15.26 12.85L12.59 19.1C12.55 19.19 12.46 19.26 12.27 19.26C11.58 19.26 11.03 18.7 11.03 18.01V13.76H5.5C5.27 13.76 5.07 13.67 4.93 13.5C4.78 13.33 4.73 13.11 4.77 12.88L6.08 5.38C6.14 5.02 6.45001 4.76 6.82 4.76H15.26V12.85ZM19.25 11.38C19.25 11.86 18.86 12.25 18.38 12.25H16.77V4.75H18.38C18.86 4.75 19.25 5.14 19.25 5.61V11.38Z" }) ) ))))), followOnQuestions && followOnQuestions.length > 0 && idle && /* @__PURE__ */ React2.createElement("div", { className: "suggestions-container" }, followOnQuestions.map((question, index) => /* @__PURE__ */ React2.createElement( "button", { key: index, className: "suggestion-button", onClick: () => handleSuggestionClick(question), disabled: !idle || isDisabledDueToNoEmail() }, question ))), /* @__PURE__ */ React2.createElement("div", { ref: bottomRef }), hasScroll && !isAtBottom && /* @__PURE__ */ React2.createElement("button", { className: "scroll-button", onClick: scrollToBottom }, "\u2193")), showEmailPanel && /* @__PURE__ */ React2.createElement(React2.Fragment, null, !emailValid && /* @__PURE__ */ React2.createElement("div", { className: "email-input-message" }, isDisabledDueToNoEmail() ? "Let's get started - please enter your email" : CTAClickedButNoEmail || emailClickedButNoEmail ? "Sure, we just need an email address to contact you" : "Email address is invalid"), /* @__PURE__ */ React2.createElement("div", { className: "email-input-container" }, /* @__PURE__ */ React2.createElement( "input", { type: "email", name: "email", id: "email", className: emailValid ? emailInputSet ? "email-input-set" : "email-input" : "email-input-invalid", placeholder: customerEmailCapturePlaceholder, value: emailInput, onChange: (e) => setEmailInput(e.target.value), disabled: emailInputSet } ), /* @__PURE__ */ React2.createElement( "button", { className: "email-input-button", onClick: () => { if (emailInputSet) { setEmailInputSet(false); return; } const newId = (currentCustomer == null ? void 0 : currentCustomer.customer_id) && currentCustomer.customer_id !== "" && currentCustomer.customer_id !== (currentCustomer == null ? void 0 : currentCustomer.customer_user_email) ? currentCustomer.customer_id : emailInput; if (isEmailAddress(emailInput)) { setEmailInputSet((current) => !current); setEmailValid(true); setCurrentCustomer({ customer_id: newId, customer_user_email: emailInput }); if (CTAClickedButNoEmail) { sendCallToActionEmail(emailInput); setCTAClickedButNoEmail(false); } if (emailClickedButNoEmail) { handleSendEmail(emailInput, emailInput); setEmailClickedButNoEmail(false); } } else { if (customerEmailCaptureMode === "REQUIRED") { setEmailValid(false); } else { if (emailInput === "") { setEmailInputSet(false); setEmailValid(true); setCurrentCustomer({ customer_id: newId, customer_user_email: emailInput }); } else { setEmailValid(false); } } } } }, emailInputSet ? "\u270E" : "set" ))), /* @__PURE__ */ React2.createElement("div", { className: "button-container-actions" }, showSaveButton && /* @__PURE__ */ React2.createElement( "button", { className: "save-button", onClick: () => { saveHTMLToFile( convertHistoryToHTML(history), `conversation-${(/* @__PURE__ */ new Date()).toISOString()}.html` ); interactionClicked(lastCallId, "save"); }, disabled: isDisabledDueToNoEmail() }, "Save Conversation" ), showEmailButton && /* @__PURE__ */ React2.createElement( "button", { className: "save-button", onClick: () => { if (isEmailAddress(emailInput)) { setEmailInputSet(true); setEmailValid(true); handleSendEmail(emailInput, emailInput); setEmailClickedButNoEmail(false); } else { setShowEmailPanel(true); setEmailValid(false); setEmailClickedButNoEmail(true); } }, disabled: isDisabledDueToNoEmail() }, "Email Conversation" + (emailSent ? " \u2713" : "") ), showCallToAction && /* @__PURE__ */ React2.createElement( "button", { className: "save-button", onClick: () => { if (isEmailAddress(emailInput)) { setEmailInputSet(true); setEmailValid(true); sendCallToActionEmail(emailInput); setCTAClickedButNoEmail(false); } else { setShowEmailPanel(true); setEmailValid(false); setCTAClickedButNoEmail(true); } }, disabled: isDisabledDueToNoEmail() }, callToActionButtonText + (callToActionSent ? " \u2713" : "") )), /* @__PURE__ */ React2.createElement( EmailModal_default, { isOpen: isEmailModalOpen, defaultEmail: emailInput != null ? emailInput : "", onClose: () => setIsEmailModalOpen(false), onSend: handleSendEmail } ), /* @__PURE__ */ React2.createElement("div", { className: "input-container" }, /* @__PURE__ */ React2.createElement( "textarea", { className: "chat-input", placeholder, value: nextPrompt, onChange: (e) => setNextPrompt(e.target.value), onKeyDown: (e) => { if (e.key === "Enter") { if (nextPrompt !== "") { e.preventDefault(); continueChat(); } } }, disabled: isDisabledDueToNoEmail() } ), /* @__PURE__ */ React2.createElement( "button", { className: "send-button", onClick: () => continueChat(), disabled: isDisabledDueToNoEmail() }, idle ? /* @__PURE__ */ React2.createElement( "svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", strokeWidth: "1", stroke: "currentColor", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", className: "icon-svg-large" }, /* @__PURE__ */ React2.createElement("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }), /* @__PURE__ */ React2.createElement("path", { d: "M10 14l11 -11" }), /* @__PURE__ */ React2.createElement("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" }) ) : /* @__PURE__ */ React2.createElement( "svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", strokeWidth: "1", stroke: "currentColor", fill: "currentColor", className: "icon-svg-large" }, /* @__PURE__ */ React2.createElement("path", { d: "M8 8h16v16H8z" }) ) )), showPoweredBy && /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("div", { className: "powered-by" }, /* @__PURE__ */ React2.createElement( "svg", { width: "16", height: "16", viewBox: "0 0 72 72", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React2.createElement( "ellipse", { cx: "14.0868", cy: "59.2146", rx: "7.8261", ry: "7.7854", fill: "#2487D8" } ), /* @__PURE__ */ React2.createElement( "ellipse", { cx: "24.9013", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487