framepexls-ui-lib 2.2.0 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Checkbox.d.mts +1 -1
- package/dist/Checkbox.d.ts +1 -1
- package/dist/QuickActionsDock.d.mts +26 -0
- package/dist/QuickActionsDock.d.ts +26 -0
- package/dist/QuickActionsDock.js +99 -0
- package/dist/QuickActionsDock.mjs +79 -0
- package/dist/Sidebar.d.mts +2 -0
- package/dist/Sidebar.d.ts +2 -0
- package/dist/Sidebar.js +98 -42
- package/dist/Sidebar.mjs +98 -42
- package/dist/SupportChatWidget.d.mts +128 -0
- package/dist/SupportChatWidget.d.ts +128 -0
- package/dist/SupportChatWidget.js +696 -0
- package/dist/SupportChatWidget.mjs +678 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/index.mjs +148 -144
- package/dist/theme.css +1 -1
- package/package.json +11 -8
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import ActionIconButton from "./ActionIconButton.mjs";
|
|
5
|
+
import AvatarSquare from "./AvatarSquare.mjs";
|
|
6
|
+
import Badge from "./Badge.mjs";
|
|
7
|
+
import Button from "./Button.mjs";
|
|
8
|
+
import Textarea from "./Textarea.mjs";
|
|
9
|
+
import TimeAgo from "./TimeAgo.mjs";
|
|
10
|
+
import Tooltip from "./Tooltip.mjs";
|
|
11
|
+
import {
|
|
12
|
+
ChatCenteredDotsIcon,
|
|
13
|
+
ClockIcon,
|
|
14
|
+
CloseIcon,
|
|
15
|
+
DownloadSimpleIcon,
|
|
16
|
+
EnvelopeIcon,
|
|
17
|
+
HeadsetIcon,
|
|
18
|
+
PaperPlaneIcon,
|
|
19
|
+
PhoneIcon,
|
|
20
|
+
RobotIcon,
|
|
21
|
+
StarIcon,
|
|
22
|
+
StorefrontIcon
|
|
23
|
+
} from "./iconos/index.mjs";
|
|
24
|
+
function cx(...a) {
|
|
25
|
+
return a.filter(Boolean).join(" ");
|
|
26
|
+
}
|
|
27
|
+
function createId(prefix) {
|
|
28
|
+
return `${prefix}_${Math.random().toString(16).slice(2)}_${Date.now().toString(16)}`;
|
|
29
|
+
}
|
|
30
|
+
function Dots() {
|
|
31
|
+
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
32
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 animate-pulse rounded-full bg-current opacity-45" }),
|
|
33
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 animate-pulse rounded-full bg-current opacity-45 [animation-delay:150ms]" }),
|
|
34
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 animate-pulse rounded-full bg-current opacity-45 [animation-delay:300ms]" })
|
|
35
|
+
] });
|
|
36
|
+
}
|
|
37
|
+
function iconForContact(kind) {
|
|
38
|
+
if (kind === "phone") return /* @__PURE__ */ jsx(PhoneIcon, { "aria-hidden": true, className: "h-4 w-4" });
|
|
39
|
+
if (kind === "email") return /* @__PURE__ */ jsx(EnvelopeIcon, { "aria-hidden": true, className: "h-4 w-4" });
|
|
40
|
+
return /* @__PURE__ */ jsx(ChatCenteredDotsIcon, { "aria-hidden": true, className: "h-4 w-4" });
|
|
41
|
+
}
|
|
42
|
+
function statusTone(mode, liveAvailable) {
|
|
43
|
+
if (mode === "live") return liveAvailable ? "emerald" : "amber";
|
|
44
|
+
if (mode === "offline") return "amber";
|
|
45
|
+
return "sky";
|
|
46
|
+
}
|
|
47
|
+
function ratingText(rating, ratingCount) {
|
|
48
|
+
if (typeof rating !== "number" || Number.isNaN(rating)) return null;
|
|
49
|
+
const formatted = rating.toFixed(rating % 1 === 0 ? 0 : 1);
|
|
50
|
+
if (!ratingCount) return formatted;
|
|
51
|
+
return `${formatted} (${ratingCount})`;
|
|
52
|
+
}
|
|
53
|
+
function roleLabel(role) {
|
|
54
|
+
if (role === "visitor") return "Visitante";
|
|
55
|
+
if (role === "agent") return "Agente";
|
|
56
|
+
if (role === "bot") return "Bot";
|
|
57
|
+
return "Sistema";
|
|
58
|
+
}
|
|
59
|
+
function formatTranscriptDate(value) {
|
|
60
|
+
if (!value) return "";
|
|
61
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
62
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
63
|
+
return date.toLocaleString("es-MX", {
|
|
64
|
+
dateStyle: "medium",
|
|
65
|
+
timeStyle: "short"
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function escapeHtml(value) {
|
|
69
|
+
return String(value != null ? value : "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
70
|
+
}
|
|
71
|
+
function safeFileName(value) {
|
|
72
|
+
return String(value || "conversacion").trim().toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "conversacion";
|
|
73
|
+
}
|
|
74
|
+
function downloadFile(fileName, content, mimeType) {
|
|
75
|
+
const blob = new Blob([content], { type: mimeType });
|
|
76
|
+
const url = URL.createObjectURL(blob);
|
|
77
|
+
const a = document.createElement("a");
|
|
78
|
+
a.href = url;
|
|
79
|
+
a.download = fileName;
|
|
80
|
+
document.body.appendChild(a);
|
|
81
|
+
a.click();
|
|
82
|
+
a.remove();
|
|
83
|
+
window.setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
84
|
+
}
|
|
85
|
+
function buildTextTranscript(messages, title) {
|
|
86
|
+
const lines = [
|
|
87
|
+
title,
|
|
88
|
+
`Generado: ${formatTranscriptDate(/* @__PURE__ */ new Date())}`,
|
|
89
|
+
"",
|
|
90
|
+
...messages.map((message) => {
|
|
91
|
+
const at = formatTranscriptDate(message.createdAt);
|
|
92
|
+
const prefix = at ? `[${at}] ${roleLabel(message.role)}` : roleLabel(message.role);
|
|
93
|
+
return `${prefix}: ${message.content}`;
|
|
94
|
+
}),
|
|
95
|
+
""
|
|
96
|
+
];
|
|
97
|
+
return lines.join("\n");
|
|
98
|
+
}
|
|
99
|
+
function buildHtmlTranscript(messages, title, brandName) {
|
|
100
|
+
const body = messages.map((message) => {
|
|
101
|
+
var _a;
|
|
102
|
+
const mine = message.role === "visitor";
|
|
103
|
+
const system = message.role === "system";
|
|
104
|
+
const side = mine ? "right" : system ? "center" : "left";
|
|
105
|
+
const at = formatTranscriptDate(message.createdAt);
|
|
106
|
+
const actor = ((_a = message.actor) == null ? void 0 : _a.name) || roleLabel(message.role);
|
|
107
|
+
return `
|
|
108
|
+
<article class="msg ${side}">
|
|
109
|
+
<div class="bubble">
|
|
110
|
+
<div class="meta">${escapeHtml(actor)}${at ? ` \xB7 ${escapeHtml(at)}` : ""}</div>
|
|
111
|
+
<div class="content">${escapeHtml(message.content).replace(/\n/g, "<br />")}</div>
|
|
112
|
+
</div>
|
|
113
|
+
</article>
|
|
114
|
+
`;
|
|
115
|
+
}).join("\n");
|
|
116
|
+
return `<!doctype html>
|
|
117
|
+
<html lang="es">
|
|
118
|
+
<head>
|
|
119
|
+
<meta charset="utf-8" />
|
|
120
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
121
|
+
<title>${escapeHtml(title)}</title>
|
|
122
|
+
<style>
|
|
123
|
+
:root { color-scheme: light; --bg: #f6f8fb; --card: #ffffff; --fg: #111827; --muted: #64748b; --line: #e2e8f0; --primary: #155dfc; }
|
|
124
|
+
* { box-sizing: border-box; }
|
|
125
|
+
body { margin: 0; background: linear-gradient(180deg, #f8fafc, var(--bg)); color: var(--fg); font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
|
126
|
+
main { width: min(860px, calc(100vw - 32px)); margin: 40px auto; }
|
|
127
|
+
header { margin-bottom: 24px; padding: 24px; border: 1px solid var(--line); border-radius: 20px; background: var(--card); box-shadow: 0 18px 50px rgba(15, 23, 42, .08); }
|
|
128
|
+
.eyebrow { margin: 0 0 8px; color: var(--primary); font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; }
|
|
129
|
+
h1 { margin: 0; font-size: 28px; line-height: 1.15; }
|
|
130
|
+
.sub { margin: 10px 0 0; color: var(--muted); font-size: 14px; }
|
|
131
|
+
.thread { display: flex; flex-direction: column; gap: 12px; }
|
|
132
|
+
.msg { display: flex; }
|
|
133
|
+
.msg.left { justify-content: flex-start; }
|
|
134
|
+
.msg.right { justify-content: flex-end; }
|
|
135
|
+
.msg.center { justify-content: center; }
|
|
136
|
+
.bubble { max-width: 72%; padding: 12px 14px; border: 1px solid var(--line); border-radius: 18px; background: var(--card); box-shadow: 0 8px 24px rgba(15, 23, 42, .06); }
|
|
137
|
+
.right .bubble { background: #eaf1ff; border-color: #c8d9ff; }
|
|
138
|
+
.center .bubble { max-width: 84%; background: #fff7ed; border-color: #fed7aa; text-align: center; }
|
|
139
|
+
.meta { margin-bottom: 5px; color: var(--muted); font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
|
|
140
|
+
.content { font-size: 14px; line-height: 1.55; white-space: normal; }
|
|
141
|
+
footer { margin-top: 24px; color: var(--muted); font-size: 12px; text-align: center; }
|
|
142
|
+
@media (max-width: 640px) { main { margin-block: 18px; } .bubble { max-width: 88%; } h1 { font-size: 23px; } }
|
|
143
|
+
</style>
|
|
144
|
+
</head>
|
|
145
|
+
<body>
|
|
146
|
+
<main>
|
|
147
|
+
<header>
|
|
148
|
+
<p class="eyebrow">${escapeHtml(brandName)}</p>
|
|
149
|
+
<h1>${escapeHtml(title)}</h1>
|
|
150
|
+
<p class="sub">Transcripcion completa generada el ${escapeHtml(formatTranscriptDate(/* @__PURE__ */ new Date()))}</p>
|
|
151
|
+
</header>
|
|
152
|
+
<section class="thread">${body}</section>
|
|
153
|
+
<footer>Exportado desde ${escapeHtml(brandName)}</footer>
|
|
154
|
+
</main>
|
|
155
|
+
</body>
|
|
156
|
+
</html>`;
|
|
157
|
+
}
|
|
158
|
+
function SupportChatWidget({
|
|
159
|
+
mode = "bot",
|
|
160
|
+
brandName = "Kontestalo",
|
|
161
|
+
assistant,
|
|
162
|
+
agent,
|
|
163
|
+
open: openProp,
|
|
164
|
+
defaultOpen = false,
|
|
165
|
+
onOpenChange,
|
|
166
|
+
messages: messagesProp,
|
|
167
|
+
defaultMessages,
|
|
168
|
+
onMessagesChange,
|
|
169
|
+
onSendMessage,
|
|
170
|
+
onRequestLive,
|
|
171
|
+
onSelectService,
|
|
172
|
+
onQuickReply,
|
|
173
|
+
transcript,
|
|
174
|
+
serviceSuggestions,
|
|
175
|
+
suggestedReplies,
|
|
176
|
+
contactActions,
|
|
177
|
+
knowledgeSignals,
|
|
178
|
+
businessHoursLabel,
|
|
179
|
+
rating,
|
|
180
|
+
ratingCount,
|
|
181
|
+
liveAllowed = false,
|
|
182
|
+
liveAvailable = false,
|
|
183
|
+
allowOfflineMessages = true,
|
|
184
|
+
disabled = false,
|
|
185
|
+
unreadCount = 0,
|
|
186
|
+
position = "bottom-right",
|
|
187
|
+
zIndex = 1280,
|
|
188
|
+
className,
|
|
189
|
+
panelClassName,
|
|
190
|
+
labels
|
|
191
|
+
}) {
|
|
192
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H;
|
|
193
|
+
const [openState, setOpenState] = React.useState(defaultOpen);
|
|
194
|
+
const open = openProp != null ? openProp : openState;
|
|
195
|
+
const setOpen = (next) => {
|
|
196
|
+
if (openProp === void 0) setOpenState(next);
|
|
197
|
+
onOpenChange == null ? void 0 : onOpenChange(next);
|
|
198
|
+
};
|
|
199
|
+
const [messagesState, setMessagesState] = React.useState(() => defaultMessages != null ? defaultMessages : []);
|
|
200
|
+
const messages = messagesProp != null ? messagesProp : messagesState;
|
|
201
|
+
const messagesRef = React.useRef(messages);
|
|
202
|
+
React.useEffect(() => {
|
|
203
|
+
messagesRef.current = messages;
|
|
204
|
+
}, [messages]);
|
|
205
|
+
const setMessages = (next) => {
|
|
206
|
+
if (typeof next === "function") {
|
|
207
|
+
const fn = next;
|
|
208
|
+
if (messagesProp === void 0) {
|
|
209
|
+
setMessagesState((prev) => {
|
|
210
|
+
const computed2 = fn(prev);
|
|
211
|
+
messagesRef.current = computed2;
|
|
212
|
+
onMessagesChange == null ? void 0 : onMessagesChange(computed2);
|
|
213
|
+
return computed2;
|
|
214
|
+
});
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const computed = fn(messagesRef.current);
|
|
218
|
+
messagesRef.current = computed;
|
|
219
|
+
onMessagesChange == null ? void 0 : onMessagesChange(computed);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
messagesRef.current = next;
|
|
223
|
+
if (messagesProp === void 0) setMessagesState(next);
|
|
224
|
+
onMessagesChange == null ? void 0 : onMessagesChange(next);
|
|
225
|
+
};
|
|
226
|
+
const [text, setText] = React.useState("");
|
|
227
|
+
const [sending, setSending] = React.useState(false);
|
|
228
|
+
const [requestingLive, setRequestingLive] = React.useState(false);
|
|
229
|
+
const endRef = React.useRef(null);
|
|
230
|
+
const textareaRef = React.useRef(null);
|
|
231
|
+
const labelTrigger = (_a = labels == null ? void 0 : labels.trigger) != null ? _a : "Abrir chat";
|
|
232
|
+
const labelTitle = (_b = labels == null ? void 0 : labels.title) != null ? _b : brandName;
|
|
233
|
+
const labelSubtitle = (_c = labels == null ? void 0 : labels.subtitle) != null ? _c : "Asistencia para visitantes";
|
|
234
|
+
const labelBotStatus = (_d = labels == null ? void 0 : labels.botStatus) != null ? _d : "Bot activo";
|
|
235
|
+
const labelLiveStatus = (_e = labels == null ? void 0 : labels.liveStatus) != null ? _e : "Atencion en vivo";
|
|
236
|
+
const labelOfflineStatus = (_f = labels == null ? void 0 : labels.offlineStatus) != null ? _f : "Fuera de horario";
|
|
237
|
+
const labelComposer = (_g = labels == null ? void 0 : labels.composerPlaceholder) != null ? _g : "Escribe tu pregunta...";
|
|
238
|
+
const labelOfflineComposer = (_h = labels == null ? void 0 : labels.offlineComposerPlaceholder) != null ? _h : "Dejanos tu mensaje...";
|
|
239
|
+
const labelSend = (_i = labels == null ? void 0 : labels.send) != null ? _i : "Enviar";
|
|
240
|
+
const labelClose = (_j = labels == null ? void 0 : labels.close) != null ? _j : "Cerrar";
|
|
241
|
+
const labelRequestLive = (_k = labels == null ? void 0 : labels.requestLive) != null ? _k : "Hablar con una persona";
|
|
242
|
+
const labelLiveUnavailable = (_l = labels == null ? void 0 : labels.liveUnavailable) != null ? _l : "Atencion en vivo no disponible";
|
|
243
|
+
const labelServicesTitle = (_m = labels == null ? void 0 : labels.servicesTitle) != null ? _m : "Servicios sugeridos";
|
|
244
|
+
const labelSuggestedTitle = (_n = labels == null ? void 0 : labels.suggestedTitle) != null ? _n : "Preguntas rapidas";
|
|
245
|
+
const labelContactTitle = (_o = labels == null ? void 0 : labels.contactTitle) != null ? _o : "Contacto";
|
|
246
|
+
const labelSaveTranscript = (_p = labels == null ? void 0 : labels.saveTranscript) != null ? _p : "Guardar conversacion";
|
|
247
|
+
const labelSaveRecentTranscript = (_q = labels == null ? void 0 : labels.saveRecentTranscript) != null ? _q : "Guardar extracto";
|
|
248
|
+
const labelSaveFullTranscript = (_r = labels == null ? void 0 : labels.saveFullTranscript) != null ? _r : "Guardar HTML";
|
|
249
|
+
const labelRating = (_s = labels == null ? void 0 : labels.ratingLabel) != null ? _s : "Calificacion";
|
|
250
|
+
const labelPoweredBy = (_t = labels == null ? void 0 : labels.poweredBy) != null ? _t : "Kontestalo";
|
|
251
|
+
const labelWelcomeTitle = (_v = labels == null ? void 0 : labels.defaultWelcomeTitle) != null ? _v : `Hola, soy ${(_u = assistant == null ? void 0 : assistant.name) != null ? _u : brandName}`;
|
|
252
|
+
const labelWelcomeMessage = (_w = labels == null ? void 0 : labels.defaultWelcomeMessage) != null ? _w : "Puedo ayudarte con servicios, horarios, contacto y dudas frecuentes.";
|
|
253
|
+
const labelError = (_x = labels == null ? void 0 : labels.errorMessage) != null ? _x : "No se pudo enviar el mensaje. Intenta de nuevo.";
|
|
254
|
+
const currentActor = mode === "live" && agent ? agent : {
|
|
255
|
+
name: (_y = assistant == null ? void 0 : assistant.name) != null ? _y : brandName,
|
|
256
|
+
subtitle: (_z = assistant == null ? void 0 : assistant.subtitle) != null ? _z : labelSubtitle,
|
|
257
|
+
avatarUrl: (_A = assistant == null ? void 0 : assistant.avatarUrl) != null ? _A : null,
|
|
258
|
+
initials: (_B = assistant == null ? void 0 : assistant.initials) != null ? _B : "KO",
|
|
259
|
+
color: (_C = assistant == null ? void 0 : assistant.color) != null ? _C : "accent"
|
|
260
|
+
};
|
|
261
|
+
const positionClass = position === "inline" ? "relative" : position === "bottom-left" ? "fixed bottom-5 left-5" : "fixed bottom-5 right-5";
|
|
262
|
+
const panelPositionClass = position === "inline" ? "relative" : position === "bottom-left" ? "fixed bottom-20 left-5" : "fixed bottom-20 right-5";
|
|
263
|
+
const canRequestLive = Boolean(liveAllowed && liveAvailable && mode !== "live" && onRequestLive);
|
|
264
|
+
const composerDisabled = disabled || sending || !allowOfflineMessages && mode === "offline";
|
|
265
|
+
const statusLabel = mode === "live" ? labelLiveStatus : mode === "offline" ? labelOfflineStatus : labelBotStatus;
|
|
266
|
+
const ratingResolved = ratingText(rating, ratingCount);
|
|
267
|
+
const transcriptEnabled = (_D = transcript == null ? void 0 : transcript.enabled) != null ? _D : true;
|
|
268
|
+
const transcriptFullAllowed = Boolean(transcript == null ? void 0 : transcript.fullHistoryAllowed);
|
|
269
|
+
React.useEffect(() => {
|
|
270
|
+
if (!open) return;
|
|
271
|
+
window.setTimeout(() => {
|
|
272
|
+
var _a2;
|
|
273
|
+
return (_a2 = textareaRef.current) == null ? void 0 : _a2.focus();
|
|
274
|
+
}, 0);
|
|
275
|
+
}, [open]);
|
|
276
|
+
React.useEffect(() => {
|
|
277
|
+
var _a2;
|
|
278
|
+
if (!open) return;
|
|
279
|
+
(_a2 = endRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "end" });
|
|
280
|
+
}, [open, messages.length, sending]);
|
|
281
|
+
const appendBotResult = (result) => {
|
|
282
|
+
if (!result) return;
|
|
283
|
+
if (typeof result === "string") {
|
|
284
|
+
const replyMessage = {
|
|
285
|
+
id: createId("bot"),
|
|
286
|
+
role: mode === "live" ? "agent" : "bot",
|
|
287
|
+
content: result,
|
|
288
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
289
|
+
state: "complete",
|
|
290
|
+
actor: currentActor
|
|
291
|
+
};
|
|
292
|
+
setMessages((prev) => [...prev, replyMessage].slice(-200));
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (Array.isArray(result)) {
|
|
296
|
+
setMessages((prev) => [...prev, ...result].slice(-200));
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const next = [];
|
|
300
|
+
if (result.reply) {
|
|
301
|
+
next.push({
|
|
302
|
+
id: createId("bot"),
|
|
303
|
+
role: mode === "live" ? "agent" : "bot",
|
|
304
|
+
content: result.reply,
|
|
305
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
306
|
+
state: "complete",
|
|
307
|
+
actor: currentActor
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
if (Array.isArray(result.messages)) next.push(...result.messages);
|
|
311
|
+
if (next.length) setMessages((prev) => [...prev, ...next].slice(-200));
|
|
312
|
+
};
|
|
313
|
+
const send = async (raw) => {
|
|
314
|
+
const trimmed = String(raw != null ? raw : text).trim();
|
|
315
|
+
if (!trimmed || composerDisabled) return;
|
|
316
|
+
const visitorMessage = {
|
|
317
|
+
id: createId("visitor"),
|
|
318
|
+
role: "visitor",
|
|
319
|
+
content: trimmed,
|
|
320
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
321
|
+
state: "sending"
|
|
322
|
+
};
|
|
323
|
+
setText("");
|
|
324
|
+
setSending(true);
|
|
325
|
+
const base = [...messagesRef.current, visitorMessage];
|
|
326
|
+
setMessages(base);
|
|
327
|
+
try {
|
|
328
|
+
const result = await (onSendMessage == null ? void 0 : onSendMessage({ text: trimmed, messages: base, mode }));
|
|
329
|
+
setMessages((prev) => prev.map((m) => m.id === visitorMessage.id ? { ...m, state: "complete" } : m));
|
|
330
|
+
appendBotResult(result);
|
|
331
|
+
} catch {
|
|
332
|
+
const errorMessage = {
|
|
333
|
+
id: createId("system"),
|
|
334
|
+
role: "system",
|
|
335
|
+
content: labelError,
|
|
336
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
337
|
+
state: "error"
|
|
338
|
+
};
|
|
339
|
+
setMessages((prev) => [
|
|
340
|
+
...prev.map((m) => m.id === visitorMessage.id ? { ...m, state: "error" } : m),
|
|
341
|
+
errorMessage
|
|
342
|
+
].slice(-200));
|
|
343
|
+
} finally {
|
|
344
|
+
setSending(false);
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
const requestLive = async () => {
|
|
348
|
+
if (!canRequestLive || requestingLive) return;
|
|
349
|
+
setRequestingLive(true);
|
|
350
|
+
try {
|
|
351
|
+
await (onRequestLive == null ? void 0 : onRequestLive());
|
|
352
|
+
} finally {
|
|
353
|
+
setRequestingLive(false);
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
const saveTranscript = async () => {
|
|
357
|
+
var _a2, _b2, _c2, _d2;
|
|
358
|
+
const config = transcript != null ? transcript : {};
|
|
359
|
+
if (config.enabled === false) return;
|
|
360
|
+
const allMessages = messagesRef.current.filter((message) => {
|
|
361
|
+
var _a3;
|
|
362
|
+
return (_a3 = message.content) == null ? void 0 : _a3.trim();
|
|
363
|
+
});
|
|
364
|
+
if (!allMessages.length) return;
|
|
365
|
+
const fullAllowed = Boolean(config.fullHistoryAllowed);
|
|
366
|
+
const mode2 = fullAllowed ? "full-html" : "recent-text";
|
|
367
|
+
const limit = Math.max(1, (_a2 = config.recentMessageLimit) != null ? _a2 : 8);
|
|
368
|
+
const selectedMessages = fullAllowed ? allMessages : allMessages.slice(-limit);
|
|
369
|
+
const title = (_b2 = config.title) != null ? _b2 : `${brandName} - conversacion`;
|
|
370
|
+
const baseFileName = safeFileName((_c2 = config.fileName) != null ? _c2 : title);
|
|
371
|
+
const content = fullAllowed ? buildHtmlTranscript(selectedMessages, title, brandName) : buildTextTranscript(selectedMessages, `${title} (extracto final)`);
|
|
372
|
+
const fileName = `${baseFileName}.${fullAllowed ? "html" : "txt"}`;
|
|
373
|
+
const mimeType = fullAllowed ? "text/html;charset=utf-8" : "text/plain;charset=utf-8";
|
|
374
|
+
const payload = {
|
|
375
|
+
mode: mode2,
|
|
376
|
+
messages: selectedMessages,
|
|
377
|
+
fileName,
|
|
378
|
+
content,
|
|
379
|
+
mimeType
|
|
380
|
+
};
|
|
381
|
+
await ((_d2 = config.onSave) == null ? void 0 : _d2.call(config, payload));
|
|
382
|
+
downloadFile(fileName, content, mimeType);
|
|
383
|
+
};
|
|
384
|
+
const trigger = /* @__PURE__ */ jsx(Tooltip, { content: labelTrigger, placement: position === "bottom-left" ? "right" : "left", offset: 10, children: /* @__PURE__ */ jsx("div", { className: cx(positionClass, className), style: position === "inline" ? void 0 : { zIndex }, children: /* @__PURE__ */ jsxs(
|
|
385
|
+
"button",
|
|
386
|
+
{
|
|
387
|
+
type: "button",
|
|
388
|
+
"aria-label": labelTrigger,
|
|
389
|
+
onClick: () => setOpen(true),
|
|
390
|
+
className: cx(
|
|
391
|
+
"relative grid h-14 w-14 place-items-center rounded-2xl border border-[color-mix(in_oklab,var(--ring)_28%,var(--border))]",
|
|
392
|
+
"bg-[var(--primary)] text-[var(--primary-foreground)] shadow-xl transition hover:brightness-95 active:scale-[0.98]"
|
|
393
|
+
),
|
|
394
|
+
children: [
|
|
395
|
+
mode === "live" ? /* @__PURE__ */ jsx(HeadsetIcon, { "aria-hidden": true, className: "h-6 w-6" }) : /* @__PURE__ */ jsx(ChatCenteredDotsIcon, { "aria-hidden": true, className: "h-6 w-6" }),
|
|
396
|
+
unreadCount > 0 ? /* @__PURE__ */ jsx("span", { className: "absolute -right-1.5 -top-1.5 grid h-5 min-w-5 place-items-center rounded-full bg-[var(--danger)] px-1 text-[0.65rem] font-semibold text-white shadow ring-2 ring-[var(--card)]", children: unreadCount > 99 ? "99+" : unreadCount }) : null
|
|
397
|
+
]
|
|
398
|
+
}
|
|
399
|
+
) }) });
|
|
400
|
+
const renderService = (service) => /* @__PURE__ */ jsx(
|
|
401
|
+
"button",
|
|
402
|
+
{
|
|
403
|
+
type: "button",
|
|
404
|
+
onClick: () => onSelectService == null ? void 0 : onSelectService(service),
|
|
405
|
+
className: "w-full rounded-2xl border border-[var(--border)] bg-[color-mix(in_oklab,var(--card)_92%,transparent)] p-3 text-left shadow-sm transition hover:bg-[color-mix(in_oklab,var(--surface)_72%,transparent)]",
|
|
406
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
|
|
407
|
+
service.imageUrl ? /* @__PURE__ */ jsx(AvatarSquare, { size: 42, src: service.imageUrl, alt: service.title, radiusClass: "rounded-xl", className: "ring-0" }) : /* @__PURE__ */ jsx("div", { className: "grid h-[42px] w-[42px] shrink-0 place-items-center rounded-xl bg-[color-mix(in_oklab,var(--primary)_12%,var(--card))] text-[var(--primary)]", children: /* @__PURE__ */ jsx(StorefrontIcon, { "aria-hidden": true, className: "h-5 w-5" }) }),
|
|
408
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
409
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
410
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-sm font-semibold text-[var(--foreground)]", children: service.title }),
|
|
411
|
+
service.priceLabel ? /* @__PURE__ */ jsx("div", { className: "shrink-0 text-xs font-semibold text-[var(--primary)]", children: service.priceLabel }) : null
|
|
412
|
+
] }),
|
|
413
|
+
service.description ? /* @__PURE__ */ jsx("div", { className: "mt-1 line-clamp-2 text-xs leading-5 text-[var(--muted)]", children: service.description }) : null,
|
|
414
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-between gap-2", children: [
|
|
415
|
+
service.meta ? /* @__PURE__ */ jsx("span", { className: "truncate text-[0.6875rem] font-medium text-[var(--muted)]", children: service.meta }) : /* @__PURE__ */ jsx("span", {}),
|
|
416
|
+
service.ctaLabel ? /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[0.6875rem] font-semibold text-[var(--primary)]", children: service.ctaLabel }) : null
|
|
417
|
+
] })
|
|
418
|
+
] })
|
|
419
|
+
] })
|
|
420
|
+
},
|
|
421
|
+
service.id
|
|
422
|
+
);
|
|
423
|
+
const welcome = /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
424
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-2xl border border-[var(--border)] bg-[color-mix(in_oklab,var(--card)_94%,transparent)] p-4 shadow-sm", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
425
|
+
/* @__PURE__ */ jsx(
|
|
426
|
+
AvatarSquare,
|
|
427
|
+
{
|
|
428
|
+
size: 42,
|
|
429
|
+
src: (_E = currentActor.avatarUrl) != null ? _E : void 0,
|
|
430
|
+
alt: currentActor.name,
|
|
431
|
+
initials: currentActor.initials,
|
|
432
|
+
color: currentActor.color,
|
|
433
|
+
radiusClass: "rounded-xl",
|
|
434
|
+
className: "ring-0"
|
|
435
|
+
}
|
|
436
|
+
),
|
|
437
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
438
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-[var(--foreground)]", children: labelWelcomeTitle }),
|
|
439
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1 text-sm leading-6 text-[var(--muted)]", children: labelWelcomeMessage })
|
|
440
|
+
] })
|
|
441
|
+
] }) }),
|
|
442
|
+
businessHoursLabel || ratingResolved || (knowledgeSignals == null ? void 0 : knowledgeSignals.length) ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
443
|
+
businessHoursLabel ? /* @__PURE__ */ jsxs(Badge, { tone: mode === "offline" ? "amber" : "emerald", size: "sm", children: [
|
|
444
|
+
/* @__PURE__ */ jsx(ClockIcon, { "aria-hidden": true, className: "h-3.5 w-3.5" }),
|
|
445
|
+
businessHoursLabel
|
|
446
|
+
] }) : null,
|
|
447
|
+
ratingResolved ? /* @__PURE__ */ jsxs(Badge, { tone: "amber", size: "sm", children: [
|
|
448
|
+
/* @__PURE__ */ jsx(StarIcon, { "aria-hidden": true, filled: true, className: "h-3.5 w-3.5" }),
|
|
449
|
+
labelRating,
|
|
450
|
+
": ",
|
|
451
|
+
ratingResolved
|
|
452
|
+
] }) : null,
|
|
453
|
+
knowledgeSignals == null ? void 0 : knowledgeSignals.slice(0, 4).map((item) => /* @__PURE__ */ jsxs(Badge, { tone: "slate", size: "sm", children: [
|
|
454
|
+
item.label,
|
|
455
|
+
item.value ? `: ${item.value}` : ""
|
|
456
|
+
] }, item.id))
|
|
457
|
+
] }) : null,
|
|
458
|
+
(suggestedReplies == null ? void 0 : suggestedReplies.length) ? /* @__PURE__ */ jsxs("div", { children: [
|
|
459
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--muted)]", children: labelSuggestedTitle }),
|
|
460
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: suggestedReplies.slice(0, 6).map((reply) => /* @__PURE__ */ jsx(
|
|
461
|
+
"button",
|
|
462
|
+
{
|
|
463
|
+
type: "button",
|
|
464
|
+
onClick: () => {
|
|
465
|
+
onQuickReply == null ? void 0 : onQuickReply(reply);
|
|
466
|
+
void send(reply);
|
|
467
|
+
},
|
|
468
|
+
className: "rounded-full border border-[var(--border)] bg-[var(--card)] px-3 py-1.5 text-xs font-semibold text-[var(--foreground)] shadow-sm transition hover:bg-[color-mix(in_oklab,var(--surface)_72%,transparent)]",
|
|
469
|
+
children: reply
|
|
470
|
+
},
|
|
471
|
+
reply
|
|
472
|
+
)) })
|
|
473
|
+
] }) : null,
|
|
474
|
+
(serviceSuggestions == null ? void 0 : serviceSuggestions.length) ? /* @__PURE__ */ jsxs("div", { children: [
|
|
475
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--muted)]", children: labelServicesTitle }),
|
|
476
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: serviceSuggestions.slice(0, 4).map(renderService) })
|
|
477
|
+
] }) : null
|
|
478
|
+
] });
|
|
479
|
+
const messageList = messages.length ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
480
|
+
messages.map((message, index) => {
|
|
481
|
+
var _a2, _b2, _c2, _d2, _e2, _f2;
|
|
482
|
+
const mine = message.role === "visitor";
|
|
483
|
+
const system = message.role === "system";
|
|
484
|
+
const actor = (_a2 = message.actor) != null ? _a2 : message.role === "agent" ? agent : currentActor;
|
|
485
|
+
const prev = messages[index - 1];
|
|
486
|
+
const showAvatar = !mine && !system && (prev == null ? void 0 : prev.role) !== message.role;
|
|
487
|
+
const bubbleClass = system ? "mx-auto max-w-[92%] border-[color-mix(in_oklab,var(--warning)_28%,var(--border))] bg-[color-mix(in_oklab,var(--warning)_10%,transparent)] text-center text-[var(--foreground)]" : mine ? "border-[color-mix(in_oklab,var(--ring)_35%,var(--border))] bg-[color-mix(in_oklab,var(--primary)_14%,transparent)] text-[var(--foreground)]" : "border-[var(--border)] bg-[color-mix(in_oklab,var(--card)_94%,transparent)] text-[var(--foreground)]";
|
|
488
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("flex items-end gap-2", mine ? "justify-end" : system ? "justify-center" : "justify-start"), children: [
|
|
489
|
+
!mine && !system ? /* @__PURE__ */ jsx("div", { className: cx("shrink-0", showAvatar ? "" : "opacity-0"), children: /* @__PURE__ */ jsx(
|
|
490
|
+
AvatarSquare,
|
|
491
|
+
{
|
|
492
|
+
size: 28,
|
|
493
|
+
src: (_b2 = actor == null ? void 0 : actor.avatarUrl) != null ? _b2 : void 0,
|
|
494
|
+
alt: (_c2 = actor == null ? void 0 : actor.name) != null ? _c2 : brandName,
|
|
495
|
+
initials: actor == null ? void 0 : actor.initials,
|
|
496
|
+
color: actor == null ? void 0 : actor.color,
|
|
497
|
+
radiusClass: "rounded-lg",
|
|
498
|
+
className: "ring-0"
|
|
499
|
+
}
|
|
500
|
+
) }) : null,
|
|
501
|
+
/* @__PURE__ */ jsxs("div", { className: cx("max-w-[82%]", mine ? "items-end" : "items-start"), children: [
|
|
502
|
+
!mine && !system && showAvatar ? /* @__PURE__ */ jsx("div", { className: "mb-1 px-2 text-[0.6875rem] font-semibold text-[var(--muted)]", children: (_d2 = actor == null ? void 0 : actor.name) != null ? _d2 : brandName }) : null,
|
|
503
|
+
/* @__PURE__ */ jsxs("div", { className: cx("rounded-2xl border px-3.5 py-2.5 text-sm leading-6 shadow-sm", bubbleClass, message.state === "error" ? "border-[color-mix(in_oklab,var(--danger)_35%,var(--border))]" : ""), children: [
|
|
504
|
+
message.state === "typing" ? /* @__PURE__ */ jsx(Dots, {}) : /* @__PURE__ */ jsx("span", { className: "whitespace-pre-wrap", children: message.content }),
|
|
505
|
+
((_e2 = message.suggestedReplies) == null ? void 0 : _e2.length) ? /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: message.suggestedReplies.slice(0, 4).map((reply) => /* @__PURE__ */ jsx(
|
|
506
|
+
"button",
|
|
507
|
+
{
|
|
508
|
+
type: "button",
|
|
509
|
+
onClick: () => {
|
|
510
|
+
onQuickReply == null ? void 0 : onQuickReply(reply);
|
|
511
|
+
void send(reply);
|
|
512
|
+
},
|
|
513
|
+
className: "rounded-full border border-[var(--border)] bg-[var(--card)] px-3 py-1.5 text-xs font-semibold text-[var(--foreground)] shadow-sm transition hover:bg-[color-mix(in_oklab,var(--surface)_72%,transparent)]",
|
|
514
|
+
children: reply
|
|
515
|
+
},
|
|
516
|
+
`${message.id}:${reply}`
|
|
517
|
+
)) }) : null,
|
|
518
|
+
((_f2 = message.serviceSuggestions) == null ? void 0 : _f2.length) ? /* @__PURE__ */ jsx("div", { className: "mt-3 space-y-2", children: message.serviceSuggestions.slice(0, 3).map(renderService) }) : null
|
|
519
|
+
] }),
|
|
520
|
+
message.createdAt ? /* @__PURE__ */ jsx("div", { className: cx("mt-1 px-2 text-[0.6875rem] text-[var(--muted)]", mine ? "text-right" : "text-left"), children: /* @__PURE__ */ jsx(TimeAgo, { iso: message.createdAt }) }) : null
|
|
521
|
+
] })
|
|
522
|
+
] }, message.id);
|
|
523
|
+
}),
|
|
524
|
+
sending ? /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
525
|
+
/* @__PURE__ */ jsx(
|
|
526
|
+
AvatarSquare,
|
|
527
|
+
{
|
|
528
|
+
size: 28,
|
|
529
|
+
src: (_F = currentActor.avatarUrl) != null ? _F : void 0,
|
|
530
|
+
alt: currentActor.name,
|
|
531
|
+
initials: currentActor.initials,
|
|
532
|
+
color: currentActor.color,
|
|
533
|
+
radiusClass: "rounded-lg",
|
|
534
|
+
className: "ring-0"
|
|
535
|
+
}
|
|
536
|
+
),
|
|
537
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-2xl border border-[var(--border)] bg-[color-mix(in_oklab,var(--card)_94%,transparent)] px-3.5 py-2.5 text-sm text-[var(--muted)] shadow-sm", children: /* @__PURE__ */ jsx(Dots, {}) })
|
|
538
|
+
] }) : null,
|
|
539
|
+
/* @__PURE__ */ jsx("div", { ref: endRef })
|
|
540
|
+
] }) : welcome;
|
|
541
|
+
const panel = open ? /* @__PURE__ */ jsxs(
|
|
542
|
+
"div",
|
|
543
|
+
{
|
|
544
|
+
className: cx(
|
|
545
|
+
panelPositionClass,
|
|
546
|
+
"flex h-[min(680px,calc(100vh-7rem))] w-[min(400px,calc(100vw-2rem))] flex-col overflow-hidden rounded-2xl border border-[var(--border)] bg-[var(--card)] text-[var(--foreground)] shadow-2xl",
|
|
547
|
+
panelClassName
|
|
548
|
+
),
|
|
549
|
+
style: position === "inline" ? void 0 : { zIndex },
|
|
550
|
+
role: "dialog",
|
|
551
|
+
"aria-label": labelTitle,
|
|
552
|
+
children: [
|
|
553
|
+
/* @__PURE__ */ jsx("div", { className: "border-b border-[var(--border)] bg-[color-mix(in_oklab,var(--card)_96%,transparent)] px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
554
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
|
|
555
|
+
/* @__PURE__ */ jsx(
|
|
556
|
+
AvatarSquare,
|
|
557
|
+
{
|
|
558
|
+
size: 42,
|
|
559
|
+
src: (_G = currentActor.avatarUrl) != null ? _G : void 0,
|
|
560
|
+
alt: currentActor.name,
|
|
561
|
+
initials: currentActor.initials,
|
|
562
|
+
color: currentActor.color,
|
|
563
|
+
radiusClass: "rounded-xl",
|
|
564
|
+
className: "ring-0"
|
|
565
|
+
}
|
|
566
|
+
),
|
|
567
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
568
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-sm font-semibold text-[var(--foreground)]", children: labelTitle }),
|
|
569
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1 flex items-center gap-2", children: /* @__PURE__ */ jsx(Badge, { tone: statusTone(mode, liveAvailable), size: "sm", children: statusLabel }) })
|
|
570
|
+
] })
|
|
571
|
+
] }),
|
|
572
|
+
/* @__PURE__ */ jsx(ActionIconButton, { title: labelClose, size: "sm", onClick: () => setOpen(false), className: "border border-[var(--border)] bg-[var(--card)] shadow-sm", children: /* @__PURE__ */ jsx(CloseIcon, { "aria-hidden": true, className: "h-4 w-4" }) })
|
|
573
|
+
] }) }),
|
|
574
|
+
canRequestLive || liveAllowed && !liveAvailable && mode !== "live" ? /* @__PURE__ */ jsx("div", { className: "border-b border-[var(--border)] bg-[color-mix(in_oklab,var(--surface)_65%,transparent)] px-4 py-3", children: canRequestLive ? /* @__PURE__ */ jsx(
|
|
575
|
+
Button,
|
|
576
|
+
{
|
|
577
|
+
size: "sm",
|
|
578
|
+
variant: "secondary",
|
|
579
|
+
block: true,
|
|
580
|
+
loading: requestingLive,
|
|
581
|
+
onClick: () => void requestLive(),
|
|
582
|
+
leftIcon: /* @__PURE__ */ jsx(HeadsetIcon, { "aria-hidden": true, className: "h-4 w-4" }),
|
|
583
|
+
children: labelRequestLive
|
|
584
|
+
}
|
|
585
|
+
) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs font-medium text-[var(--muted)]", children: [
|
|
586
|
+
/* @__PURE__ */ jsx(ClockIcon, { "aria-hidden": true, className: "h-4 w-4" }),
|
|
587
|
+
labelLiveUnavailable
|
|
588
|
+
] }) }) : null,
|
|
589
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto bg-[linear-gradient(180deg,color-mix(in_oklab,var(--surface)_48%,transparent),transparent_34%)] px-4 py-4", children: messageList }),
|
|
590
|
+
(contactActions == null ? void 0 : contactActions.length) ? /* @__PURE__ */ jsxs("div", { className: "border-t border-[var(--border)] bg-[color-mix(in_oklab,var(--surface)_58%,transparent)] px-4 py-3", children: [
|
|
591
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--muted)]", children: labelContactTitle }),
|
|
592
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-2 overflow-x-auto pb-1", children: contactActions.map((action) => {
|
|
593
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
594
|
+
iconForContact(action.kind),
|
|
595
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: action.label })
|
|
596
|
+
] });
|
|
597
|
+
const classNameAction = "inline-flex h-9 shrink-0 items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--card)] px-3 text-xs font-semibold text-[var(--foreground)] shadow-sm transition hover:bg-[color-mix(in_oklab,var(--surface)_72%,transparent)]";
|
|
598
|
+
if (action.href) {
|
|
599
|
+
return /* @__PURE__ */ jsx("a", { href: action.href, className: classNameAction, onClick: () => {
|
|
600
|
+
var _a2;
|
|
601
|
+
return (_a2 = action.onSelect) == null ? void 0 : _a2.call(action);
|
|
602
|
+
}, children: content }, action.id);
|
|
603
|
+
}
|
|
604
|
+
return /* @__PURE__ */ jsx("button", { type: "button", className: classNameAction, onClick: () => {
|
|
605
|
+
var _a2;
|
|
606
|
+
return (_a2 = action.onSelect) == null ? void 0 : _a2.call(action);
|
|
607
|
+
}, children: content }, action.id);
|
|
608
|
+
}) })
|
|
609
|
+
] }) : null,
|
|
610
|
+
/* @__PURE__ */ jsxs("div", { className: "border-t border-[var(--border)] bg-[var(--card)] px-4 py-3", children: [
|
|
611
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
612
|
+
/* @__PURE__ */ jsx(
|
|
613
|
+
Textarea,
|
|
614
|
+
{
|
|
615
|
+
ref: textareaRef,
|
|
616
|
+
value: text,
|
|
617
|
+
onChange: (e) => setText(e.currentTarget.value),
|
|
618
|
+
placeholder: mode === "offline" ? labelOfflineComposer : labelComposer,
|
|
619
|
+
disabled: composerDisabled,
|
|
620
|
+
rows: 2,
|
|
621
|
+
className: "max-h-28 min-h-[44px] flex-1 resize-none rounded-xl",
|
|
622
|
+
onKeyDown: (e) => {
|
|
623
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
624
|
+
e.preventDefault();
|
|
625
|
+
void send();
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
),
|
|
630
|
+
/* @__PURE__ */ jsx(
|
|
631
|
+
ActionIconButton,
|
|
632
|
+
{
|
|
633
|
+
title: labelSend,
|
|
634
|
+
size: "lg",
|
|
635
|
+
color: "blue",
|
|
636
|
+
disabled: !text.trim() || composerDisabled,
|
|
637
|
+
loading: sending,
|
|
638
|
+
onClick: () => void send(),
|
|
639
|
+
className: "border border-[color-mix(in_oklab,var(--ring)_30%,var(--border))] bg-[var(--primary)] text-[var(--primary-foreground)] shadow-sm",
|
|
640
|
+
children: /* @__PURE__ */ jsx(PaperPlaneIcon, { "aria-hidden": true, className: "h-5 w-5" })
|
|
641
|
+
}
|
|
642
|
+
)
|
|
643
|
+
] }),
|
|
644
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-between gap-3 text-[0.6875rem] text-[var(--muted)]", children: [
|
|
645
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1.5 truncate", children: [
|
|
646
|
+
mode === "live" ? /* @__PURE__ */ jsx(HeadsetIcon, { "aria-hidden": true, className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(RobotIcon, { "aria-hidden": true, className: "h-3.5 w-3.5" }),
|
|
647
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: (_H = currentActor.subtitle) != null ? _H : labelSubtitle })
|
|
648
|
+
] }),
|
|
649
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
650
|
+
transcriptEnabled ? /* @__PURE__ */ jsxs(
|
|
651
|
+
"button",
|
|
652
|
+
{
|
|
653
|
+
type: "button",
|
|
654
|
+
onClick: () => void saveTranscript(),
|
|
655
|
+
disabled: !messages.length,
|
|
656
|
+
title: labelSaveTranscript,
|
|
657
|
+
className: "inline-flex items-center gap-1.5 rounded-lg px-1.5 py-1 font-semibold text-[var(--muted)] transition hover:bg-[color-mix(in_oklab,var(--surface)_72%,transparent)] hover:text-[var(--foreground)] disabled:cursor-not-allowed disabled:opacity-45",
|
|
658
|
+
children: [
|
|
659
|
+
/* @__PURE__ */ jsx(DownloadSimpleIcon, { "aria-hidden": true, className: "h-3.5 w-3.5" }),
|
|
660
|
+
/* @__PURE__ */ jsx("span", { children: transcriptFullAllowed ? labelSaveFullTranscript : labelSaveRecentTranscript })
|
|
661
|
+
]
|
|
662
|
+
}
|
|
663
|
+
) : null,
|
|
664
|
+
/* @__PURE__ */ jsx("span", { children: labelPoweredBy })
|
|
665
|
+
] })
|
|
666
|
+
] })
|
|
667
|
+
] })
|
|
668
|
+
]
|
|
669
|
+
}
|
|
670
|
+
) : null;
|
|
671
|
+
if (position === "inline") {
|
|
672
|
+
return /* @__PURE__ */ jsx("div", { className, children: open ? panel : trigger });
|
|
673
|
+
}
|
|
674
|
+
return /* @__PURE__ */ jsx(Fragment, { children: open ? panel : trigger });
|
|
675
|
+
}
|
|
676
|
+
export {
|
|
677
|
+
SupportChatWidget as default
|
|
678
|
+
};
|