@adatechnology/conversations-ui 0.1.0-rc.20 → 0.1.0-rc.21
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/{chunk-OIDAIVCH.js → chunk-TV4OQRGH.js} +636 -434
- package/dist/index.d.ts +142 -9
- package/dist/index.js +374 -66
- package/dist/preview/index.d.ts +100 -3
- package/dist/preview/index.js +89 -7
- package/dist/styles.css +30 -0
- package/dist/{types-O7kMP1Yn.d.ts → types-C6A_9edv.d.ts} +42 -2
- package/package.json +2 -2
- package/src/AudioTranscription.test.tsx +115 -0
- package/src/AudioTranscription.tsx +249 -0
- package/src/ConversationContextPanel.tsx +205 -52
- package/src/ConversationLocalesProvider.tsx +28 -0
- package/src/MediaRenderer.tsx +37 -6
- package/src/MessageBubble.tsx +26 -3
- package/src/MessageComposer.tsx +21 -2
- package/src/Wallpaper.tsx +27 -13
- package/src/conversationTranscript.test.ts +57 -0
- package/src/conversationTranscript.ts +29 -4
- package/src/hooks/useScrollToLatestMessage.ts +127 -0
- package/src/index.ts +11 -0
- package/src/preview/ConversationPreview.tsx +13 -4
- package/src/preview/createPreviewBridgeClient.ts +37 -1
- package/src/preview/createPreviewMediaUploader.ts +82 -0
- package/src/preview/createPreviewWebhookClient.test.ts +89 -0
- package/src/preview/createPreviewWebhookClient.ts +91 -0
- package/src/preview/index.ts +6 -0
- package/src/preview/previewMediaUploader.test.ts +61 -0
- package/src/providers/types.ts +12 -1
- package/src/settings/TranscriptionSettingsForm.test.tsx +81 -0
- package/src/settings/TranscriptionSettingsForm.tsx +189 -0
- package/src/styles.css +37 -0
- package/src/types.ts +26 -0
|
@@ -28,6 +28,19 @@ var DEFAULT_LOCALES = {
|
|
|
28
28
|
untitledDocument: "Documento",
|
|
29
29
|
downloadFile: "Baixar"
|
|
30
30
|
},
|
|
31
|
+
transcription: {
|
|
32
|
+
label: "Transcri\xE7\xE3o",
|
|
33
|
+
copy: "Copiar",
|
|
34
|
+
copied: "Copiado!",
|
|
35
|
+
transcribe: "Transcrever \xE1udio",
|
|
36
|
+
transcribing: "Transcrevendo...",
|
|
37
|
+
retry: "Transcrever novamente",
|
|
38
|
+
failed: "Falha ao transcrever \u2014 tentar novamente",
|
|
39
|
+
empty: "Sem fala detectada",
|
|
40
|
+
unsupported: "Formato de \xE1udio n\xE3o suportado para transcri\xE7\xE3o",
|
|
41
|
+
showMore: "ver transcri\xE7\xE3o completa",
|
|
42
|
+
showLess: "ver menos"
|
|
43
|
+
},
|
|
31
44
|
selection: {
|
|
32
45
|
select: "Selecionar"
|
|
33
46
|
},
|
|
@@ -40,6 +53,7 @@ var ConversationLocalesContext = createContext(DEFAULT_LOCALES);
|
|
|
40
53
|
function ConversationLocalesProvider({ children, locales }) {
|
|
41
54
|
const merged = {
|
|
42
55
|
bubble: { ...DEFAULT_LOCALES.bubble, ...locales?.bubble },
|
|
56
|
+
transcription: { ...DEFAULT_LOCALES.transcription, ...locales?.transcription },
|
|
43
57
|
selection: { ...DEFAULT_LOCALES.selection, ...locales?.selection },
|
|
44
58
|
dateDivider: { ...DEFAULT_LOCALES.dateDivider, ...locales?.dateDivider }
|
|
45
59
|
};
|
|
@@ -166,36 +180,183 @@ function AudioPlayer({ src, isMine = false }) {
|
|
|
166
180
|
] });
|
|
167
181
|
}
|
|
168
182
|
|
|
183
|
+
// src/AudioTranscription.tsx
|
|
184
|
+
import { useCallback, useState as useState2 } from "react";
|
|
185
|
+
import { Check, Copy, FileText, Loader2, RefreshCw } from "lucide-react";
|
|
186
|
+
|
|
187
|
+
// src/lib/cn.ts
|
|
188
|
+
import { clsx } from "clsx";
|
|
189
|
+
import { twMerge } from "tailwind-merge";
|
|
190
|
+
function cn(...inputs) {
|
|
191
|
+
return twMerge(clsx(inputs));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/AudioTranscription.tsx
|
|
195
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
196
|
+
var COPIED_FEEDBACK_MS = 2e3;
|
|
197
|
+
var COLLAPSE_ABOVE_CHARS = 320;
|
|
198
|
+
var COLLAPSED_LINE_CLAMP = 4;
|
|
199
|
+
function AudioTranscription({ transcription, onTranscribe, isMine = false }) {
|
|
200
|
+
const { transcription: locales } = useConversationLocales();
|
|
201
|
+
const [hasCopied, setHasCopied] = useState2(false);
|
|
202
|
+
const [isTranscribing, setIsTranscribing] = useState2(false);
|
|
203
|
+
const [hasRequestFailed, setHasRequestFailed] = useState2(false);
|
|
204
|
+
const [justTranscribed, setJustTranscribed] = useState2();
|
|
205
|
+
const [isExpanded, setIsExpanded] = useState2(false);
|
|
206
|
+
const effective = justTranscribed ?? transcription;
|
|
207
|
+
const text = effective?.text?.trim() ?? "";
|
|
208
|
+
const status = effective?.status;
|
|
209
|
+
const isLong = text.length > COLLAPSE_ABOVE_CHARS;
|
|
210
|
+
const isTruncated = isLong && !isExpanded;
|
|
211
|
+
const isDone = status === "done";
|
|
212
|
+
const hasText = isDone && text.length > 0;
|
|
213
|
+
const isSilent = isDone && text.length === 0;
|
|
214
|
+
const handleCopy = useCallback(async () => {
|
|
215
|
+
if (!text) return;
|
|
216
|
+
try {
|
|
217
|
+
await navigator.clipboard.writeText(text);
|
|
218
|
+
setHasCopied(true);
|
|
219
|
+
setTimeout(() => setHasCopied(false), COPIED_FEEDBACK_MS);
|
|
220
|
+
} catch {
|
|
221
|
+
}
|
|
222
|
+
}, [text]);
|
|
223
|
+
const handleTranscribe = useCallback(async () => {
|
|
224
|
+
if (!onTranscribe || isTranscribing) return;
|
|
225
|
+
setIsTranscribing(true);
|
|
226
|
+
setHasRequestFailed(false);
|
|
227
|
+
try {
|
|
228
|
+
const result = await onTranscribe();
|
|
229
|
+
if (result) setJustTranscribed(result);
|
|
230
|
+
} catch {
|
|
231
|
+
setHasRequestFailed(true);
|
|
232
|
+
} finally {
|
|
233
|
+
setIsTranscribing(false);
|
|
234
|
+
}
|
|
235
|
+
}, [onTranscribe, isTranscribing]);
|
|
236
|
+
const dividerClass = isMine ? "border-black/10 dark:border-white/10" : "border-black/10 dark:border-white/10";
|
|
237
|
+
if (!status && !onTranscribe) return null;
|
|
238
|
+
if (!status || status === "pending" || status === "failed") {
|
|
239
|
+
return /* @__PURE__ */ jsx4("div", { className: `mt-1.5 border-t pt-1.5 ${dividerClass}`, children: /* @__PURE__ */ jsx4(
|
|
240
|
+
TranscribeButton,
|
|
241
|
+
{
|
|
242
|
+
label: resolveActionLabel({ status, hasRequestFailed, isTranscribing, locales }),
|
|
243
|
+
isBusy: isTranscribing,
|
|
244
|
+
onClick: handleTranscribe,
|
|
245
|
+
isDisabled: !onTranscribe
|
|
246
|
+
}
|
|
247
|
+
) });
|
|
248
|
+
}
|
|
249
|
+
if (status === "unsupported") {
|
|
250
|
+
return /* @__PURE__ */ jsx4("div", { className: `mt-1.5 border-t pt-1.5 ${dividerClass}`, children: /* @__PURE__ */ jsx4("p", { className: "text-xs italic text-gray-500 dark:text-gray-400", children: locales.unsupported }) });
|
|
251
|
+
}
|
|
252
|
+
return /* @__PURE__ */ jsxs3("div", { className: `mt-1.5 border-t pt-1.5 ${dividerClass}`, children: [
|
|
253
|
+
/* @__PURE__ */ jsxs3("div", { className: "mb-0.5 flex items-center gap-1.5", children: [
|
|
254
|
+
/* @__PURE__ */ jsx4(FileText, { size: 11, className: "flex-shrink-0 text-gray-500 dark:text-gray-400", "aria-hidden": true }),
|
|
255
|
+
/* @__PURE__ */ jsx4("span", { className: "text-[11px] font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: locales.label }),
|
|
256
|
+
hasText && /* @__PURE__ */ jsx4(
|
|
257
|
+
"button",
|
|
258
|
+
{
|
|
259
|
+
onClick: handleCopy,
|
|
260
|
+
title: locales.copy,
|
|
261
|
+
"aria-label": hasCopied ? locales.copied : locales.copy,
|
|
262
|
+
className: "ml-auto flex flex-shrink-0 items-center gap-1 rounded px-1.5 py-0.5 text-[11px] text-gray-500 transition-colors hover:bg-black/5 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/10 dark:hover:text-gray-200",
|
|
263
|
+
children: hasCopied ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
264
|
+
/* @__PURE__ */ jsx4(Check, { size: 11, "aria-hidden": true }),
|
|
265
|
+
/* @__PURE__ */ jsx4("span", { children: locales.copied })
|
|
266
|
+
] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
267
|
+
/* @__PURE__ */ jsx4(Copy, { size: 11, "aria-hidden": true }),
|
|
268
|
+
/* @__PURE__ */ jsx4("span", { children: locales.copy })
|
|
269
|
+
] })
|
|
270
|
+
}
|
|
271
|
+
)
|
|
272
|
+
] }),
|
|
273
|
+
isSilent ? /* @__PURE__ */ jsx4("p", { className: "text-xs italic text-gray-500 dark:text-gray-400", children: locales.empty }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
274
|
+
/* @__PURE__ */ jsx4(
|
|
275
|
+
"p",
|
|
276
|
+
{
|
|
277
|
+
className: cn(
|
|
278
|
+
"select-all whitespace-pre-wrap break-words text-[13px] leading-[18px] text-gray-700 dark:text-gray-200",
|
|
279
|
+
isTruncated && "overflow-hidden"
|
|
280
|
+
),
|
|
281
|
+
style: isTruncated ? {
|
|
282
|
+
display: "-webkit-box",
|
|
283
|
+
WebkitBoxOrient: "vertical",
|
|
284
|
+
WebkitLineClamp: COLLAPSED_LINE_CLAMP
|
|
285
|
+
} : void 0,
|
|
286
|
+
children: text
|
|
287
|
+
}
|
|
288
|
+
),
|
|
289
|
+
isLong && /* @__PURE__ */ jsx4(
|
|
290
|
+
"button",
|
|
291
|
+
{
|
|
292
|
+
onClick: () => setIsExpanded((current) => !current),
|
|
293
|
+
className: "mt-1 text-[11px] font-medium text-gray-500 underline decoration-dotted hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200",
|
|
294
|
+
children: isExpanded ? locales.showLess : locales.showMore
|
|
295
|
+
}
|
|
296
|
+
)
|
|
297
|
+
] }),
|
|
298
|
+
onTranscribe && /* @__PURE__ */ jsxs3(
|
|
299
|
+
"button",
|
|
300
|
+
{
|
|
301
|
+
onClick: handleTranscribe,
|
|
302
|
+
disabled: isTranscribing,
|
|
303
|
+
className: "mt-1 flex items-center gap-1 text-[11px] text-gray-400 opacity-0 transition-opacity hover:text-gray-600 focus:opacity-100 group-hover:opacity-100 disabled:opacity-50 dark:text-gray-500 dark:hover:text-gray-300",
|
|
304
|
+
children: [
|
|
305
|
+
isTranscribing ? /* @__PURE__ */ jsx4(Loader2, { size: 10, className: "animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx4(RefreshCw, { size: 10, "aria-hidden": true }),
|
|
306
|
+
/* @__PURE__ */ jsx4("span", { children: isTranscribing ? locales.transcribing : locales.retry })
|
|
307
|
+
]
|
|
308
|
+
}
|
|
309
|
+
)
|
|
310
|
+
] });
|
|
311
|
+
}
|
|
312
|
+
function resolveActionLabel(params) {
|
|
313
|
+
if (params.isTranscribing) return params.locales.transcribing;
|
|
314
|
+
if (params.hasRequestFailed) return params.locales.retry;
|
|
315
|
+
if (params.status === "pending") return params.locales.transcribing;
|
|
316
|
+
if (params.status === "failed") return params.locales.failed;
|
|
317
|
+
return params.locales.transcribe;
|
|
318
|
+
}
|
|
319
|
+
function TranscribeButton({
|
|
320
|
+
label,
|
|
321
|
+
isBusy,
|
|
322
|
+
isDisabled,
|
|
323
|
+
onClick
|
|
324
|
+
}) {
|
|
325
|
+
return /* @__PURE__ */ jsxs3(
|
|
326
|
+
"button",
|
|
327
|
+
{
|
|
328
|
+
onClick,
|
|
329
|
+
disabled: isBusy || isDisabled,
|
|
330
|
+
className: "flex items-center gap-1.5 rounded px-1.5 py-0.5 text-[11px] text-gray-500 transition-colors hover:bg-black/5 hover:text-gray-700 disabled:opacity-60 dark:text-gray-400 dark:hover:bg-white/10 dark:hover:text-gray-200",
|
|
331
|
+
children: [
|
|
332
|
+
isBusy ? /* @__PURE__ */ jsx4(Loader2, { size: 11, className: "animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx4(FileText, { size: 11, "aria-hidden": true }),
|
|
333
|
+
/* @__PURE__ */ jsx4("span", { children: label })
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
169
339
|
// src/FileIcon.tsx
|
|
170
340
|
import {
|
|
171
341
|
FileArchive,
|
|
172
342
|
FileAudio,
|
|
173
343
|
FileImage,
|
|
174
344
|
FileSpreadsheet,
|
|
175
|
-
FileText,
|
|
345
|
+
FileText as FileText2,
|
|
176
346
|
FileVideo,
|
|
177
347
|
File as FileGeneric,
|
|
178
348
|
Presentation
|
|
179
349
|
} from "lucide-react";
|
|
180
|
-
|
|
181
|
-
// src/lib/cn.ts
|
|
182
|
-
import { clsx } from "clsx";
|
|
183
|
-
import { twMerge } from "tailwind-merge";
|
|
184
|
-
function cn(...inputs) {
|
|
185
|
-
return twMerge(clsx(inputs));
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// src/FileIcon.tsx
|
|
189
|
-
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
350
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
190
351
|
var IMAGE_STYLE = { Icon: FileImage, colorClass: "text-violet-500" };
|
|
191
352
|
var VIDEO_STYLE = { Icon: FileVideo, colorClass: "text-fuchsia-500" };
|
|
192
353
|
var AUDIO_STYLE = { Icon: FileAudio, colorClass: "text-amber-500" };
|
|
193
354
|
var SHEET_STYLE = { Icon: FileSpreadsheet, colorClass: "text-green-600" };
|
|
194
|
-
var WORD_STYLE = { Icon:
|
|
355
|
+
var WORD_STYLE = { Icon: FileText2, colorClass: "text-blue-500" };
|
|
195
356
|
var SLIDES_STYLE = { Icon: Presentation, colorClass: "text-orange-600" };
|
|
196
|
-
var TEXT_STYLE = { Icon:
|
|
357
|
+
var TEXT_STYLE = { Icon: FileText2, colorClass: "text-gray-500" };
|
|
197
358
|
var EXTENSION_STYLE = {
|
|
198
|
-
pdf: { Icon:
|
|
359
|
+
pdf: { Icon: FileText2, colorClass: "text-red-500" },
|
|
199
360
|
doc: WORD_STYLE,
|
|
200
361
|
docx: WORD_STYLE,
|
|
201
362
|
xls: SHEET_STYLE,
|
|
@@ -242,7 +403,7 @@ function FileIcon({ filename, mimeType, size = 20, className }) {
|
|
|
242
403
|
const extension = resolveFileIconExtension(filename, mimeType);
|
|
243
404
|
const style = EXTENSION_STYLE[extension] ?? { Icon: FileGeneric, colorClass: "text-gray-500" };
|
|
244
405
|
const { Icon, colorClass } = style;
|
|
245
|
-
return /* @__PURE__ */
|
|
406
|
+
return /* @__PURE__ */ jsx5(Icon, { size, className: cn(colorClass, className) });
|
|
246
407
|
}
|
|
247
408
|
|
|
248
409
|
// src/lib/format.ts
|
|
@@ -274,8 +435,8 @@ function formatFileSize(bytes) {
|
|
|
274
435
|
}
|
|
275
436
|
|
|
276
437
|
// src/MediaRenderer.tsx
|
|
277
|
-
import { useState as
|
|
278
|
-
import { jsx as
|
|
438
|
+
import { useState as useState3 } from "react";
|
|
439
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
279
440
|
function documentTypeLabel(filename, mimeType) {
|
|
280
441
|
const extension = filename?.split(".").pop();
|
|
281
442
|
if (extension && extension.length <= 5 && extension !== filename) return extension.toUpperCase();
|
|
@@ -296,9 +457,9 @@ function hasLazyRef(message) {
|
|
|
296
457
|
return Boolean(message.uploadId || message.mediaId);
|
|
297
458
|
}
|
|
298
459
|
function useLazyMediaUrl(message, onResolveUrl) {
|
|
299
|
-
const [url, setUrl] =
|
|
300
|
-
const [loading, setLoading] =
|
|
301
|
-
const [error, setError] =
|
|
460
|
+
const [url, setUrl] = useState3(null);
|
|
461
|
+
const [loading, setLoading] = useState3(false);
|
|
462
|
+
const [error, setError] = useState3(false);
|
|
302
463
|
const load = async () => {
|
|
303
464
|
if (url || loading || !onResolveUrl) return;
|
|
304
465
|
setLoading(true);
|
|
@@ -315,22 +476,28 @@ function useLazyMediaUrl(message, onResolveUrl) {
|
|
|
315
476
|
};
|
|
316
477
|
return { url, loading, error, load };
|
|
317
478
|
}
|
|
318
|
-
function MediaRenderer({
|
|
479
|
+
function MediaRenderer({
|
|
480
|
+
message,
|
|
481
|
+
onLightbox,
|
|
482
|
+
onResolveUrl,
|
|
483
|
+
onTranscribeAudio,
|
|
484
|
+
className
|
|
485
|
+
}) {
|
|
319
486
|
const { bubble } = useConversationLocales();
|
|
320
487
|
const eagerSrc = resolveMediaSource(message);
|
|
321
488
|
const lazy = useLazyMediaUrl(message, onResolveUrl);
|
|
322
489
|
const src = eagerSrc ?? lazy.url;
|
|
323
490
|
const canLazyLoad = !eagerSrc && hasLazyRef(message) && Boolean(onResolveUrl);
|
|
324
491
|
function LazyMediaButton({ icon, label }) {
|
|
325
|
-
return /* @__PURE__ */
|
|
492
|
+
return /* @__PURE__ */ jsxs4(
|
|
326
493
|
"button",
|
|
327
494
|
{
|
|
328
495
|
onClick: lazy.load,
|
|
329
496
|
disabled: lazy.loading,
|
|
330
497
|
className: "flex min-w-[180px] items-center gap-2 rounded-lg bg-black/5 px-2 py-1.5 text-left transition-colors hover:bg-black/10 disabled:opacity-60 dark:bg-white/10 dark:hover:bg-white/15",
|
|
331
498
|
children: [
|
|
332
|
-
/* @__PURE__ */
|
|
333
|
-
/* @__PURE__ */
|
|
499
|
+
/* @__PURE__ */ jsx6("span", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full bg-gray-600 text-white", children: icon }),
|
|
500
|
+
/* @__PURE__ */ jsx6("span", { className: "truncate text-xs text-gray-600 dark:text-gray-300", children: label })
|
|
334
501
|
]
|
|
335
502
|
}
|
|
336
503
|
);
|
|
@@ -339,82 +506,96 @@ function MediaRenderer({ message, onLightbox, onResolveUrl, className }) {
|
|
|
339
506
|
case "image":
|
|
340
507
|
case "sticker": {
|
|
341
508
|
if (!src && canLazyLoad) {
|
|
342
|
-
return /* @__PURE__ */
|
|
509
|
+
return /* @__PURE__ */ jsx6(
|
|
343
510
|
LazyMediaButton,
|
|
344
511
|
{
|
|
345
|
-
icon: /* @__PURE__ */
|
|
346
|
-
/* @__PURE__ */
|
|
347
|
-
/* @__PURE__ */
|
|
348
|
-
/* @__PURE__ */
|
|
512
|
+
icon: /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
513
|
+
/* @__PURE__ */ jsx6("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
514
|
+
/* @__PURE__ */ jsx6("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
515
|
+
/* @__PURE__ */ jsx6("polyline", { points: "21 15 16 10 5 21" })
|
|
349
516
|
] }),
|
|
350
517
|
label: lazy.loading ? bubble.mediaLoading : lazy.error ? bubble.mediaRetry : bubble.viewImage
|
|
351
518
|
}
|
|
352
519
|
);
|
|
353
520
|
}
|
|
354
|
-
return /* @__PURE__ */
|
|
355
|
-
/* @__PURE__ */
|
|
356
|
-
/* @__PURE__ */
|
|
357
|
-
/* @__PURE__ */
|
|
521
|
+
return /* @__PURE__ */ jsx6("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx6("img", { src, alt: message.caption ?? bubble.imageAlt, className: "w-full max-h-80 object-cover cursor-pointer hover:opacity-90 transition-opacity", onClick: () => onLightbox(src), loading: "lazy" }) : /* @__PURE__ */ jsx6("div", { className: "w-full h-40 bg-gray-200 flex items-center justify-center text-gray-400", children: /* @__PURE__ */ jsxs4("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
522
|
+
/* @__PURE__ */ jsx6("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
523
|
+
/* @__PURE__ */ jsx6("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
524
|
+
/* @__PURE__ */ jsx6("polyline", { points: "21 15 16 10 5 21" })
|
|
358
525
|
] }) }) });
|
|
359
526
|
}
|
|
360
527
|
case "video": {
|
|
361
528
|
if (!src && canLazyLoad) {
|
|
362
|
-
return /* @__PURE__ */
|
|
529
|
+
return /* @__PURE__ */ jsx6(
|
|
363
530
|
LazyMediaButton,
|
|
364
531
|
{
|
|
365
|
-
icon: /* @__PURE__ */
|
|
366
|
-
/* @__PURE__ */
|
|
367
|
-
/* @__PURE__ */
|
|
532
|
+
icon: /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
533
|
+
/* @__PURE__ */ jsx6("polygon", { points: "23 7 16 12 23 17 23 7" }),
|
|
534
|
+
/* @__PURE__ */ jsx6("rect", { x: "1", y: "5", width: "15", height: "14", rx: "2", ry: "2" })
|
|
368
535
|
] }),
|
|
369
536
|
label: lazy.loading ? bubble.mediaLoading : lazy.error ? bubble.mediaRetry : bubble.viewVideo
|
|
370
537
|
}
|
|
371
538
|
);
|
|
372
539
|
}
|
|
373
|
-
return /* @__PURE__ */
|
|
374
|
-
/* @__PURE__ */
|
|
375
|
-
/* @__PURE__ */
|
|
540
|
+
return /* @__PURE__ */ jsx6("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx6("video", { src, className: "w-full max-h-80 rounded-lg", controls: true, preload: "metadata", children: /* @__PURE__ */ jsx6("track", { kind: "captions" }) }) : /* @__PURE__ */ jsx6("div", { className: "w-full h-32 bg-gray-200 rounded-lg flex items-center justify-center text-gray-400", children: /* @__PURE__ */ jsxs4("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
541
|
+
/* @__PURE__ */ jsx6("polygon", { points: "23 7 16 12 23 17 23 7" }),
|
|
542
|
+
/* @__PURE__ */ jsx6("rect", { x: "1", y: "5", width: "15", height: "14", rx: "2", ry: "2" })
|
|
376
543
|
] }) }) });
|
|
377
544
|
}
|
|
378
545
|
case "audio": {
|
|
546
|
+
const transcriptionBlock = /* @__PURE__ */ jsx6(
|
|
547
|
+
AudioTranscription,
|
|
548
|
+
{
|
|
549
|
+
transcription: message.transcription,
|
|
550
|
+
isMine: message.direction === "outbound",
|
|
551
|
+
...onTranscribeAudio ? { onTranscribe: onTranscribeAudio } : {}
|
|
552
|
+
}
|
|
553
|
+
);
|
|
379
554
|
if (!src && canLazyLoad) {
|
|
380
|
-
return /* @__PURE__ */
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
555
|
+
return /* @__PURE__ */ jsxs4("div", { className: "min-w-[200px]", children: [
|
|
556
|
+
/* @__PURE__ */ jsx6(
|
|
557
|
+
LazyMediaButton,
|
|
558
|
+
{
|
|
559
|
+
icon: /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", className: "translate-x-0.5", children: /* @__PURE__ */ jsx6("polygon", { points: "5 3 19 12 5 21 5 3" }) }),
|
|
560
|
+
label: lazy.loading ? bubble.mediaLoading : lazy.error ? bubble.mediaRetry : bubble.listenAudio
|
|
561
|
+
}
|
|
562
|
+
),
|
|
563
|
+
transcriptionBlock
|
|
564
|
+
] });
|
|
387
565
|
}
|
|
388
|
-
return /* @__PURE__ */
|
|
566
|
+
return /* @__PURE__ */ jsxs4("div", { className: "min-w-[200px]", children: [
|
|
567
|
+
src ? /* @__PURE__ */ jsx6(AudioPlayer, { src, isMine: message.direction === "outbound" }) : /* @__PURE__ */ jsx6("div", { className: "h-12 bg-gray-200 rounded-lg flex items-center justify-center text-gray-400 text-xs", children: bubble.mediaUnavailable }),
|
|
568
|
+
transcriptionBlock
|
|
569
|
+
] });
|
|
389
570
|
}
|
|
390
571
|
case "document": {
|
|
391
572
|
const typeLabel = documentTypeLabel(message.filename, message.mimeType);
|
|
392
573
|
const sizeLabel = message.sizeBytes ? formatFileSize(message.sizeBytes) : null;
|
|
393
|
-
return /* @__PURE__ */
|
|
394
|
-
/* @__PURE__ */
|
|
395
|
-
/* @__PURE__ */
|
|
396
|
-
/* @__PURE__ */
|
|
397
|
-
/* @__PURE__ */
|
|
574
|
+
return /* @__PURE__ */ jsxs4("div", { className: cn("flex items-center gap-3 min-w-[200px]", className), children: [
|
|
575
|
+
/* @__PURE__ */ jsx6("div", { className: "w-10 h-10 bg-gray-200 rounded-lg flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsx6(FileIcon, { filename: message.filename, mimeType: message.mimeType }) }),
|
|
576
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex-1 min-w-0", children: [
|
|
577
|
+
/* @__PURE__ */ jsx6("p", { className: "text-sm font-medium truncate", children: message.filename ?? bubble.untitledDocument }),
|
|
578
|
+
/* @__PURE__ */ jsx6("p", { className: "truncate text-xs text-gray-500", children: sizeLabel ? `${typeLabel} \xB7 ${sizeLabel}` : typeLabel })
|
|
398
579
|
] }),
|
|
399
|
-
src ? /* @__PURE__ */
|
|
400
|
-
/* @__PURE__ */
|
|
401
|
-
/* @__PURE__ */
|
|
402
|
-
/* @__PURE__ */
|
|
403
|
-
] }) }) : canLazyLoad ? /* @__PURE__ */
|
|
580
|
+
src ? /* @__PURE__ */ jsx6("a", { href: src, download: message.filename, className: "w-8 h-8 flex items-center justify-center rounded-full bg-gray-200 hover:bg-gray-300 flex-shrink-0 transition-colors", "aria-label": bubble.downloadFile, children: /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", className: "text-gray-600", children: [
|
|
581
|
+
/* @__PURE__ */ jsx6("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
582
|
+
/* @__PURE__ */ jsx6("polyline", { points: "7 10 12 15 17 10" }),
|
|
583
|
+
/* @__PURE__ */ jsx6("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
|
|
584
|
+
] }) }) : canLazyLoad ? /* @__PURE__ */ jsx6(
|
|
404
585
|
"button",
|
|
405
586
|
{
|
|
406
587
|
onClick: lazy.load,
|
|
407
588
|
disabled: lazy.loading,
|
|
408
589
|
className: "w-8 h-8 flex items-center justify-center rounded-full bg-gray-200 hover:bg-gray-300 flex-shrink-0 transition-colors disabled:opacity-50",
|
|
409
590
|
"aria-label": bubble.downloadFile,
|
|
410
|
-
children: /* @__PURE__ */
|
|
411
|
-
/* @__PURE__ */
|
|
412
|
-
/* @__PURE__ */
|
|
413
|
-
/* @__PURE__ */
|
|
591
|
+
children: /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", className: "text-gray-600", children: [
|
|
592
|
+
/* @__PURE__ */ jsx6("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
593
|
+
/* @__PURE__ */ jsx6("polyline", { points: "7 10 12 15 17 10" }),
|
|
594
|
+
/* @__PURE__ */ jsx6("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
|
|
414
595
|
] })
|
|
415
596
|
}
|
|
416
597
|
) : null,
|
|
417
|
-
lazy.error && /* @__PURE__ */
|
|
598
|
+
lazy.error && /* @__PURE__ */ jsx6("span", { className: "text-xs text-red-500 flex-shrink-0", children: bubble.mediaError })
|
|
418
599
|
] });
|
|
419
600
|
}
|
|
420
601
|
default:
|
|
@@ -423,7 +604,7 @@ function MediaRenderer({ message, onLightbox, onResolveUrl, className }) {
|
|
|
423
604
|
}
|
|
424
605
|
|
|
425
606
|
// src/Lightbox.tsx
|
|
426
|
-
import { jsx as
|
|
607
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
427
608
|
var DEFAULT_LIGHTBOX_LABELS = {
|
|
428
609
|
imageAlt: "Imagem",
|
|
429
610
|
close: "Fechar"
|
|
@@ -431,16 +612,16 @@ var DEFAULT_LIGHTBOX_LABELS = {
|
|
|
431
612
|
function Lightbox({ imageUrl, caption, onClose, labels }) {
|
|
432
613
|
const imageAltLabel = labels?.imageAlt ?? DEFAULT_LIGHTBOX_LABELS.imageAlt;
|
|
433
614
|
const closeLabel = labels?.close ?? DEFAULT_LIGHTBOX_LABELS.close;
|
|
434
|
-
return /* @__PURE__ */
|
|
435
|
-
/* @__PURE__ */
|
|
436
|
-
caption && /* @__PURE__ */
|
|
437
|
-
/* @__PURE__ */
|
|
615
|
+
return /* @__PURE__ */ jsx7("div", { className: "fixed inset-0 z-50 bg-black/85 flex items-center justify-center p-4", onClick: onClose, children: /* @__PURE__ */ jsxs5("div", { className: "max-w-[90vw] max-h-[90vh] flex flex-col items-center", onClick: (e) => e.stopPropagation(), children: [
|
|
616
|
+
/* @__PURE__ */ jsx7("img", { src: imageUrl, alt: caption ?? imageAltLabel, className: "max-w-full max-h-[80vh] object-contain rounded-lg" }),
|
|
617
|
+
caption && /* @__PURE__ */ jsx7("p", { className: "text-white text-sm mt-3 text-center", children: caption }),
|
|
618
|
+
/* @__PURE__ */ jsx7("button", { onClick: onClose, className: "mt-4 px-4 py-2 bg-white/20 text-white rounded-lg hover:bg-white/30 transition-colors", children: closeLabel })
|
|
438
619
|
] }) });
|
|
439
620
|
}
|
|
440
621
|
|
|
441
622
|
// src/InteractiveMessage.tsx
|
|
442
|
-
import { useState as
|
|
443
|
-
import { jsx as
|
|
623
|
+
import { useState as useState4 } from "react";
|
|
624
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
444
625
|
var FORMATTING_CLASSES = "[&_strong]:font-bold [&_em]:italic [&_del]:line-through";
|
|
445
626
|
var DEFAULT_INTERACTIVE_MESSAGE_LABELS = {
|
|
446
627
|
openList: "Ver op\xE7\xF5es"
|
|
@@ -453,14 +634,14 @@ function collectButtons(payload) {
|
|
|
453
634
|
}
|
|
454
635
|
function InteractiveMessage({ payload, onSelect, labels, className }) {
|
|
455
636
|
const openListLabel = labels?.openList ?? DEFAULT_INTERACTIVE_MESSAGE_LABELS.openList;
|
|
456
|
-
const [isListOpen, setIsListOpen] =
|
|
637
|
+
const [isListOpen, setIsListOpen] = useState4(false);
|
|
457
638
|
const buttons = collectButtons(payload);
|
|
458
639
|
const sections = payload.action?.sections ?? [];
|
|
459
640
|
const hasRows = collectRows(payload).length > 0;
|
|
460
641
|
const isInteractable = onSelect !== void 0;
|
|
461
|
-
return /* @__PURE__ */
|
|
462
|
-
payload.header?.text ? /* @__PURE__ */
|
|
463
|
-
payload.body?.text ? /* @__PURE__ */
|
|
642
|
+
return /* @__PURE__ */ jsxs6("div", { className: cn("flex flex-col gap-1", className), children: [
|
|
643
|
+
payload.header?.text ? /* @__PURE__ */ jsx8("p", { className: cn("text-sm font-semibold text-gray-900 dark:text-gray-100", FORMATTING_CLASSES), children: parseWhatsAppFormatting(payload.header.text) }) : null,
|
|
644
|
+
payload.body?.text ? /* @__PURE__ */ jsx8(
|
|
464
645
|
"p",
|
|
465
646
|
{
|
|
466
647
|
className: cn(
|
|
@@ -470,8 +651,8 @@ function InteractiveMessage({ payload, onSelect, labels, className }) {
|
|
|
470
651
|
children: parseWhatsAppFormatting(payload.body.text)
|
|
471
652
|
}
|
|
472
653
|
) : null,
|
|
473
|
-
payload.footer?.text ? /* @__PURE__ */
|
|
474
|
-
buttons.length > 0 ? /* @__PURE__ */
|
|
654
|
+
payload.footer?.text ? /* @__PURE__ */ jsx8("p", { className: cn("text-xs text-gray-500 dark:text-gray-400", FORMATTING_CLASSES), children: parseWhatsAppFormatting(payload.footer.text) }) : null,
|
|
655
|
+
buttons.length > 0 ? /* @__PURE__ */ jsx8("div", { className: "mt-1 flex flex-col gap-1 border-t border-gray-200 pt-1 dark:border-gray-700", children: buttons.map((button) => /* @__PURE__ */ jsx8(
|
|
475
656
|
"button",
|
|
476
657
|
{
|
|
477
658
|
type: "button",
|
|
@@ -482,8 +663,8 @@ function InteractiveMessage({ payload, onSelect, labels, className }) {
|
|
|
482
663
|
},
|
|
483
664
|
button.id
|
|
484
665
|
)) }) : null,
|
|
485
|
-
hasRows ? /* @__PURE__ */
|
|
486
|
-
/* @__PURE__ */
|
|
666
|
+
hasRows ? /* @__PURE__ */ jsxs6("div", { className: "mt-1 border-t border-gray-200 pt-1 dark:border-gray-700", children: [
|
|
667
|
+
/* @__PURE__ */ jsxs6(
|
|
487
668
|
"button",
|
|
488
669
|
{
|
|
489
670
|
type: "button",
|
|
@@ -496,9 +677,9 @@ function InteractiveMessage({ payload, onSelect, labels, className }) {
|
|
|
496
677
|
]
|
|
497
678
|
}
|
|
498
679
|
),
|
|
499
|
-
isListOpen ? /* @__PURE__ */
|
|
500
|
-
section.title ? /* @__PURE__ */
|
|
501
|
-
(section.rows ?? []).map((row) => /* @__PURE__ */
|
|
680
|
+
isListOpen ? /* @__PURE__ */ jsx8("div", { className: "mt-1 flex flex-col gap-1", children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs6("div", { className: "flex flex-col", children: [
|
|
681
|
+
section.title ? /* @__PURE__ */ jsx8("p", { className: "px-3 py-1 text-xs font-semibold uppercase text-gray-500 dark:text-gray-400", children: section.title }) : null,
|
|
682
|
+
(section.rows ?? []).map((row) => /* @__PURE__ */ jsxs6(
|
|
502
683
|
"button",
|
|
503
684
|
{
|
|
504
685
|
type: "button",
|
|
@@ -506,8 +687,8 @@ function InteractiveMessage({ payload, onSelect, labels, className }) {
|
|
|
506
687
|
onClick: () => onSelect?.({ kind: "list", option: row }),
|
|
507
688
|
className: "rounded-md px-3 py-1.5 text-left text-sm text-gray-900 transition-colors enabled:hover:bg-gray-100 disabled:cursor-default dark:text-gray-100 dark:enabled:hover:bg-gray-700",
|
|
508
689
|
children: [
|
|
509
|
-
/* @__PURE__ */
|
|
510
|
-
row.description ? /* @__PURE__ */
|
|
690
|
+
/* @__PURE__ */ jsx8("span", { className: "block", children: row.title }),
|
|
691
|
+
row.description ? /* @__PURE__ */ jsx8("span", { className: "block text-xs text-gray-500 dark:text-gray-400", children: row.description }) : null
|
|
511
692
|
]
|
|
512
693
|
},
|
|
513
694
|
row.id
|
|
@@ -531,23 +712,23 @@ function createMediaUrlResolver(api) {
|
|
|
531
712
|
|
|
532
713
|
// src/providers/ConversationsProvider.tsx
|
|
533
714
|
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
534
|
-
import { jsx as
|
|
715
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
535
716
|
var ConversationsContext = createContext2(null);
|
|
536
717
|
function ConversationsProvider({
|
|
537
718
|
api,
|
|
538
719
|
sse,
|
|
539
720
|
children
|
|
540
721
|
}) {
|
|
541
|
-
return /* @__PURE__ */
|
|
722
|
+
return /* @__PURE__ */ jsx9(ConversationsContext.Provider, { value: { api, sse }, children });
|
|
542
723
|
}
|
|
543
724
|
function useConversations() {
|
|
544
725
|
return useContext2(ConversationsContext);
|
|
545
726
|
}
|
|
546
727
|
|
|
547
728
|
// src/MessageBubble.tsx
|
|
548
|
-
import { useMemo, useState as
|
|
549
|
-
import { Check } from "lucide-react";
|
|
550
|
-
import { Fragment, jsx as
|
|
729
|
+
import { useMemo, useState as useState5 } from "react";
|
|
730
|
+
import { Check as Check2 } from "lucide-react";
|
|
731
|
+
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
551
732
|
var BUBBLE_COLOR = {
|
|
552
733
|
agent: "bg-[#d9fdd3] dark:bg-[#005c4b]",
|
|
553
734
|
bot: "bg-[#d7f0ec] dark:bg-[#0a3d3a]",
|
|
@@ -564,15 +745,21 @@ function MessageBubble({
|
|
|
564
745
|
onToggleSelect,
|
|
565
746
|
onResolveMediaUrl,
|
|
566
747
|
onInteractiveSelect,
|
|
748
|
+
onTranscribeAudio,
|
|
567
749
|
className
|
|
568
750
|
}) {
|
|
569
751
|
const { bubble, selection } = useConversationLocales();
|
|
570
|
-
const [lightboxSrc, setLightboxSrc] =
|
|
752
|
+
const [lightboxSrc, setLightboxSrc] = useState5(null);
|
|
571
753
|
const context = useConversations();
|
|
572
754
|
const resolveMediaUrl = useMemo(
|
|
573
755
|
() => onResolveMediaUrl ?? (context?.api ? createMediaUrlResolver(context.api) : void 0),
|
|
574
756
|
[onResolveMediaUrl, context?.api]
|
|
575
757
|
);
|
|
758
|
+
const requestTranscription = useMemo(() => {
|
|
759
|
+
const transcribe = onTranscribeAudio ?? context?.api?.transcribeAudio?.bind(context.api);
|
|
760
|
+
if (!transcribe) return void 0;
|
|
761
|
+
return () => transcribe(message.id);
|
|
762
|
+
}, [onTranscribeAudio, context?.api, message.id]);
|
|
576
763
|
const bubbleColor = BUBBLE_COLOR[message.sender] ?? BUBBLE_COLOR.customer;
|
|
577
764
|
const hasError = message.status === "failed";
|
|
578
765
|
const isMedia = MEDIA_TYPES.has(message.type);
|
|
@@ -581,7 +768,7 @@ function MessageBubble({
|
|
|
581
768
|
const displayName = message.sender === "agent" && senderName ? senderName : bubble[message.sender] ?? message.sender;
|
|
582
769
|
const tooltipText = message.status === "read" && message.readAt ? `${bubble.readAt}${formatDateTime(message.readAt)}` : message.status === "failed" ? bubble.windowExpired : void 0;
|
|
583
770
|
const tailCornerClass = isFirstInGroup ? isMine ? "rounded-tr-md" : "rounded-tl-md" : "";
|
|
584
|
-
const checkbox = /* @__PURE__ */
|
|
771
|
+
const checkbox = /* @__PURE__ */ jsx10(
|
|
585
772
|
"button",
|
|
586
773
|
{
|
|
587
774
|
onClick: (e) => {
|
|
@@ -594,10 +781,10 @@ function MessageBubble({
|
|
|
594
781
|
${isSelected ? "bg-teal-600 border-teal-600" : "bg-white/80 dark:bg-black/40 border-black/20 dark:border-white/30"}
|
|
595
782
|
${isSelecting ? "opacity-100" : "opacity-0 group-hover:opacity-100"}
|
|
596
783
|
`,
|
|
597
|
-
children: isSelected && /* @__PURE__ */
|
|
784
|
+
children: isSelected && /* @__PURE__ */ jsx10(Check2, { size: 12, className: "text-white", strokeWidth: 3 })
|
|
598
785
|
}
|
|
599
786
|
);
|
|
600
|
-
return /* @__PURE__ */
|
|
787
|
+
return /* @__PURE__ */ jsxs7(
|
|
601
788
|
"div",
|
|
602
789
|
{
|
|
603
790
|
className: cn(
|
|
@@ -608,7 +795,7 @@ function MessageBubble({
|
|
|
608
795
|
),
|
|
609
796
|
children: [
|
|
610
797
|
isMine && checkbox,
|
|
611
|
-
/* @__PURE__ */
|
|
798
|
+
/* @__PURE__ */ jsxs7(
|
|
612
799
|
"div",
|
|
613
800
|
{
|
|
614
801
|
onClick: isSelecting ? onToggleSelect : void 0,
|
|
@@ -621,48 +808,57 @@ function MessageBubble({
|
|
|
621
808
|
relative
|
|
622
809
|
`,
|
|
623
810
|
children: [
|
|
624
|
-
isMine && isFirstInGroup && (message.sender === "bot" || message.sender === "agent" && senderName) && /* @__PURE__ */
|
|
625
|
-
message.moderation?.isOffensive && /* @__PURE__ */
|
|
811
|
+
isMine && isFirstInGroup && (message.sender === "bot" || message.sender === "agent" && senderName) && /* @__PURE__ */ jsx10("div", { className: "text-xs font-semibold mb-0.5 text-teal-700 dark:text-teal-400", children: displayName }),
|
|
812
|
+
message.moderation?.isOffensive && /* @__PURE__ */ jsxs7(
|
|
626
813
|
"div",
|
|
627
814
|
{
|
|
628
815
|
className: "mb-1 inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-800 dark:bg-amber-950/60 dark:text-amber-300",
|
|
629
816
|
title: message.moderation.terms.length > 0 ? message.moderation.terms.join(", ") : void 0,
|
|
630
817
|
children: [
|
|
631
|
-
/* @__PURE__ */
|
|
632
|
-
/* @__PURE__ */
|
|
818
|
+
/* @__PURE__ */ jsx10("span", { "aria-hidden": true, children: "\u26A0\uFE0F" }),
|
|
819
|
+
/* @__PURE__ */ jsx10("span", { children: bubble.moderationFlagged })
|
|
633
820
|
]
|
|
634
821
|
}
|
|
635
822
|
),
|
|
636
|
-
isMedia ? /* @__PURE__ */
|
|
823
|
+
isMedia ? /* @__PURE__ */ jsx10(
|
|
824
|
+
MediaRenderer,
|
|
825
|
+
{
|
|
826
|
+
message,
|
|
827
|
+
onLightbox: setLightboxSrc,
|
|
828
|
+
onResolveUrl: resolveMediaUrl,
|
|
829
|
+
...requestTranscription ? { onTranscribeAudio: requestTranscription } : {}
|
|
830
|
+
}
|
|
831
|
+
) : isInteractive && message.payload ? (
|
|
637
832
|
// O texto da mensagem interativa mora dentro do payload (`body.text`), e `content` guarda
|
|
638
833
|
// só uma cópia achatada para busca — renderizar `content` aqui duplicaria o corpo.
|
|
639
|
-
/* @__PURE__ */
|
|
640
|
-
) : /* @__PURE__ */
|
|
641
|
-
isTemplate && /* @__PURE__ */
|
|
642
|
-
/* @__PURE__ */
|
|
643
|
-
/* @__PURE__ */
|
|
834
|
+
/* @__PURE__ */ jsx10(InteractiveMessage, { payload: message.payload, onSelect: onInteractiveSelect })
|
|
835
|
+
) : /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
836
|
+
isTemplate && /* @__PURE__ */ jsxs7("p", { className: "text-xs text-gray-500 dark:text-gray-400 flex items-center gap-1 mb-0.5", children: [
|
|
837
|
+
/* @__PURE__ */ jsx10("span", { children: "\u{1F4E8}" }),
|
|
838
|
+
/* @__PURE__ */ jsxs7("span", { className: "font-medium", children: [
|
|
644
839
|
bubble.templateLabel,
|
|
645
840
|
message.templateName ? ` \u2014 ${message.templateName}` : ""
|
|
646
841
|
] })
|
|
647
842
|
] }),
|
|
648
|
-
/* @__PURE__ */
|
|
843
|
+
/* @__PURE__ */ jsx10("div", { className: "text-sm text-gray-900 dark:text-gray-100 whitespace-pre-wrap break-words leading-[19px]", children: parseWhatsAppFormatting(message.content ?? "") })
|
|
649
844
|
] }),
|
|
650
|
-
/* @__PURE__ */
|
|
651
|
-
/* @__PURE__ */
|
|
652
|
-
isMine && message.status && /* @__PURE__ */
|
|
845
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-end gap-1 mt-0.5 select-none", children: [
|
|
846
|
+
/* @__PURE__ */ jsx10("span", { className: "text-xs text-black/40 dark:text-white/40 font-medium", children: formatTimestamp(message.timestamp) }),
|
|
847
|
+
isMine && message.status && /* @__PURE__ */ jsx10(StatusTicks, { status: message.status, title: tooltipText })
|
|
653
848
|
] })
|
|
654
849
|
]
|
|
655
850
|
}
|
|
656
851
|
),
|
|
657
852
|
!isMine && checkbox,
|
|
658
|
-
lightboxSrc && /* @__PURE__ */
|
|
853
|
+
lightboxSrc && /* @__PURE__ */ jsx10(Lightbox, { imageUrl: lightboxSrc, onClose: () => setLightboxSrc(null) })
|
|
659
854
|
]
|
|
660
855
|
}
|
|
661
856
|
);
|
|
662
857
|
}
|
|
663
858
|
|
|
664
859
|
// src/Wallpaper.tsx
|
|
665
|
-
import {
|
|
860
|
+
import { forwardRef } from "react";
|
|
861
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
666
862
|
function doodlePattern(strokeColor, opacity) {
|
|
667
863
|
const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><g fill='none' stroke='${strokeColor}' stroke-width='1.2' opacity='${opacity}'><circle cx='15' cy='15' r='3'/><path d='M40 10 q5 8 0 16 q-5 -8 0 -16z'/><circle cx='70' cy='28' r='2'/><path d='M18 55 l6 6 m-6 0 l6 -6'/><circle cx='55' cy='68' r='2.5'/><path d='M85 58 q6 6 0 12 q-6 -6 0 -12z'/><circle cx='8' cy='85' r='2'/><path d='M65 90 l5 5 m-5 0 l5 -5'/></g></svg>`;
|
|
668
864
|
return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
|
|
@@ -679,17 +875,21 @@ var DARK_WALLPAPER = {
|
|
|
679
875
|
backgroundRepeat: "repeat",
|
|
680
876
|
backgroundSize: "100px 100px"
|
|
681
877
|
};
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
878
|
+
var ConversationWallpaper = forwardRef(
|
|
879
|
+
function ConversationWallpaper2({ children, className, style, onScroll }, ref) {
|
|
880
|
+
const isDark = useIsDarkTheme();
|
|
881
|
+
return /* @__PURE__ */ jsx11(
|
|
882
|
+
"div",
|
|
883
|
+
{
|
|
884
|
+
ref,
|
|
885
|
+
onScroll,
|
|
886
|
+
className: cn("cv-wallpaper", className),
|
|
887
|
+
style: { ...isDark ? DARK_WALLPAPER : LIGHT_WALLPAPER, ...style },
|
|
888
|
+
children
|
|
889
|
+
}
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
);
|
|
693
893
|
|
|
694
894
|
// src/emojiCatalog.ts
|
|
695
895
|
var EMOJI_CATEGORIES = [
|
|
@@ -846,8 +1046,8 @@ function searchEmojis(query) {
|
|
|
846
1046
|
}
|
|
847
1047
|
|
|
848
1048
|
// src/EmojiPicker.tsx
|
|
849
|
-
import { useState as
|
|
850
|
-
import { jsx as
|
|
1049
|
+
import { useState as useState6, useCallback as useCallback2, useMemo as useMemo2 } from "react";
|
|
1050
|
+
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
851
1051
|
var DEFAULT_EMOJI_PICKER_LABELS = {
|
|
852
1052
|
search: "Buscar emoji",
|
|
853
1053
|
noResults: "Nenhum emoji encontrado"
|
|
@@ -855,9 +1055,9 @@ var DEFAULT_EMOJI_PICKER_LABELS = {
|
|
|
855
1055
|
var EmojiPicker = ({ onSelect, labels, className = "" }) => {
|
|
856
1056
|
const searchLabel = labels?.search ?? DEFAULT_EMOJI_PICKER_LABELS.search;
|
|
857
1057
|
const noResultsLabel = labels?.noResults ?? DEFAULT_EMOJI_PICKER_LABELS.noResults;
|
|
858
|
-
const [activeCategory, setActiveCategory] =
|
|
859
|
-
const [query, setQuery] =
|
|
860
|
-
const handleSelect =
|
|
1058
|
+
const [activeCategory, setActiveCategory] = useState6(0);
|
|
1059
|
+
const [query, setQuery] = useState6("");
|
|
1060
|
+
const handleSelect = useCallback2(
|
|
861
1061
|
(emoji) => {
|
|
862
1062
|
onSelect(emoji);
|
|
863
1063
|
},
|
|
@@ -868,8 +1068,8 @@ var EmojiPicker = ({ onSelect, labels, className = "" }) => {
|
|
|
868
1068
|
() => isSearching ? searchEmojis(query) : EMOJI_CATEGORIES[activeCategory].entries,
|
|
869
1069
|
[isSearching, query, activeCategory]
|
|
870
1070
|
);
|
|
871
|
-
return /* @__PURE__ */
|
|
872
|
-
/* @__PURE__ */
|
|
1071
|
+
return /* @__PURE__ */ jsxs8("div", { className: `bg-white border border-gray-200 rounded-lg shadow-lg overflow-hidden ${className}`, children: [
|
|
1072
|
+
/* @__PURE__ */ jsx12("div", { className: "p-2 border-b border-gray-200", children: /* @__PURE__ */ jsx12(
|
|
873
1073
|
"input",
|
|
874
1074
|
{
|
|
875
1075
|
type: "search",
|
|
@@ -880,7 +1080,7 @@ var EmojiPicker = ({ onSelect, labels, className = "" }) => {
|
|
|
880
1080
|
className: "w-full rounded-md border border-gray-200 px-2 py-1.5 text-sm outline-none focus:border-blue-500"
|
|
881
1081
|
}
|
|
882
1082
|
) }),
|
|
883
|
-
isSearching ? null : /* @__PURE__ */
|
|
1083
|
+
isSearching ? null : /* @__PURE__ */ jsx12("div", { className: "flex border-b border-gray-200 overflow-x-auto", children: EMOJI_CATEGORIES.map((category, index) => /* @__PURE__ */ jsxs8(
|
|
884
1084
|
"button",
|
|
885
1085
|
{
|
|
886
1086
|
onClick: () => setActiveCategory(index),
|
|
@@ -893,7 +1093,7 @@ var EmojiPicker = ({ onSelect, labels, className = "" }) => {
|
|
|
893
1093
|
},
|
|
894
1094
|
category.name
|
|
895
1095
|
)) }),
|
|
896
|
-
visibleEntries.length === 0 ? /* @__PURE__ */
|
|
1096
|
+
visibleEntries.length === 0 ? /* @__PURE__ */ jsx12("p", { className: "px-3 py-6 text-center text-sm text-gray-500", children: noResultsLabel }) : /* @__PURE__ */ jsx12("div", { className: "grid grid-cols-10 gap-0.5 p-2 max-h-[240px] overflow-y-auto", children: visibleEntries.map((entry) => /* @__PURE__ */ jsx12(
|
|
897
1097
|
"button",
|
|
898
1098
|
{
|
|
899
1099
|
onClick: () => handleSelect(entry.emoji),
|
|
@@ -907,9 +1107,167 @@ var EmojiPicker = ({ onSelect, labels, className = "" }) => {
|
|
|
907
1107
|
] });
|
|
908
1108
|
};
|
|
909
1109
|
|
|
1110
|
+
// src/AudioRecorderButton.tsx
|
|
1111
|
+
import { useCallback as useCallback3, useEffect as useEffect2, useRef as useRef2, useState as useState7 } from "react";
|
|
1112
|
+
import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1113
|
+
var DEFAULT_AUDIO_RECORDER_BUTTON_LABELS = {
|
|
1114
|
+
start: "Gravar \xE1udio",
|
|
1115
|
+
stop: "Parar grava\xE7\xE3o",
|
|
1116
|
+
unsupported: "Este navegador n\xE3o grava \xE1udio.",
|
|
1117
|
+
denied: "Sem permiss\xE3o para usar o microfone.",
|
|
1118
|
+
review: "Ou\xE7a antes de enviar",
|
|
1119
|
+
send: "Enviar \xE1udio",
|
|
1120
|
+
discard: "Descartar \xE1udio",
|
|
1121
|
+
empty: "Nada foi captado pelo microfone."
|
|
1122
|
+
};
|
|
1123
|
+
var DEFAULT_MAX_RECORDING_MILLISECONDS = 5 * 60 * 1e3;
|
|
1124
|
+
var RECORDING_FORMATS = [
|
|
1125
|
+
{ mimeType: "audio/ogg;codecs=opus", uploadMimeType: "audio/ogg", extension: "ogg" },
|
|
1126
|
+
{ mimeType: "audio/mp4", uploadMimeType: "audio/mp4", extension: "m4a" },
|
|
1127
|
+
{ mimeType: "audio/webm", uploadMimeType: "audio/webm", extension: "webm" }
|
|
1128
|
+
];
|
|
1129
|
+
function resolveRecordingFormat() {
|
|
1130
|
+
if (typeof MediaRecorder === "undefined") return void 0;
|
|
1131
|
+
if (typeof MediaRecorder.isTypeSupported !== "function") return RECORDING_FORMATS[0];
|
|
1132
|
+
return RECORDING_FORMATS.find((format) => MediaRecorder.isTypeSupported(format.mimeType));
|
|
1133
|
+
}
|
|
1134
|
+
function AudioRecorderButton({
|
|
1135
|
+
onRecorded,
|
|
1136
|
+
onFailure,
|
|
1137
|
+
onRecordingChange,
|
|
1138
|
+
reviewBeforeSend = true,
|
|
1139
|
+
maxDurationMilliseconds = DEFAULT_MAX_RECORDING_MILLISECONDS,
|
|
1140
|
+
labels,
|
|
1141
|
+
disabled
|
|
1142
|
+
}) {
|
|
1143
|
+
const labelOf = (key) => labels?.[key] ?? DEFAULT_AUDIO_RECORDER_BUTTON_LABELS[key];
|
|
1144
|
+
const [isRecording, setIsRecording] = useState7(false);
|
|
1145
|
+
const [pending, setPending] = useState7(void 0);
|
|
1146
|
+
const recorderRef = useRef2(null);
|
|
1147
|
+
const autoStopRef = useRef2(void 0);
|
|
1148
|
+
const discard = useCallback3(() => {
|
|
1149
|
+
setPending((current) => {
|
|
1150
|
+
if (current) URL.revokeObjectURL(current.objectURL);
|
|
1151
|
+
return void 0;
|
|
1152
|
+
});
|
|
1153
|
+
}, []);
|
|
1154
|
+
useEffect2(() => discard, [discard]);
|
|
1155
|
+
const confirm = useCallback3(() => {
|
|
1156
|
+
if (!pending) return;
|
|
1157
|
+
void onRecorded(pending.file);
|
|
1158
|
+
discard();
|
|
1159
|
+
}, [discard, onRecorded, pending]);
|
|
1160
|
+
const stop = useCallback3(() => {
|
|
1161
|
+
recorderRef.current?.stop();
|
|
1162
|
+
}, []);
|
|
1163
|
+
const start = useCallback3(async () => {
|
|
1164
|
+
const format = resolveRecordingFormat();
|
|
1165
|
+
if (!format || !navigator.mediaDevices?.getUserMedia) {
|
|
1166
|
+
onFailure?.(labels?.unsupported ?? DEFAULT_AUDIO_RECORDER_BUTTON_LABELS.unsupported);
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
try {
|
|
1170
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
1171
|
+
const recorder = new MediaRecorder(stream, { mimeType: format.mimeType });
|
|
1172
|
+
const chunks = [];
|
|
1173
|
+
recorder.addEventListener("dataavailable", (event) => {
|
|
1174
|
+
if (event.data.size > 0) chunks.push(event.data);
|
|
1175
|
+
});
|
|
1176
|
+
recorder.addEventListener("stop", () => {
|
|
1177
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
1178
|
+
clearTimeout(autoStopRef.current);
|
|
1179
|
+
setIsRecording(false);
|
|
1180
|
+
onRecordingChange?.(false);
|
|
1181
|
+
recorderRef.current = null;
|
|
1182
|
+
const blob = new Blob(chunks, { type: format.uploadMimeType });
|
|
1183
|
+
const file = new File([blob], `audio-${Date.now()}.${format.extension}`, {
|
|
1184
|
+
type: format.uploadMimeType
|
|
1185
|
+
});
|
|
1186
|
+
if (blob.size === 0) {
|
|
1187
|
+
onFailure?.(labelOf("empty"));
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
if (!reviewBeforeSend) {
|
|
1191
|
+
void onRecorded(file);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
setPending({ file, objectURL: URL.createObjectURL(blob) });
|
|
1195
|
+
});
|
|
1196
|
+
recorderRef.current = recorder;
|
|
1197
|
+
recorder.start();
|
|
1198
|
+
autoStopRef.current = setTimeout(() => recorder.stop(), maxDurationMilliseconds);
|
|
1199
|
+
setIsRecording(true);
|
|
1200
|
+
onRecordingChange?.(true);
|
|
1201
|
+
} catch {
|
|
1202
|
+
onFailure?.(labels?.denied ?? DEFAULT_AUDIO_RECORDER_BUTTON_LABELS.denied);
|
|
1203
|
+
}
|
|
1204
|
+
}, [maxDurationMilliseconds, onFailure, onRecorded, onRecordingChange, reviewBeforeSend]);
|
|
1205
|
+
const toggleLabel = isRecording ? labelOf("stop") : labelOf("start");
|
|
1206
|
+
return (
|
|
1207
|
+
/* O painel de revisão flutua sobre o botão em vez de ocupar espaço na barra: o microfone mora
|
|
1208
|
+
na caixa do botão de enviar, e empurrar o composer para cima a cada gravação faria a
|
|
1209
|
+
conversa saltar. */
|
|
1210
|
+
/* @__PURE__ */ jsxs9("div", { className: "relative flex-shrink-0", children: [
|
|
1211
|
+
pending && /* @__PURE__ */ jsxs9(
|
|
1212
|
+
"div",
|
|
1213
|
+
{
|
|
1214
|
+
role: "group",
|
|
1215
|
+
"aria-label": labelOf("review"),
|
|
1216
|
+
className: "absolute bottom-full right-0 z-20 mb-2 flex w-[calc(100vw-2rem)] max-w-[20rem] flex-wrap items-center justify-end gap-2 rounded-xl border border-gray-200 bg-white p-2 shadow-lg dark:border-gray-700 dark:bg-gray-800",
|
|
1217
|
+
children: [
|
|
1218
|
+
/* @__PURE__ */ jsx13("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx13(AudioPlayer, { src: pending.objectURL }) }),
|
|
1219
|
+
/* @__PURE__ */ jsx13(
|
|
1220
|
+
"button",
|
|
1221
|
+
{
|
|
1222
|
+
type: "button",
|
|
1223
|
+
onClick: discard,
|
|
1224
|
+
title: labelOf("discard"),
|
|
1225
|
+
"aria-label": labelOf("discard"),
|
|
1226
|
+
className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full text-gray-500 transition-colors hover:bg-gray-100 hover:text-red-500 dark:hover:bg-gray-700",
|
|
1227
|
+
children: /* @__PURE__ */ jsxs9("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
1228
|
+
/* @__PURE__ */ jsx13("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
1229
|
+
/* @__PURE__ */ jsx13("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
1230
|
+
] })
|
|
1231
|
+
}
|
|
1232
|
+
),
|
|
1233
|
+
/* @__PURE__ */ jsx13(
|
|
1234
|
+
"button",
|
|
1235
|
+
{
|
|
1236
|
+
type: "button",
|
|
1237
|
+
onClick: confirm,
|
|
1238
|
+
title: labelOf("send"),
|
|
1239
|
+
"aria-label": labelOf("send"),
|
|
1240
|
+
className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-emerald-500 text-white transition-colors hover:bg-emerald-600",
|
|
1241
|
+
children: /* @__PURE__ */ jsx13("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx13("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) })
|
|
1242
|
+
}
|
|
1243
|
+
)
|
|
1244
|
+
]
|
|
1245
|
+
}
|
|
1246
|
+
),
|
|
1247
|
+
/* @__PURE__ */ jsx13(
|
|
1248
|
+
"button",
|
|
1249
|
+
{
|
|
1250
|
+
type: "button",
|
|
1251
|
+
disabled: disabled || pending !== void 0,
|
|
1252
|
+
onClick: () => isRecording ? stop() : void start(),
|
|
1253
|
+
title: toggleLabel,
|
|
1254
|
+
"aria-label": toggleLabel,
|
|
1255
|
+
"aria-pressed": isRecording,
|
|
1256
|
+
className: `flex h-10 w-10 items-center justify-center rounded-full transition-colors disabled:opacity-50 ${isRecording ? "animate-pulse bg-red-500 text-white ring-4 ring-red-500/30 hover:bg-red-600" : "text-gray-500 hover:bg-gray-200 dark:hover:bg-gray-700"}`,
|
|
1257
|
+
children: isRecording ? /* @__PURE__ */ jsx13("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx13("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) }) : /* @__PURE__ */ jsxs9("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
1258
|
+
/* @__PURE__ */ jsx13("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3z" }),
|
|
1259
|
+
/* @__PURE__ */ jsx13("path", { d: "M19 11a7 7 0 0 1-14 0" }),
|
|
1260
|
+
/* @__PURE__ */ jsx13("line", { x1: "12", y1: "18", x2: "12", y2: "22" })
|
|
1261
|
+
] })
|
|
1262
|
+
}
|
|
1263
|
+
)
|
|
1264
|
+
] })
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
910
1268
|
// src/MessageComposer.tsx
|
|
911
|
-
import { useState as
|
|
912
|
-
import { Fragment as
|
|
1269
|
+
import { useState as useState8, useRef as useRef3, useCallback as useCallback4 } from "react";
|
|
1270
|
+
import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
913
1271
|
function applyQuickReplyVariables(template, variables = {}) {
|
|
914
1272
|
return template.replace(/\{\{\s*([\w.]+)\s*\}\}/g, (_, chave) => variables[chave] ?? "");
|
|
915
1273
|
}
|
|
@@ -951,14 +1309,14 @@ var MessageComposer = ({
|
|
|
951
1309
|
const emojiLabel = labels?.emoji ?? DEFAULT_MESSAGE_COMPOSER_LABELS.emoji;
|
|
952
1310
|
const attachLabel = labels?.attach ?? DEFAULT_MESSAGE_COMPOSER_LABELS.attach;
|
|
953
1311
|
const sendLabel = labels?.send ?? DEFAULT_MESSAGE_COMPOSER_LABELS.send;
|
|
954
|
-
const [internalText, setInternalText] =
|
|
955
|
-
const [showEmoji, setShowEmoji] =
|
|
956
|
-
const [attachments, setAttachments] =
|
|
957
|
-
const textareaRef =
|
|
958
|
-
const fileInputRef =
|
|
1312
|
+
const [internalText, setInternalText] = useState8("");
|
|
1313
|
+
const [showEmoji, setShowEmoji] = useState8(false);
|
|
1314
|
+
const [attachments, setAttachments] = useState8([]);
|
|
1315
|
+
const textareaRef = useRef3(null);
|
|
1316
|
+
const fileInputRef = useRef3(null);
|
|
959
1317
|
const isControlled = externalValue !== void 0;
|
|
960
1318
|
const text = isControlled ? externalValue : internalText;
|
|
961
|
-
const setText =
|
|
1319
|
+
const setText = useCallback4((newText) => {
|
|
962
1320
|
if (isControlled) {
|
|
963
1321
|
externalOnChange?.(newText);
|
|
964
1322
|
} else {
|
|
@@ -967,7 +1325,7 @@ var MessageComposer = ({
|
|
|
967
1325
|
}, [isControlled, externalOnChange]);
|
|
968
1326
|
const showEmojiButton = features?.emoji !== false;
|
|
969
1327
|
const showAttachButton = features?.documents !== false;
|
|
970
|
-
const sendMessage =
|
|
1328
|
+
const sendMessage = useCallback4(() => {
|
|
971
1329
|
const trimmed = text.trim();
|
|
972
1330
|
if (!trimmed && attachments.length === 0) return;
|
|
973
1331
|
if (trimmed) onSend(trimmed);
|
|
@@ -980,19 +1338,19 @@ var MessageComposer = ({
|
|
|
980
1338
|
setShowEmoji(false);
|
|
981
1339
|
if (textareaRef.current) textareaRef.current.style.height = "auto";
|
|
982
1340
|
}, [text, attachments, onSend, onAttach, isControlled]);
|
|
983
|
-
const handleKeyDown =
|
|
1341
|
+
const handleKeyDown = useCallback4((e) => {
|
|
984
1342
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
985
1343
|
e.preventDefault();
|
|
986
1344
|
if (!disabled) sendMessage();
|
|
987
1345
|
}
|
|
988
1346
|
}, [sendMessage, disabled]);
|
|
989
|
-
const handleInput =
|
|
1347
|
+
const handleInput = useCallback4(() => {
|
|
990
1348
|
const ta = textareaRef.current;
|
|
991
1349
|
if (!ta) return;
|
|
992
1350
|
ta.style.height = "auto";
|
|
993
1351
|
ta.style.height = `${Math.min(ta.scrollHeight, 100)}px`;
|
|
994
1352
|
}, []);
|
|
995
|
-
const handleEmojiSelect =
|
|
1353
|
+
const handleEmojiSelect = useCallback4((emoji) => {
|
|
996
1354
|
const ta = textareaRef.current;
|
|
997
1355
|
if (!ta) {
|
|
998
1356
|
setText(text + emoji);
|
|
@@ -1008,7 +1366,7 @@ var MessageComposer = ({
|
|
|
1008
1366
|
handleInput();
|
|
1009
1367
|
});
|
|
1010
1368
|
}, [text, setText, handleInput]);
|
|
1011
|
-
const handleFileChange =
|
|
1369
|
+
const handleFileChange = useCallback4((e) => {
|
|
1012
1370
|
const files = e.target.files;
|
|
1013
1371
|
if (!files) return;
|
|
1014
1372
|
const previews = [];
|
|
@@ -1019,7 +1377,7 @@ var MessageComposer = ({
|
|
|
1019
1377
|
setAttachments((prev) => [...prev, ...previews]);
|
|
1020
1378
|
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
1021
1379
|
}, []);
|
|
1022
|
-
const removeAttachment =
|
|
1380
|
+
const removeAttachment = useCallback4((index) => {
|
|
1023
1381
|
setAttachments((prev) => {
|
|
1024
1382
|
const next = [...prev];
|
|
1025
1383
|
if (next[index].previewUrl) URL.revokeObjectURL(next[index].previewUrl);
|
|
@@ -1027,7 +1385,7 @@ var MessageComposer = ({
|
|
|
1027
1385
|
return next;
|
|
1028
1386
|
});
|
|
1029
1387
|
}, []);
|
|
1030
|
-
const insertFormatting =
|
|
1388
|
+
const insertFormatting = useCallback4((marker) => {
|
|
1031
1389
|
const ta = textareaRef.current;
|
|
1032
1390
|
if (!ta) return;
|
|
1033
1391
|
const start = ta.selectionStart;
|
|
@@ -1041,21 +1399,22 @@ var MessageComposer = ({
|
|
|
1041
1399
|
});
|
|
1042
1400
|
}
|
|
1043
1401
|
}, [text, setText]);
|
|
1402
|
+
const effectiveIdleAction = idleAction ?? (onAttach ? /* @__PURE__ */ jsx14(AudioRecorderButton, { onRecorded: (file) => onAttach(file) }) : void 0);
|
|
1044
1403
|
const canSend = text.trim().length > 0 || attachments.length > 0;
|
|
1045
1404
|
const remaining = maxLength ? maxLength - text.length : null;
|
|
1046
1405
|
return (
|
|
1047
1406
|
/* A barra é a superfície (cinza, largura cheia, sem raio) e o campo dentro é que arredonda —
|
|
1048
1407
|
ordem do WhatsApp. Invertido, o pill arredondado ia até a borda da tela e os cantos
|
|
1049
1408
|
descobriam o fundo branco da página, que lia como defeito. */
|
|
1050
|
-
/* @__PURE__ */
|
|
1051
|
-
quickReplies && quickReplies.length > 0 && /* @__PURE__ */
|
|
1409
|
+
/* @__PURE__ */ jsxs10("div", { className: cn("bg-[#f0f2f5] px-2 py-2", className), children: [
|
|
1410
|
+
quickReplies && quickReplies.length > 0 && /* @__PURE__ */ jsx14(
|
|
1052
1411
|
"div",
|
|
1053
1412
|
{
|
|
1054
1413
|
className: cn(
|
|
1055
1414
|
"mb-2 flex flex-nowrap gap-1 overflow-x-auto px-1 sm:flex-wrap sm:overflow-x-visible [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
1056
1415
|
classNames?.quickReplies
|
|
1057
1416
|
),
|
|
1058
|
-
children: quickReplies.map((quickReply) => /* @__PURE__ */
|
|
1417
|
+
children: quickReplies.map((quickReply) => /* @__PURE__ */ jsx14(
|
|
1059
1418
|
"button",
|
|
1060
1419
|
{
|
|
1061
1420
|
type: "button",
|
|
@@ -1073,24 +1432,24 @@ var MessageComposer = ({
|
|
|
1073
1432
|
))
|
|
1074
1433
|
}
|
|
1075
1434
|
),
|
|
1076
|
-
attachments.length > 0 && /* @__PURE__ */
|
|
1077
|
-
a.previewUrl ? /* @__PURE__ */
|
|
1078
|
-
/* @__PURE__ */
|
|
1079
|
-
/* @__PURE__ */
|
|
1435
|
+
attachments.length > 0 && /* @__PURE__ */ jsx14("div", { className: "flex gap-2 px-1 pb-2 overflow-x-auto", children: attachments.map((a, i) => /* @__PURE__ */ jsxs10("div", { className: "relative flex-shrink-0", children: [
|
|
1436
|
+
a.previewUrl ? /* @__PURE__ */ jsx14("img", { src: a.previewUrl, alt: "", className: "w-16 h-16 object-cover rounded-lg border border-gray-200" }) : /* @__PURE__ */ jsx14("div", { className: "w-16 h-16 bg-gray-100 rounded-lg border border-gray-200 flex items-center justify-center", children: /* @__PURE__ */ jsxs10("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "#9ca3af", strokeWidth: "1.5", children: [
|
|
1437
|
+
/* @__PURE__ */ jsx14("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
1438
|
+
/* @__PURE__ */ jsx14("polyline", { points: "14 2 14 8 20 8" })
|
|
1080
1439
|
] }) }),
|
|
1081
|
-
/* @__PURE__ */
|
|
1440
|
+
/* @__PURE__ */ jsx14("button", { onClick: () => removeAttachment(i), className: "absolute -top-2 -right-2 w-5 h-5 bg-gray-600 text-white rounded-full flex items-center justify-center hover:bg-gray-800 text-xs", children: "\u2715" })
|
|
1082
1441
|
] }, i)) }),
|
|
1083
|
-
/* @__PURE__ */
|
|
1084
|
-
showEmojiButton && /* @__PURE__ */
|
|
1085
|
-
/* @__PURE__ */
|
|
1086
|
-
/* @__PURE__ */
|
|
1087
|
-
/* @__PURE__ */
|
|
1088
|
-
/* @__PURE__ */
|
|
1089
|
-
/* @__PURE__ */
|
|
1442
|
+
/* @__PURE__ */ jsxs10("div", { className: cn("flex items-end gap-1.5 rounded-xl bg-white px-3 py-2", classNames?.field), children: [
|
|
1443
|
+
showEmojiButton && /* @__PURE__ */ jsxs10("div", { className: "relative flex-shrink-0", children: [
|
|
1444
|
+
/* @__PURE__ */ jsx14("button", { onClick: () => setShowEmoji((v) => !v), className: "w-9 h-9 flex items-center justify-center rounded-full text-gray-500 hover:bg-gray-200 transition-colors", "aria-label": emojiLabel, children: /* @__PURE__ */ jsxs10("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
1445
|
+
/* @__PURE__ */ jsx14("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1446
|
+
/* @__PURE__ */ jsx14("path", { d: "M8 14s1.5 2 4 2 4-2 4-2" }),
|
|
1447
|
+
/* @__PURE__ */ jsx14("circle", { cx: "9", cy: "9", r: "0.5", fill: "currentColor" }),
|
|
1448
|
+
/* @__PURE__ */ jsx14("circle", { cx: "15", cy: "9", r: "0.5", fill: "currentColor" })
|
|
1090
1449
|
] }) }),
|
|
1091
|
-
showEmoji && /* @__PURE__ */
|
|
1450
|
+
showEmoji && /* @__PURE__ */ jsx14("div", { className: "absolute bottom-full left-0 mb-2 z-10", children: /* @__PURE__ */ jsx14(EmojiPicker, { onSelect: handleEmojiSelect }) })
|
|
1092
1451
|
] }),
|
|
1093
|
-
/* @__PURE__ */
|
|
1452
|
+
/* @__PURE__ */ jsx14(
|
|
1094
1453
|
"textarea",
|
|
1095
1454
|
{
|
|
1096
1455
|
ref: textareaRef,
|
|
@@ -1107,189 +1466,31 @@ var MessageComposer = ({
|
|
|
1107
1466
|
style: { fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" }
|
|
1108
1467
|
}
|
|
1109
1468
|
),
|
|
1110
|
-
showAttachButton && /* @__PURE__ */
|
|
1111
|
-
/* @__PURE__ */
|
|
1112
|
-
/* @__PURE__ */
|
|
1469
|
+
showAttachButton && /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
1470
|
+
/* @__PURE__ */ jsx14("input", { ref: fileInputRef, type: "file", multiple: true, accept: acceptedFileTypes, onChange: handleFileChange, className: "hidden" }),
|
|
1471
|
+
/* @__PURE__ */ jsx14("button", { onClick: () => fileInputRef.current?.click(), className: "w-9 h-9 flex items-center justify-center rounded-full text-gray-500 hover:bg-gray-200 flex-shrink-0 transition-colors", "aria-label": attachLabel, children: /* @__PURE__ */ jsx14("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx14("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) }) })
|
|
1113
1472
|
] }),
|
|
1114
|
-
!canSend &&
|
|
1473
|
+
!canSend && effectiveIdleAction ? /* @__PURE__ */ jsx14("div", { className: "flex-shrink-0", children: effectiveIdleAction }) : /* @__PURE__ */ jsx14(
|
|
1115
1474
|
"button",
|
|
1116
1475
|
{
|
|
1117
1476
|
onClick: sendMessage,
|
|
1118
1477
|
disabled: !canSend || disabled,
|
|
1119
1478
|
className: `w-10 h-10 flex items-center justify-center rounded-full flex-shrink-0 transition-all ${canSend && !disabled ? "bg-[#00a884] text-white hover:bg-[#06cf9c] shadow-sm" : "bg-gray-200 text-gray-400 cursor-not-allowed"}`,
|
|
1120
1479
|
"aria-label": sendLabel,
|
|
1121
|
-
children: /* @__PURE__ */
|
|
1480
|
+
children: /* @__PURE__ */ jsx14("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx14("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) })
|
|
1122
1481
|
}
|
|
1123
1482
|
)
|
|
1124
1483
|
] }),
|
|
1125
|
-
remaining !== null && /* @__PURE__ */
|
|
1484
|
+
remaining !== null && /* @__PURE__ */ jsx14("div", { className: "flex justify-end mt-1 pr-1", children: /* @__PURE__ */ jsx14("span", { className: `text-xs ${remaining < 20 ? "text-red-500" : "text-gray-400"}`, children: remaining }) })
|
|
1126
1485
|
] })
|
|
1127
1486
|
);
|
|
1128
1487
|
};
|
|
1129
1488
|
|
|
1130
|
-
// src/AudioRecorderButton.tsx
|
|
1131
|
-
import { useCallback as useCallback3, useEffect as useEffect2, useRef as useRef3, useState as useState7 } from "react";
|
|
1132
|
-
import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1133
|
-
var DEFAULT_AUDIO_RECORDER_BUTTON_LABELS = {
|
|
1134
|
-
start: "Gravar \xE1udio",
|
|
1135
|
-
stop: "Parar grava\xE7\xE3o",
|
|
1136
|
-
unsupported: "Este navegador n\xE3o grava \xE1udio.",
|
|
1137
|
-
denied: "Sem permiss\xE3o para usar o microfone.",
|
|
1138
|
-
review: "Ou\xE7a antes de enviar",
|
|
1139
|
-
send: "Enviar \xE1udio",
|
|
1140
|
-
discard: "Descartar \xE1udio",
|
|
1141
|
-
empty: "Nada foi captado pelo microfone."
|
|
1142
|
-
};
|
|
1143
|
-
var DEFAULT_MAX_RECORDING_MILLISECONDS = 5 * 60 * 1e3;
|
|
1144
|
-
var RECORDING_FORMATS = [
|
|
1145
|
-
{ mimeType: "audio/ogg;codecs=opus", uploadMimeType: "audio/ogg", extension: "ogg" },
|
|
1146
|
-
{ mimeType: "audio/mp4", uploadMimeType: "audio/mp4", extension: "m4a" },
|
|
1147
|
-
{ mimeType: "audio/webm", uploadMimeType: "audio/webm", extension: "webm" }
|
|
1148
|
-
];
|
|
1149
|
-
function resolveRecordingFormat() {
|
|
1150
|
-
if (typeof MediaRecorder === "undefined") return void 0;
|
|
1151
|
-
if (typeof MediaRecorder.isTypeSupported !== "function") return RECORDING_FORMATS[0];
|
|
1152
|
-
return RECORDING_FORMATS.find((format) => MediaRecorder.isTypeSupported(format.mimeType));
|
|
1153
|
-
}
|
|
1154
|
-
function AudioRecorderButton({
|
|
1155
|
-
onRecorded,
|
|
1156
|
-
onFailure,
|
|
1157
|
-
onRecordingChange,
|
|
1158
|
-
reviewBeforeSend = true,
|
|
1159
|
-
maxDurationMilliseconds = DEFAULT_MAX_RECORDING_MILLISECONDS,
|
|
1160
|
-
labels,
|
|
1161
|
-
disabled
|
|
1162
|
-
}) {
|
|
1163
|
-
const labelOf = (key) => labels?.[key] ?? DEFAULT_AUDIO_RECORDER_BUTTON_LABELS[key];
|
|
1164
|
-
const [isRecording, setIsRecording] = useState7(false);
|
|
1165
|
-
const [pending, setPending] = useState7(void 0);
|
|
1166
|
-
const recorderRef = useRef3(null);
|
|
1167
|
-
const autoStopRef = useRef3(void 0);
|
|
1168
|
-
const discard = useCallback3(() => {
|
|
1169
|
-
setPending((current) => {
|
|
1170
|
-
if (current) URL.revokeObjectURL(current.objectURL);
|
|
1171
|
-
return void 0;
|
|
1172
|
-
});
|
|
1173
|
-
}, []);
|
|
1174
|
-
useEffect2(() => discard, [discard]);
|
|
1175
|
-
const confirm = useCallback3(() => {
|
|
1176
|
-
if (!pending) return;
|
|
1177
|
-
void onRecorded(pending.file);
|
|
1178
|
-
discard();
|
|
1179
|
-
}, [discard, onRecorded, pending]);
|
|
1180
|
-
const stop = useCallback3(() => {
|
|
1181
|
-
recorderRef.current?.stop();
|
|
1182
|
-
}, []);
|
|
1183
|
-
const start = useCallback3(async () => {
|
|
1184
|
-
const format = resolveRecordingFormat();
|
|
1185
|
-
if (!format || !navigator.mediaDevices?.getUserMedia) {
|
|
1186
|
-
onFailure?.(labels?.unsupported ?? DEFAULT_AUDIO_RECORDER_BUTTON_LABELS.unsupported);
|
|
1187
|
-
return;
|
|
1188
|
-
}
|
|
1189
|
-
try {
|
|
1190
|
-
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
1191
|
-
const recorder = new MediaRecorder(stream, { mimeType: format.mimeType });
|
|
1192
|
-
const chunks = [];
|
|
1193
|
-
recorder.addEventListener("dataavailable", (event) => {
|
|
1194
|
-
if (event.data.size > 0) chunks.push(event.data);
|
|
1195
|
-
});
|
|
1196
|
-
recorder.addEventListener("stop", () => {
|
|
1197
|
-
stream.getTracks().forEach((track) => track.stop());
|
|
1198
|
-
clearTimeout(autoStopRef.current);
|
|
1199
|
-
setIsRecording(false);
|
|
1200
|
-
onRecordingChange?.(false);
|
|
1201
|
-
recorderRef.current = null;
|
|
1202
|
-
const blob = new Blob(chunks, { type: format.uploadMimeType });
|
|
1203
|
-
const file = new File([blob], `audio-${Date.now()}.${format.extension}`, {
|
|
1204
|
-
type: format.uploadMimeType
|
|
1205
|
-
});
|
|
1206
|
-
if (blob.size === 0) {
|
|
1207
|
-
onFailure?.(labelOf("empty"));
|
|
1208
|
-
return;
|
|
1209
|
-
}
|
|
1210
|
-
if (!reviewBeforeSend) {
|
|
1211
|
-
void onRecorded(file);
|
|
1212
|
-
return;
|
|
1213
|
-
}
|
|
1214
|
-
setPending({ file, objectURL: URL.createObjectURL(blob) });
|
|
1215
|
-
});
|
|
1216
|
-
recorderRef.current = recorder;
|
|
1217
|
-
recorder.start();
|
|
1218
|
-
autoStopRef.current = setTimeout(() => recorder.stop(), maxDurationMilliseconds);
|
|
1219
|
-
setIsRecording(true);
|
|
1220
|
-
onRecordingChange?.(true);
|
|
1221
|
-
} catch {
|
|
1222
|
-
onFailure?.(labels?.denied ?? DEFAULT_AUDIO_RECORDER_BUTTON_LABELS.denied);
|
|
1223
|
-
}
|
|
1224
|
-
}, [maxDurationMilliseconds, onFailure, onRecorded, onRecordingChange, reviewBeforeSend]);
|
|
1225
|
-
const toggleLabel = isRecording ? labelOf("stop") : labelOf("start");
|
|
1226
|
-
return (
|
|
1227
|
-
/* O painel de revisão flutua sobre o botão em vez de ocupar espaço na barra: o microfone mora
|
|
1228
|
-
na caixa do botão de enviar, e empurrar o composer para cima a cada gravação faria a
|
|
1229
|
-
conversa saltar. */
|
|
1230
|
-
/* @__PURE__ */ jsxs9("div", { className: "relative flex-shrink-0", children: [
|
|
1231
|
-
pending && /* @__PURE__ */ jsxs9(
|
|
1232
|
-
"div",
|
|
1233
|
-
{
|
|
1234
|
-
role: "group",
|
|
1235
|
-
"aria-label": labelOf("review"),
|
|
1236
|
-
className: "absolute bottom-full right-0 z-20 mb-2 flex w-[calc(100vw-2rem)] max-w-[20rem] flex-wrap items-center justify-end gap-2 rounded-xl border border-gray-200 bg-white p-2 shadow-lg dark:border-gray-700 dark:bg-gray-800",
|
|
1237
|
-
children: [
|
|
1238
|
-
/* @__PURE__ */ jsx13("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx13(AudioPlayer, { src: pending.objectURL }) }),
|
|
1239
|
-
/* @__PURE__ */ jsx13(
|
|
1240
|
-
"button",
|
|
1241
|
-
{
|
|
1242
|
-
type: "button",
|
|
1243
|
-
onClick: discard,
|
|
1244
|
-
title: labelOf("discard"),
|
|
1245
|
-
"aria-label": labelOf("discard"),
|
|
1246
|
-
className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full text-gray-500 transition-colors hover:bg-gray-100 hover:text-red-500 dark:hover:bg-gray-700",
|
|
1247
|
-
children: /* @__PURE__ */ jsxs9("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
1248
|
-
/* @__PURE__ */ jsx13("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
1249
|
-
/* @__PURE__ */ jsx13("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
1250
|
-
] })
|
|
1251
|
-
}
|
|
1252
|
-
),
|
|
1253
|
-
/* @__PURE__ */ jsx13(
|
|
1254
|
-
"button",
|
|
1255
|
-
{
|
|
1256
|
-
type: "button",
|
|
1257
|
-
onClick: confirm,
|
|
1258
|
-
title: labelOf("send"),
|
|
1259
|
-
"aria-label": labelOf("send"),
|
|
1260
|
-
className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-emerald-500 text-white transition-colors hover:bg-emerald-600",
|
|
1261
|
-
children: /* @__PURE__ */ jsx13("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx13("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) })
|
|
1262
|
-
}
|
|
1263
|
-
)
|
|
1264
|
-
]
|
|
1265
|
-
}
|
|
1266
|
-
),
|
|
1267
|
-
/* @__PURE__ */ jsx13(
|
|
1268
|
-
"button",
|
|
1269
|
-
{
|
|
1270
|
-
type: "button",
|
|
1271
|
-
disabled: disabled || pending !== void 0,
|
|
1272
|
-
onClick: () => isRecording ? stop() : void start(),
|
|
1273
|
-
title: toggleLabel,
|
|
1274
|
-
"aria-label": toggleLabel,
|
|
1275
|
-
"aria-pressed": isRecording,
|
|
1276
|
-
className: `flex h-10 w-10 items-center justify-center rounded-full transition-colors disabled:opacity-50 ${isRecording ? "animate-pulse bg-red-500 text-white ring-4 ring-red-500/30 hover:bg-red-600" : "text-gray-500 hover:bg-gray-200 dark:hover:bg-gray-700"}`,
|
|
1277
|
-
children: isRecording ? /* @__PURE__ */ jsx13("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx13("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) }) : /* @__PURE__ */ jsxs9("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
1278
|
-
/* @__PURE__ */ jsx13("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3z" }),
|
|
1279
|
-
/* @__PURE__ */ jsx13("path", { d: "M19 11a7 7 0 0 1-14 0" }),
|
|
1280
|
-
/* @__PURE__ */ jsx13("line", { x1: "12", y1: "18", x2: "12", y2: "22" })
|
|
1281
|
-
] })
|
|
1282
|
-
}
|
|
1283
|
-
)
|
|
1284
|
-
] })
|
|
1285
|
-
);
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
1489
|
// src/DateDivider.tsx
|
|
1289
|
-
import { jsx as
|
|
1490
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
1290
1491
|
function DateDivider({ iso, className, classNames }) {
|
|
1291
1492
|
const { dateDivider } = useConversationLocales();
|
|
1292
|
-
return /* @__PURE__ */
|
|
1493
|
+
return /* @__PURE__ */ jsx15("div", { className: cn("flex justify-center sticky top-0 z-10 my-2 pointer-events-none", classNames?.root, className), children: /* @__PURE__ */ jsx15(
|
|
1293
1494
|
"span",
|
|
1294
1495
|
{
|
|
1295
1496
|
className: cn(
|
|
@@ -1356,13 +1557,13 @@ function phoneInitials(number) {
|
|
|
1356
1557
|
}
|
|
1357
1558
|
|
|
1358
1559
|
// src/hooks/useAsyncResource.ts
|
|
1359
|
-
import { useCallback as
|
|
1560
|
+
import { useCallback as useCallback5, useEffect as useEffect3, useRef as useRef4, useState as useState9 } from "react";
|
|
1360
1561
|
function useAsyncResource(fetcher, deps) {
|
|
1361
|
-
const [data, setData] =
|
|
1362
|
-
const [loading, setLoading] =
|
|
1363
|
-
const [error, setError] =
|
|
1562
|
+
const [data, setData] = useState9(void 0);
|
|
1563
|
+
const [loading, setLoading] = useState9(false);
|
|
1564
|
+
const [error, setError] = useState9(void 0);
|
|
1364
1565
|
const requestIdRef = useRef4(0);
|
|
1365
|
-
const load =
|
|
1566
|
+
const load = useCallback5(async () => {
|
|
1366
1567
|
const requestId = ++requestIdRef.current;
|
|
1367
1568
|
setLoading(true);
|
|
1368
1569
|
setError(void 0);
|
|
@@ -1410,9 +1611,9 @@ function useConversationDocuments(conversationId, params) {
|
|
|
1410
1611
|
}
|
|
1411
1612
|
|
|
1412
1613
|
// src/ConversationDocumentsPanel.tsx
|
|
1413
|
-
import { useState as
|
|
1614
|
+
import { useState as useState10 } from "react";
|
|
1414
1615
|
import { ArrowUpDown, Bot, Download, Eye, Users } from "lucide-react";
|
|
1415
|
-
import { jsx as
|
|
1616
|
+
import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1416
1617
|
var DOCUMENT_SOURCE_FILTER = {
|
|
1417
1618
|
ALL: "all",
|
|
1418
1619
|
CUSTOMER: "customer",
|
|
@@ -1452,12 +1653,12 @@ function ConversationDocumentsPanel({
|
|
|
1452
1653
|
}) {
|
|
1453
1654
|
const labels = { ...DEFAULT_CONVERSATION_DOCUMENTS_LABELS, ...labelsOverride };
|
|
1454
1655
|
const context = useConversations();
|
|
1455
|
-
const [search, setSearch] =
|
|
1456
|
-
const [sourceFilter, setSourceFilter] =
|
|
1457
|
-
const [sortDirection, setSortDirection] =
|
|
1458
|
-
const [page, setPage] =
|
|
1459
|
-
const [selectedIds, setSelectedIds] =
|
|
1460
|
-
const [archiveError, setArchiveError] =
|
|
1656
|
+
const [search, setSearch] = useState10("");
|
|
1657
|
+
const [sourceFilter, setSourceFilter] = useState10(DOCUMENT_SOURCE_FILTER.ALL);
|
|
1658
|
+
const [sortDirection, setSortDirection] = useState10("desc");
|
|
1659
|
+
const [page, setPage] = useState10(1);
|
|
1660
|
+
const [selectedIds, setSelectedIds] = useState10([]);
|
|
1661
|
+
const [archiveError, setArchiveError] = useState10(false);
|
|
1461
1662
|
const hasFilters = search !== "" || sourceFilter !== DOCUMENT_SOURCE_FILTER.ALL || sortDirection !== "desc";
|
|
1462
1663
|
const { documents, total, loading, error } = useConversationDocuments(open ? conversationId : void 0, {
|
|
1463
1664
|
search,
|
|
@@ -1508,10 +1709,10 @@ function ConversationDocumentsPanel({
|
|
|
1508
1709
|
}
|
|
1509
1710
|
}
|
|
1510
1711
|
if (!open) return null;
|
|
1511
|
-
return /* @__PURE__ */
|
|
1512
|
-
/* @__PURE__ */
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
/* @__PURE__ */
|
|
1712
|
+
return /* @__PURE__ */ jsx16("div", { className: cn("border-b", classNames?.root, className), children: /* @__PURE__ */ jsxs11("section", { className: cn("px-4 py-3", classNames?.body), children: [
|
|
1713
|
+
/* @__PURE__ */ jsx16("p", { className: cn("mb-2 text-sm font-medium", classNames?.title), children: labels.title }),
|
|
1714
|
+
/* @__PURE__ */ jsxs11("div", { className: cn("mb-2 flex flex-wrap items-center gap-2 border-b pb-2", classNames?.filters), children: [
|
|
1715
|
+
/* @__PURE__ */ jsx16(
|
|
1515
1716
|
"input",
|
|
1516
1717
|
{
|
|
1517
1718
|
type: "search",
|
|
@@ -1522,7 +1723,7 @@ function ConversationDocumentsPanel({
|
|
|
1522
1723
|
className: cn("w-full rounded-md border px-3 py-2 text-sm sm:w-52", classNames?.search)
|
|
1523
1724
|
}
|
|
1524
1725
|
),
|
|
1525
|
-
/* @__PURE__ */
|
|
1726
|
+
/* @__PURE__ */ jsxs11(
|
|
1526
1727
|
"select",
|
|
1527
1728
|
{
|
|
1528
1729
|
value: sourceFilter,
|
|
@@ -1530,25 +1731,25 @@ function ConversationDocumentsPanel({
|
|
|
1530
1731
|
"aria-label": labels.sourceFilterAll,
|
|
1531
1732
|
className: cn("w-full rounded-md border px-2 py-2 text-sm sm:w-36", classNames?.sourceSelect),
|
|
1532
1733
|
children: [
|
|
1533
|
-
/* @__PURE__ */
|
|
1534
|
-
/* @__PURE__ */
|
|
1535
|
-
/* @__PURE__ */
|
|
1734
|
+
/* @__PURE__ */ jsx16("option", { value: DOCUMENT_SOURCE_FILTER.ALL, children: labels.sourceFilterAll }),
|
|
1735
|
+
/* @__PURE__ */ jsx16("option", { value: DOCUMENT_SOURCE_FILTER.CUSTOMER, children: labels.sourceFilterCustomer }),
|
|
1736
|
+
/* @__PURE__ */ jsx16("option", { value: DOCUMENT_SOURCE_FILTER.TEAM, children: labels.sourceFilterTeam })
|
|
1536
1737
|
]
|
|
1537
1738
|
}
|
|
1538
1739
|
),
|
|
1539
|
-
/* @__PURE__ */
|
|
1740
|
+
/* @__PURE__ */ jsxs11(
|
|
1540
1741
|
"button",
|
|
1541
1742
|
{
|
|
1542
1743
|
type: "button",
|
|
1543
1744
|
onClick: () => applyFilter(() => setSortDirection(sortDirection === "desc" ? "asc" : "desc")),
|
|
1544
1745
|
className: cn("cv-header-action inline-flex items-center gap-1", classNames?.sortButton),
|
|
1545
1746
|
children: [
|
|
1546
|
-
/* @__PURE__ */
|
|
1747
|
+
/* @__PURE__ */ jsx16(ArrowUpDown, { size: 14 }),
|
|
1547
1748
|
sortDirection === "desc" ? labels.sortMostRecent : labels.sortOldest
|
|
1548
1749
|
]
|
|
1549
1750
|
}
|
|
1550
1751
|
),
|
|
1551
|
-
hasFilters ? /* @__PURE__ */
|
|
1752
|
+
hasFilters ? /* @__PURE__ */ jsx16(
|
|
1552
1753
|
"button",
|
|
1553
1754
|
{
|
|
1554
1755
|
type: "button",
|
|
@@ -1562,12 +1763,12 @@ function ConversationDocumentsPanel({
|
|
|
1562
1763
|
}
|
|
1563
1764
|
) : null
|
|
1564
1765
|
] }),
|
|
1565
|
-
loading ? /* @__PURE__ */
|
|
1566
|
-
error ? /* @__PURE__ */
|
|
1567
|
-
!loading && !error && documents.length === 0 ? /* @__PURE__ */
|
|
1568
|
-
canArchive && documents.length > 0 ? /* @__PURE__ */
|
|
1569
|
-
/* @__PURE__ */
|
|
1570
|
-
/* @__PURE__ */
|
|
1766
|
+
loading ? /* @__PURE__ */ jsx16("p", { className: cn("text-xs text-gray-500", classNames?.status), children: labels.loading }) : null,
|
|
1767
|
+
error ? /* @__PURE__ */ jsx16("p", { role: "alert", className: cn("text-xs text-red-600 dark:text-red-400", classNames?.status), children: labels.failure }) : null,
|
|
1768
|
+
!loading && !error && documents.length === 0 ? /* @__PURE__ */ jsx16("p", { className: cn("text-xs text-gray-500", classNames?.status), children: hasFilters ? labels.noResults : labels.empty }) : null,
|
|
1769
|
+
canArchive && documents.length > 0 ? /* @__PURE__ */ jsxs11("div", { className: cn("mb-2 flex flex-wrap items-center gap-3 text-xs", classNames?.selectionBar), children: [
|
|
1770
|
+
/* @__PURE__ */ jsxs11("label", { className: "inline-flex items-center gap-1.5", children: [
|
|
1771
|
+
/* @__PURE__ */ jsx16(
|
|
1571
1772
|
"input",
|
|
1572
1773
|
{
|
|
1573
1774
|
type: "checkbox",
|
|
@@ -1578,13 +1779,13 @@ function ConversationDocumentsPanel({
|
|
|
1578
1779
|
),
|
|
1579
1780
|
labels.selectAll
|
|
1580
1781
|
] }),
|
|
1581
|
-
selectedIds.length > 0 ? /* @__PURE__ */
|
|
1582
|
-
archiveError ? /* @__PURE__ */
|
|
1782
|
+
selectedIds.length > 0 ? /* @__PURE__ */ jsx16("button", { type: "button", onClick: () => void handleDownloadSelected(), className: "cv-header-action", children: labels.downloadSelected(selectedIds.length) }) : null,
|
|
1783
|
+
archiveError ? /* @__PURE__ */ jsx16("span", { role: "alert", className: "text-red-600 dark:text-red-400", children: labels.archiveFailed }) : null
|
|
1583
1784
|
] }) : null,
|
|
1584
|
-
/* @__PURE__ */
|
|
1785
|
+
/* @__PURE__ */ jsx16("ul", { className: cn("space-y-2", classNames?.list), children: documents.map((document2) => {
|
|
1585
1786
|
const isFromCustomer = !TEAM_SOURCES.has(document2.source);
|
|
1586
1787
|
const SourceIcon = isFromCustomer ? Users : Bot;
|
|
1587
|
-
return /* @__PURE__ */
|
|
1788
|
+
return /* @__PURE__ */ jsxs11(
|
|
1588
1789
|
"li",
|
|
1589
1790
|
{
|
|
1590
1791
|
className: cn(
|
|
@@ -1592,8 +1793,8 @@ function ConversationDocumentsPanel({
|
|
|
1592
1793
|
classNames?.item
|
|
1593
1794
|
),
|
|
1594
1795
|
children: [
|
|
1595
|
-
/* @__PURE__ */
|
|
1596
|
-
canArchive ? /* @__PURE__ */
|
|
1796
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
1797
|
+
canArchive ? /* @__PURE__ */ jsx16(
|
|
1597
1798
|
"input",
|
|
1598
1799
|
{
|
|
1599
1800
|
type: "checkbox",
|
|
@@ -1603,9 +1804,9 @@ function ConversationDocumentsPanel({
|
|
|
1603
1804
|
className: cn("shrink-0", classNames?.checkbox)
|
|
1604
1805
|
}
|
|
1605
1806
|
) : null,
|
|
1606
|
-
/* @__PURE__ */
|
|
1607
|
-
/* @__PURE__ */
|
|
1608
|
-
/* @__PURE__ */
|
|
1807
|
+
/* @__PURE__ */ jsx16(FileIcon, { filename: document2.filename, mimeType: document2.mimeType }),
|
|
1808
|
+
/* @__PURE__ */ jsxs11("div", { className: "min-w-0 flex-1", children: [
|
|
1809
|
+
/* @__PURE__ */ jsxs11(
|
|
1609
1810
|
"div",
|
|
1610
1811
|
{
|
|
1611
1812
|
className: cn(
|
|
@@ -1614,12 +1815,12 @@ function ConversationDocumentsPanel({
|
|
|
1614
1815
|
classNames?.sourceBadge
|
|
1615
1816
|
),
|
|
1616
1817
|
children: [
|
|
1617
|
-
/* @__PURE__ */
|
|
1818
|
+
/* @__PURE__ */ jsx16(SourceIcon, { size: 11 }),
|
|
1618
1819
|
isFromCustomer ? labels.sourceFilterCustomer : labels.sourceFilterTeam
|
|
1619
1820
|
]
|
|
1620
1821
|
}
|
|
1621
1822
|
),
|
|
1622
|
-
/* @__PURE__ */
|
|
1823
|
+
/* @__PURE__ */ jsx16(
|
|
1623
1824
|
"div",
|
|
1624
1825
|
{
|
|
1625
1826
|
className: cn("truncate text-sm font-medium", classNames?.filename),
|
|
@@ -1627,15 +1828,15 @@ function ConversationDocumentsPanel({
|
|
|
1627
1828
|
children: document2.filename
|
|
1628
1829
|
}
|
|
1629
1830
|
),
|
|
1630
|
-
/* @__PURE__ */
|
|
1831
|
+
/* @__PURE__ */ jsxs11("div", { className: cn("text-xs text-gray-500 dark:text-gray-400", classNames?.meta), children: [
|
|
1631
1832
|
formatDateTime(document2.linkedAt),
|
|
1632
1833
|
" \xB7 ",
|
|
1633
1834
|
formatFileSize(document2.sizeBytes)
|
|
1634
1835
|
] })
|
|
1635
1836
|
] })
|
|
1636
1837
|
] }),
|
|
1637
|
-
/* @__PURE__ */
|
|
1638
|
-
/* @__PURE__ */
|
|
1838
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex shrink-0 gap-1", children: [
|
|
1839
|
+
/* @__PURE__ */ jsx16(
|
|
1639
1840
|
"button",
|
|
1640
1841
|
{
|
|
1641
1842
|
type: "button",
|
|
@@ -1643,10 +1844,10 @@ function ConversationDocumentsPanel({
|
|
|
1643
1844
|
title: labels.view,
|
|
1644
1845
|
"aria-label": `${labels.view}: ${document2.filename}`,
|
|
1645
1846
|
className: cn("cv-header-icon", classNames?.viewButton),
|
|
1646
|
-
children: /* @__PURE__ */
|
|
1847
|
+
children: /* @__PURE__ */ jsx16(Eye, { size: 14 })
|
|
1647
1848
|
}
|
|
1648
1849
|
),
|
|
1649
|
-
/* @__PURE__ */
|
|
1850
|
+
/* @__PURE__ */ jsx16(
|
|
1650
1851
|
"button",
|
|
1651
1852
|
{
|
|
1652
1853
|
type: "button",
|
|
@@ -1654,7 +1855,7 @@ function ConversationDocumentsPanel({
|
|
|
1654
1855
|
title: labels.download,
|
|
1655
1856
|
"aria-label": `${labels.download}: ${document2.filename}`,
|
|
1656
1857
|
className: cn("cv-header-icon", classNames?.downloadButton),
|
|
1657
|
-
children: /* @__PURE__ */
|
|
1858
|
+
children: /* @__PURE__ */ jsx16(Download, { size: 14 })
|
|
1658
1859
|
}
|
|
1659
1860
|
)
|
|
1660
1861
|
] })
|
|
@@ -1663,7 +1864,7 @@ function ConversationDocumentsPanel({
|
|
|
1663
1864
|
document2.id
|
|
1664
1865
|
);
|
|
1665
1866
|
}) }),
|
|
1666
|
-
total > perPage ? /* @__PURE__ */
|
|
1867
|
+
total > perPage ? /* @__PURE__ */ jsxs11(
|
|
1667
1868
|
"div",
|
|
1668
1869
|
{
|
|
1669
1870
|
className: cn(
|
|
@@ -1671,9 +1872,9 @@ function ConversationDocumentsPanel({
|
|
|
1671
1872
|
classNames?.pagination
|
|
1672
1873
|
),
|
|
1673
1874
|
children: [
|
|
1674
|
-
/* @__PURE__ */
|
|
1675
|
-
/* @__PURE__ */
|
|
1676
|
-
/* @__PURE__ */
|
|
1875
|
+
/* @__PURE__ */ jsx16("span", { className: "text-gray-400", children: labels.total(total) }),
|
|
1876
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
|
|
1877
|
+
/* @__PURE__ */ jsx16(
|
|
1677
1878
|
"button",
|
|
1678
1879
|
{
|
|
1679
1880
|
type: "button",
|
|
@@ -1684,8 +1885,8 @@ function ConversationDocumentsPanel({
|
|
|
1684
1885
|
children: "\u2039"
|
|
1685
1886
|
}
|
|
1686
1887
|
),
|
|
1687
|
-
/* @__PURE__ */
|
|
1688
|
-
/* @__PURE__ */
|
|
1888
|
+
/* @__PURE__ */ jsx16("span", { className: "text-gray-500", children: labels.page(page, lastPage) }),
|
|
1889
|
+
/* @__PURE__ */ jsx16(
|
|
1689
1890
|
"button",
|
|
1690
1891
|
{
|
|
1691
1892
|
type: "button",
|
|
@@ -1704,9 +1905,9 @@ function ConversationDocumentsPanel({
|
|
|
1704
1905
|
}
|
|
1705
1906
|
|
|
1706
1907
|
// src/DocumentsLibrary.tsx
|
|
1707
|
-
import { useEffect as useEffect4, useState as
|
|
1908
|
+
import { useEffect as useEffect4, useState as useState11 } from "react";
|
|
1708
1909
|
import { ArrowUpDown as ArrowUpDown2, Bot as Bot2, Download as Download2, Eye as Eye2, MessageSquare, Users as Users2 } from "lucide-react";
|
|
1709
|
-
import { jsx as
|
|
1910
|
+
import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1710
1911
|
var DEFAULT_DOCUMENTS_LIBRARY_LABELS = {
|
|
1711
1912
|
title: "Documentos",
|
|
1712
1913
|
searchPlaceholder: "Buscar por nome do arquivo ou telefone",
|
|
@@ -1737,14 +1938,14 @@ function DocumentsLibrary({
|
|
|
1737
1938
|
}) {
|
|
1738
1939
|
const labels = { ...DEFAULT_DOCUMENTS_LIBRARY_LABELS, ...labelsOverride };
|
|
1739
1940
|
const context = useConversations();
|
|
1740
|
-
const [search, setSearch] =
|
|
1741
|
-
const [sourceFilter, setSourceFilter] =
|
|
1742
|
-
const [sortDirection, setSortDirection] =
|
|
1743
|
-
const [page, setPage] =
|
|
1744
|
-
const [documents, setDocuments] =
|
|
1745
|
-
const [total, setTotal] =
|
|
1746
|
-
const [loading, setLoading] =
|
|
1747
|
-
const [failed, setFailed] =
|
|
1941
|
+
const [search, setSearch] = useState11("");
|
|
1942
|
+
const [sourceFilter, setSourceFilter] = useState11(DOCUMENT_SOURCE_FILTER.ALL);
|
|
1943
|
+
const [sortDirection, setSortDirection] = useState11("desc");
|
|
1944
|
+
const [page, setPage] = useState11(1);
|
|
1945
|
+
const [documents, setDocuments] = useState11([]);
|
|
1946
|
+
const [total, setTotal] = useState11(0);
|
|
1947
|
+
const [loading, setLoading] = useState11(false);
|
|
1948
|
+
const [failed, setFailed] = useState11(false);
|
|
1748
1949
|
const hasFilters = search !== "" || sourceFilter !== DOCUMENT_SOURCE_FILTER.ALL || sortDirection !== "desc";
|
|
1749
1950
|
const lastPage = Math.max(1, Math.ceil(total / perPage));
|
|
1750
1951
|
const fetchAll = context?.api.getAllDocuments;
|
|
@@ -1781,10 +1982,10 @@ function DocumentsLibrary({
|
|
|
1781
1982
|
if (url) window.open(url, "_blank", "noopener,noreferrer");
|
|
1782
1983
|
}
|
|
1783
1984
|
if (!fetchAll) return null;
|
|
1784
|
-
return /* @__PURE__ */
|
|
1785
|
-
/* @__PURE__ */
|
|
1786
|
-
/* @__PURE__ */
|
|
1787
|
-
/* @__PURE__ */
|
|
1985
|
+
return /* @__PURE__ */ jsxs12("div", { className: cn("space-y-3", classNames?.root, className), children: [
|
|
1986
|
+
/* @__PURE__ */ jsx17("h2", { className: cn("text-lg font-semibold", classNames?.title), children: labels.title }),
|
|
1987
|
+
/* @__PURE__ */ jsxs12("div", { className: cn("flex flex-wrap items-center gap-2", classNames?.filters), children: [
|
|
1988
|
+
/* @__PURE__ */ jsx17(
|
|
1788
1989
|
"input",
|
|
1789
1990
|
{
|
|
1790
1991
|
type: "search",
|
|
@@ -1795,7 +1996,7 @@ function DocumentsLibrary({
|
|
|
1795
1996
|
className: cn("w-full rounded-md border px-3 py-2 text-sm sm:w-64", classNames?.search)
|
|
1796
1997
|
}
|
|
1797
1998
|
),
|
|
1798
|
-
/* @__PURE__ */
|
|
1999
|
+
/* @__PURE__ */ jsxs12(
|
|
1799
2000
|
"select",
|
|
1800
2001
|
{
|
|
1801
2002
|
value: sourceFilter,
|
|
@@ -1803,25 +2004,25 @@ function DocumentsLibrary({
|
|
|
1803
2004
|
"aria-label": labels.sourceFilterAll,
|
|
1804
2005
|
className: cn("w-full rounded-md border px-2 py-2 text-sm sm:w-40", classNames?.sourceSelect),
|
|
1805
2006
|
children: [
|
|
1806
|
-
/* @__PURE__ */
|
|
1807
|
-
/* @__PURE__ */
|
|
1808
|
-
/* @__PURE__ */
|
|
2007
|
+
/* @__PURE__ */ jsx17("option", { value: DOCUMENT_SOURCE_FILTER.ALL, children: labels.sourceFilterAll }),
|
|
2008
|
+
/* @__PURE__ */ jsx17("option", { value: DOCUMENT_SOURCE_FILTER.CUSTOMER, children: labels.sourceFilterCustomer }),
|
|
2009
|
+
/* @__PURE__ */ jsx17("option", { value: DOCUMENT_SOURCE_FILTER.TEAM, children: labels.sourceFilterTeam })
|
|
1809
2010
|
]
|
|
1810
2011
|
}
|
|
1811
2012
|
),
|
|
1812
|
-
/* @__PURE__ */
|
|
2013
|
+
/* @__PURE__ */ jsxs12(
|
|
1813
2014
|
"button",
|
|
1814
2015
|
{
|
|
1815
2016
|
type: "button",
|
|
1816
2017
|
onClick: () => applyFilter(() => setSortDirection(sortDirection === "desc" ? "asc" : "desc")),
|
|
1817
2018
|
className: cn("cv-header-action inline-flex items-center gap-1", classNames?.sortButton),
|
|
1818
2019
|
children: [
|
|
1819
|
-
/* @__PURE__ */
|
|
2020
|
+
/* @__PURE__ */ jsx17(ArrowUpDown2, { size: 14 }),
|
|
1820
2021
|
sortDirection === "desc" ? labels.sortMostRecent : labels.sortOldest
|
|
1821
2022
|
]
|
|
1822
2023
|
}
|
|
1823
2024
|
),
|
|
1824
|
-
hasFilters ? /* @__PURE__ */
|
|
2025
|
+
hasFilters ? /* @__PURE__ */ jsx17(
|
|
1825
2026
|
"button",
|
|
1826
2027
|
{
|
|
1827
2028
|
type: "button",
|
|
@@ -1835,13 +2036,13 @@ function DocumentsLibrary({
|
|
|
1835
2036
|
}
|
|
1836
2037
|
) : null
|
|
1837
2038
|
] }),
|
|
1838
|
-
loading ? /* @__PURE__ */
|
|
1839
|
-
failed ? /* @__PURE__ */
|
|
1840
|
-
!loading && !failed && documents.length === 0 ? /* @__PURE__ */
|
|
1841
|
-
/* @__PURE__ */
|
|
2039
|
+
loading ? /* @__PURE__ */ jsx17("p", { className: cn("text-sm text-gray-500", classNames?.status), children: labels.loading }) : null,
|
|
2040
|
+
failed ? /* @__PURE__ */ jsx17("p", { role: "alert", className: cn("text-sm text-red-600 dark:text-red-400", classNames?.status), children: labels.failure }) : null,
|
|
2041
|
+
!loading && !failed && documents.length === 0 ? /* @__PURE__ */ jsx17("p", { className: cn("text-sm text-gray-500", classNames?.status), children: hasFilters ? labels.noResults : labels.empty }) : null,
|
|
2042
|
+
/* @__PURE__ */ jsx17("ul", { className: cn("space-y-2", classNames?.list), children: documents.map((document2) => {
|
|
1842
2043
|
const isFromCustomer = !TEAM_SOURCES2.has(document2.source);
|
|
1843
2044
|
const SourceIcon = isFromCustomer ? Users2 : Bot2;
|
|
1844
|
-
return /* @__PURE__ */
|
|
2045
|
+
return /* @__PURE__ */ jsxs12(
|
|
1845
2046
|
"li",
|
|
1846
2047
|
{
|
|
1847
2048
|
className: cn(
|
|
@@ -1849,23 +2050,23 @@ function DocumentsLibrary({
|
|
|
1849
2050
|
classNames?.item
|
|
1850
2051
|
),
|
|
1851
2052
|
children: [
|
|
1852
|
-
/* @__PURE__ */
|
|
1853
|
-
/* @__PURE__ */
|
|
1854
|
-
/* @__PURE__ */
|
|
1855
|
-
/* @__PURE__ */
|
|
1856
|
-
/* @__PURE__ */
|
|
1857
|
-
/* @__PURE__ */
|
|
1858
|
-
/* @__PURE__ */
|
|
2053
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
|
|
2054
|
+
/* @__PURE__ */ jsx17(FileIcon, { filename: document2.filename, mimeType: document2.mimeType }),
|
|
2055
|
+
/* @__PURE__ */ jsxs12("div", { className: "min-w-0 flex-1", children: [
|
|
2056
|
+
/* @__PURE__ */ jsx17("div", { className: cn("truncate text-sm font-medium", classNames?.filename), title: document2.filename, children: document2.filename }),
|
|
2057
|
+
/* @__PURE__ */ jsxs12("div", { className: cn("flex flex-wrap items-center gap-x-2 text-xs text-gray-500", classNames?.meta), children: [
|
|
2058
|
+
/* @__PURE__ */ jsxs12("span", { className: "inline-flex items-center gap-1", children: [
|
|
2059
|
+
/* @__PURE__ */ jsx17(SourceIcon, { size: 11 }),
|
|
1859
2060
|
isFromCustomer ? labels.sourceFilterCustomer : labels.sourceFilterTeam
|
|
1860
2061
|
] }),
|
|
1861
|
-
/* @__PURE__ */
|
|
1862
|
-
/* @__PURE__ */
|
|
1863
|
-
/* @__PURE__ */
|
|
1864
|
-
/* @__PURE__ */
|
|
2062
|
+
/* @__PURE__ */ jsx17("span", { children: "\xB7" }),
|
|
2063
|
+
/* @__PURE__ */ jsx17("span", { children: formatDateTime(document2.linkedAt) }),
|
|
2064
|
+
/* @__PURE__ */ jsx17("span", { children: "\xB7" }),
|
|
2065
|
+
/* @__PURE__ */ jsx17("span", { children: formatFileSize(document2.sizeBytes) })
|
|
1865
2066
|
] })
|
|
1866
2067
|
] })
|
|
1867
2068
|
] }),
|
|
1868
|
-
onOpenConversation ? /* @__PURE__ */
|
|
2069
|
+
onOpenConversation ? /* @__PURE__ */ jsxs12(
|
|
1869
2070
|
"button",
|
|
1870
2071
|
{
|
|
1871
2072
|
type: "button",
|
|
@@ -1873,13 +2074,13 @@ function DocumentsLibrary({
|
|
|
1873
2074
|
title: labels.openConversation,
|
|
1874
2075
|
className: cn("cv-header-action inline-flex shrink-0 items-center gap-1", classNames?.conversationLink),
|
|
1875
2076
|
children: [
|
|
1876
|
-
/* @__PURE__ */
|
|
2077
|
+
/* @__PURE__ */ jsx17(MessageSquare, { size: 12 }),
|
|
1877
2078
|
formatPhone(document2.conversationId)
|
|
1878
2079
|
]
|
|
1879
2080
|
}
|
|
1880
|
-
) : /* @__PURE__ */
|
|
1881
|
-
/* @__PURE__ */
|
|
1882
|
-
/* @__PURE__ */
|
|
2081
|
+
) : /* @__PURE__ */ jsx17("span", { className: cn("shrink-0 text-xs text-gray-500", classNames?.conversationLink), children: formatPhone(document2.conversationId) }),
|
|
2082
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex shrink-0 gap-1", children: [
|
|
2083
|
+
/* @__PURE__ */ jsx17(
|
|
1883
2084
|
"button",
|
|
1884
2085
|
{
|
|
1885
2086
|
type: "button",
|
|
@@ -1887,10 +2088,10 @@ function DocumentsLibrary({
|
|
|
1887
2088
|
title: labels.view,
|
|
1888
2089
|
"aria-label": `${labels.view}: ${document2.filename}`,
|
|
1889
2090
|
className: "cv-header-icon",
|
|
1890
|
-
children: /* @__PURE__ */
|
|
2091
|
+
children: /* @__PURE__ */ jsx17(Eye2, { size: 14 })
|
|
1891
2092
|
}
|
|
1892
2093
|
),
|
|
1893
|
-
/* @__PURE__ */
|
|
2094
|
+
/* @__PURE__ */ jsx17(
|
|
1894
2095
|
"button",
|
|
1895
2096
|
{
|
|
1896
2097
|
type: "button",
|
|
@@ -1898,7 +2099,7 @@ function DocumentsLibrary({
|
|
|
1898
2099
|
title: labels.download,
|
|
1899
2100
|
"aria-label": `${labels.download}: ${document2.filename}`,
|
|
1900
2101
|
className: "cv-header-icon",
|
|
1901
|
-
children: /* @__PURE__ */
|
|
2102
|
+
children: /* @__PURE__ */ jsx17(Download2, { size: 14 })
|
|
1902
2103
|
}
|
|
1903
2104
|
)
|
|
1904
2105
|
] })
|
|
@@ -1907,10 +2108,10 @@ function DocumentsLibrary({
|
|
|
1907
2108
|
`${document2.conversationId}:${document2.id}`
|
|
1908
2109
|
);
|
|
1909
2110
|
}) }),
|
|
1910
|
-
total > perPage ? /* @__PURE__ */
|
|
1911
|
-
/* @__PURE__ */
|
|
1912
|
-
/* @__PURE__ */
|
|
1913
|
-
/* @__PURE__ */
|
|
2111
|
+
total > perPage ? /* @__PURE__ */ jsxs12("div", { className: cn("flex items-center justify-between border-t pt-2 text-xs dark:border-gray-700", classNames?.pagination), children: [
|
|
2112
|
+
/* @__PURE__ */ jsx17("span", { className: "text-gray-400", children: labels.total(total) }),
|
|
2113
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
|
|
2114
|
+
/* @__PURE__ */ jsx17(
|
|
1914
2115
|
"button",
|
|
1915
2116
|
{
|
|
1916
2117
|
type: "button",
|
|
@@ -1920,8 +2121,8 @@ function DocumentsLibrary({
|
|
|
1920
2121
|
children: "\u2039"
|
|
1921
2122
|
}
|
|
1922
2123
|
),
|
|
1923
|
-
/* @__PURE__ */
|
|
1924
|
-
/* @__PURE__ */
|
|
2124
|
+
/* @__PURE__ */ jsx17("span", { className: "text-gray-500", children: labels.page(page, lastPage) }),
|
|
2125
|
+
/* @__PURE__ */ jsx17(
|
|
1925
2126
|
"button",
|
|
1926
2127
|
{
|
|
1927
2128
|
type: "button",
|
|
@@ -1942,6 +2143,7 @@ export {
|
|
|
1942
2143
|
StatusTicks,
|
|
1943
2144
|
AudioPlayer,
|
|
1944
2145
|
cn,
|
|
2146
|
+
AudioTranscription,
|
|
1945
2147
|
FileIcon,
|
|
1946
2148
|
formatTimestamp,
|
|
1947
2149
|
formatDateTime,
|
|
@@ -1961,14 +2163,14 @@ export {
|
|
|
1961
2163
|
searchEmojis,
|
|
1962
2164
|
DEFAULT_EMOJI_PICKER_LABELS,
|
|
1963
2165
|
EmojiPicker,
|
|
2166
|
+
DEFAULT_AUDIO_RECORDER_BUTTON_LABELS,
|
|
2167
|
+
DEFAULT_MAX_RECORDING_MILLISECONDS,
|
|
2168
|
+
AudioRecorderButton,
|
|
1964
2169
|
applyQuickReplyVariables,
|
|
1965
2170
|
resolveQuickReply,
|
|
1966
2171
|
DEFAULT_MESSAGE_COMPOSER_LABELS,
|
|
1967
2172
|
DEFAULT_ACCEPTED_FILE_TYPES,
|
|
1968
2173
|
MessageComposer,
|
|
1969
|
-
DEFAULT_AUDIO_RECORDER_BUTTON_LABELS,
|
|
1970
|
-
DEFAULT_MAX_RECORDING_MILLISECONDS,
|
|
1971
|
-
AudioRecorderButton,
|
|
1972
2174
|
DateDivider,
|
|
1973
2175
|
formatPhone,
|
|
1974
2176
|
phoneCountryFlag,
|