llmasaservice-ui
Version:
Prebuilt UI components for LLMAsAService.io
1,186 lines (1,182 loc) • 65 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
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 __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
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());
});
};
// index.ts
var llmasaservice_ui_exports = {};
__export(llmasaservice_ui_exports, {
AgentPanel: () => AgentPanel_default,
ChatPanel: () => ChatPanel_default
});
module.exports = __toCommonJS(llmasaservice_ui_exports);
// src/ChatPanel.tsx
var import_llmasaservice_client = require("llmasaservice-client");
var import_react2 = __toESM(require("react"));
var import_react_markdown = __toESM(require("react-markdown"));
var import_rehype_raw = __toESM(require("rehype-raw"));
var import_server = __toESM(require("react-dom/server"));
var import_remark_gfm = __toESM(require("remark-gfm"));
var import_react_syntax_highlighter = require("react-syntax-highlighter");
var import_material_dark = __toESM(require("react-syntax-highlighter/dist/esm/styles/prism/material-dark.js"));
var import_material_light = __toESM(require("react-syntax-highlighter/dist/esm/styles/prism/material-light.js"));
// src/EmailModal.tsx
var import_react = __toESM(require("react"));
var EmailModal = ({ isOpen, onClose, onSend, defaultEmail }) => {
const [email, setEmail] = (0, import_react.useState)("");
const [emailFrom, setEmailFrom] = (0, import_react.useState)(defaultEmail || "");
const handleSend = () => {
onSend(email, emailFrom);
onClose();
};
if (!isOpen) return null;
return /* @__PURE__ */ import_react.default.createElement("div", { className: "modal-overlay" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "modal-content" }, /* @__PURE__ */ import_react.default.createElement("p", { className: "modal-text" }, "Email Addresses", /* @__PURE__ */ import_react.default.createElement("br", null), " (If multiple, comma separate them)"), /* @__PURE__ */ import_react.default.createElement("p", null, /* @__PURE__ */ import_react.default.createElement(
"input",
{
type: "email",
width: "100%",
value: email,
onChange: (e) => setEmail(e.target.value),
placeholder: "To email address"
}
)), /* @__PURE__ */ import_react.default.createElement("p", null, /* @__PURE__ */ import_react.default.createElement(
"input",
{
type: "email",
width: "100%",
value: emailFrom,
onChange: (e) => setEmailFrom(e.target.value),
placeholder: "From email address (optional)"
}
)), /* @__PURE__ */ import_react.default.createElement("div", { className: "modal-buttons" }, /* @__PURE__ */ import_react.default.createElement("button", { onClick: onClose }, "Cancel"), /* @__PURE__ */ import_react.default.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" ? import_material_light.default : import_material_dark.default,
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] = (0, import_react2.useState)("");
const [lastController, setLastController] = (0, import_react2.useState)(new AbortController());
const [history, setHistory] = (0, import_react2.useState)(initialHistory);
const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
const [lastPrompt, setLastPrompt] = (0, import_react2.useState)(null);
const [lastKey, setLastKey] = (0, import_react2.useState)(null);
const [hasScroll, setHasScroll] = (0, import_react2.useState)(false);
const bottomRef = (0, import_react2.useRef)(null);
const bottomPanelRef = (0, import_react2.useRef)(null);
const [isAtBottom, setIsAtBottom] = (0, import_react2.useState)(true);
const [isEmailModalOpen, setIsEmailModalOpen] = (0, import_react2.useState)(false);
const [currentConversation, setCurrentConversation] = (0, import_react2.useState)(
conversation
);
const [emailInput, setEmailInput] = (0, import_react2.useState)(
(_a = customer == null ? void 0 : customer.customer_user_email) != null ? _a : ""
);
const [emailInputSet, setEmailInputSet] = (0, import_react2.useState)(
isEmailAddress(emailInput)
);
const [emailValid, setEmailValid] = (0, import_react2.useState)(true);
const [showEmailPanel, setShowEmailPanel] = (0, import_react2.useState)(
customerEmailCaptureMode !== "HIDE"
);
const [callToActionSent, setCallToActionSent] = (0, import_react2.useState)(false);
const [CTAClickedButNoEmail, setCTAClickedButNoEmail] = (0, import_react2.useState)(false);
const [emailSent, setEmailSent] = (0, import_react2.useState)(false);
const [emailClickedButNoEmail, setEmailClickedButNoEmail] = (0, import_react2.useState)(false);
const handleSendEmail = (to, from) => {
sendConversationsViaEmail(to, `Conversation History from ${title}`, from);
interactionClicked(lastCallId, "email", to);
setEmailSent(true);
};
const [currentCustomer, setCurrentCustomer] = (0, import_react2.useState)(customer);
const { send, response, idle, stop, lastCallId } = (0, import_llmasaservice_client.useLLM)({
project_id,
customer: currentCustomer,
url,
agent
});
const responseAreaRef = (0, import_react2.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";
}
(0, import_react2.useEffect)(() => {
setShowEmailPanel(customerEmailCaptureMode !== "HIDE");
if (customerEmailCaptureMode === "REQUIRED") {
if (!isEmailAddress(emailInput)) {
setEmailValid(false);
}
}
}, [customerEmailCaptureMode]);
(0, import_react2.useEffect)(() => {
if (responseCompleteCallback) {
if (lastCallId && lastCallId !== "" && idle)
responseCompleteCallback(lastCallId, lastPrompt != null ? lastPrompt : "", response);
}
}, [idle]);
(0, import_react2.useEffect)(() => {
if (Object.keys(initialHistory).length === 0) return;
setHistory(initialHistory);
}, [initialHistory]);
(0, import_react2.useEffect)(() => {
if (!conversation || conversation === "") return;
setCurrentConversation(conversation);
setHistory(initialHistory);
}, [conversation]);
(0, import_react2.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]);
(0, import_react2.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"
};
}
};
(0, import_react2.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]);
(0, import_react2.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]);
(0, import_react2.useEffect)(() => {
if (historyChangedCallback) {
historyChangedCallback(history);
}
}, [history, historyChangedCallback]);
(0, import_react2.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);
};
(0, import_react2.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__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(
"div",
{
style: {
border: 0,
padding: 0,
height: "16px",
display: "flex",
justifyContent: "space-between",
alignItems: "center"
}
},
/* @__PURE__ */ import_react2.default.createElement("span", null, match ? match[1] : "Unknown"),
/* @__PURE__ */ import_react2.default.createElement(
"button",
{
onClick: () => copyToClipboard(children),
className: "copy-button"
},
/* @__PURE__ */ import_react2.default.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 320 320",
fill: "currentColor",
className: "icon-svg"
},
/* @__PURE__ */ import_react2.default.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__ */ import_react2.default.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__ */ import_react2.default.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__ */ import_react2.default.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__ */ import_react2.default.createElement(
import_react_syntax_highlighter.Prism,
__spreadValues({
style: prismStyle,
PreTag: "div",
language: match[1]
}, props),
String(children).replace(/\n$/, "")
)) : /* @__PURE__ */ import_react2.default.createElement("code", __spreadValues({ className: className ? className : "" }, props), children);
};
const CustomLink = (_d) => {
var _e = _d, { href, children } = _e, props = __objRest(_e, ["href", "children"]);
return /* @__PURE__ */ import_react2.default.createElement("a", __spreadValues({ href, target: "_blank", rel: "noopener noreferrer" }, props), children);
};
const convertMarkdownToHTML = (markdown) => {
const html = import_server.default.renderToStaticMarkup(
/* @__PURE__ */ import_react2.default.createElement(
import_react_markdown.default,
{
className: markdownClass,
remarkPlugins: [import_remark_gfm.default],
rehypePlugins: [import_rehype_raw.default]
},
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__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(
"div",
{
style: { width, height },
className: "llm-panel" + (theme === "light" ? "" : " dark-theme")
},
title && title !== "" ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "title" }, title) : null,
/* @__PURE__ */ import_react2.default.createElement("div", { className: "responseArea", ref: responseAreaRef }, initialMessage && initialMessage !== "" ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "history-entry" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "response" }, /* @__PURE__ */ import_react2.default.createElement(
import_react_markdown.default,
{
className: markdownClass,
remarkPlugins: [import_remark_gfm.default],
rehypePlugins: [import_rehype_raw.default]
},
initialMessage
))) : null, Object.entries(history).map(([prompt, response2], index) => /* @__PURE__ */ import_react2.default.createElement("div", { className: "history-entry", key: index }, hideInitialPrompt && index === 0 ? null : /* @__PURE__ */ import_react2.default.createElement("div", { className: "prompt" }, formatPromptForDisplay(prompt)), /* @__PURE__ */ import_react2.default.createElement("div", { className: "response" }, index === Object.keys(history).length - 1 && isLoading ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "loading-text" }, "loading...") : null, /* @__PURE__ */ import_react2.default.createElement(
import_react_markdown.default,
{
className: markdownClass,
remarkPlugins: [import_remark_gfm.default],
rehypePlugins: [import_rehype_raw.default],
components: {
/*a: CustomLink,*/
code: CodeBlock
}
},
response2.content
), /* @__PURE__ */ import_react2.default.createElement("div", { className: "button-container" }, /* @__PURE__ */ import_react2.default.createElement(
"button",
{
className: "copy-button",
onClick: () => {
copyToClipboard(response2.content);
},
disabled: isDisabledDueToNoEmail()
},
/* @__PURE__ */ import_react2.default.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 320 320",
fill: "currentColor",
className: "icon-svg"
},
/* @__PURE__ */ import_react2.default.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__ */ import_react2.default.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__ */ import_react2.default.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__ */ import_react2.default.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__ */ import_react2.default.createElement(
"button",
{
className: "thumbs-button",
onClick: () => {
if (thumbsUpClick) thumbsUpClick(response2.callId);
interactionClicked(response2.callId, "thumbsup");
},
disabled: isDisabledDueToNoEmail()
},
/* @__PURE__ */ import_react2.default.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 20 20",
fill: "currentColor",
className: "icon-svg"
},
/* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement(
"button",
{
className: "thumbs-button",
onClick: () => {
if (thumbsDownClick) thumbsDownClick(response2.callId);
interactionClicked(response2.callId, "thumbsdown");
},
disabled: isDisabledDueToNoEmail()
},
/* @__PURE__ */ import_react2.default.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 20 20",
fill: "currentColor",
className: "icon-svg"
},
/* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement("div", { className: "suggestions-container" }, followOnQuestions.map((question, index) => /* @__PURE__ */ import_react2.default.createElement(
"button",
{
key: index,
className: "suggestion-button",
onClick: () => handleSuggestionClick(question),
disabled: !idle || isDisabledDueToNoEmail()
},
question
))), /* @__PURE__ */ import_react2.default.createElement("div", { ref: bottomRef }), hasScroll && !isAtBottom && /* @__PURE__ */ import_react2.default.createElement("button", { className: "scroll-button", onClick: scrollToBottom }, "\u2193")),
showEmailPanel && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, !emailValid && /* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement("div", { className: "email-input-container" }, /* @__PURE__ */ import_react2.default.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__ */ import_react2.default.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__ */ import_react2.default.createElement("div", { className: "button-container-actions" }, showSaveButton && /* @__PURE__ */ import_react2.default.createElement(
"button",
{
className: "save-button",
onClick: () => {
saveHTMLToFile(
convertHistoryToHTML(history),
`conversation-${(/* @__PURE__ */ new Date()).toISOString()}.html`
);
interactionClicked(lastCallId, "save");
},
disabled: isDisabledDueToNoEmail()
},
"Save Conversation"
), showEmailButton && /* @__PURE__ */ import_react2.default.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 ? "