@cimplify/sdk 0.96.0 → 0.97.0
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/react.js +379 -66
- package/dist/react.mjs +380 -67
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/registry/chat-widget.json +1 -1
package/dist/react.js
CHANGED
|
@@ -3115,8 +3115,8 @@ function fmtScheduleWindow(start, end) {
|
|
|
3115
3115
|
const startStr = dateFns.format(s, "EEE, MMM d \xB7 h:mm a");
|
|
3116
3116
|
const e = end ? dateFns.parseISO(end) : null;
|
|
3117
3117
|
if (e && dateFns.isValid(e)) {
|
|
3118
|
-
const
|
|
3119
|
-
return `${startStr} \u2013 ${dateFns.format(e,
|
|
3118
|
+
const sameDay2 = s.toDateString() === e.toDateString();
|
|
3119
|
+
return `${startStr} \u2013 ${dateFns.format(e, sameDay2 ? "h:mm a" : "EEE, MMM d \xB7 h:mm a")}`;
|
|
3120
3120
|
}
|
|
3121
3121
|
return startStr;
|
|
3122
3122
|
}
|
|
@@ -5907,6 +5907,33 @@ function useBootstrap(options = {}) {
|
|
|
5907
5907
|
const { data, isLoading, error, refetch } = bootstrapQuery(options);
|
|
5908
5908
|
return { bootstrap: data, isLoading, error, refetch };
|
|
5909
5909
|
}
|
|
5910
|
+
|
|
5911
|
+
// src/react/chat-model.ts
|
|
5912
|
+
var OPTIMISTIC_ID_PREFIX = "opt_";
|
|
5913
|
+
function optimisticId(clientId) {
|
|
5914
|
+
return `${OPTIMISTIC_ID_PREFIX}${clientId}`;
|
|
5915
|
+
}
|
|
5916
|
+
function isOptimistic(message) {
|
|
5917
|
+
return message.id.startsWith(OPTIMISTIC_ID_PREFIX);
|
|
5918
|
+
}
|
|
5919
|
+
function partitionMessages(messages) {
|
|
5920
|
+
const bubbles = [];
|
|
5921
|
+
const reactionsByTarget = /* @__PURE__ */ new Map();
|
|
5922
|
+
for (const message of messages) {
|
|
5923
|
+
if (message.content_type === "reaction") {
|
|
5924
|
+
const targetId = message.reply_to?.id;
|
|
5925
|
+
if (!targetId) continue;
|
|
5926
|
+
const list = reactionsByTarget.get(targetId);
|
|
5927
|
+
if (list) list.push(message);
|
|
5928
|
+
else reactionsByTarget.set(targetId, [message]);
|
|
5929
|
+
continue;
|
|
5930
|
+
}
|
|
5931
|
+
bubbles.push(message);
|
|
5932
|
+
}
|
|
5933
|
+
return { bubbles, reactionsByTarget };
|
|
5934
|
+
}
|
|
5935
|
+
|
|
5936
|
+
// src/react/hooks/use-chat.ts
|
|
5910
5937
|
var POLL_INTERVAL_MS = 3e3;
|
|
5911
5938
|
var POLL_IDLE_INTERVAL_MS = 15e3;
|
|
5912
5939
|
var IDLE_TIMEOUT_MS = 6e4;
|
|
@@ -5921,10 +5948,10 @@ function mergeMessages(prev, incoming) {
|
|
|
5921
5948
|
(a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
|
5922
5949
|
);
|
|
5923
5950
|
const confirmed = new Set(
|
|
5924
|
-
merged.filter((m) => !m
|
|
5951
|
+
merged.filter((m) => !isOptimistic(m) && m.client_id).map((m) => m.client_id)
|
|
5925
5952
|
);
|
|
5926
5953
|
return merged.filter(
|
|
5927
|
-
(m) => !(m
|
|
5954
|
+
(m) => !(isOptimistic(m) && m.client_id && confirmed.has(m.client_id))
|
|
5928
5955
|
);
|
|
5929
5956
|
}
|
|
5930
5957
|
function useChat(options = {}) {
|
|
@@ -5987,7 +6014,7 @@ function useChat(options = {}) {
|
|
|
5987
6014
|
const lastConfirmedMessage = React14.useCallback(() => {
|
|
5988
6015
|
const list = messagesRef.current;
|
|
5989
6016
|
for (let i = list.length - 1; i >= 0; i--) {
|
|
5990
|
-
if (!list[i]
|
|
6017
|
+
if (!isOptimistic(list[i])) return list[i];
|
|
5991
6018
|
}
|
|
5992
6019
|
return void 0;
|
|
5993
6020
|
}, []);
|
|
@@ -6035,7 +6062,7 @@ function useChat(options = {}) {
|
|
|
6035
6062
|
const snapshotIds = new Set(snapshot.map((m) => m.id));
|
|
6036
6063
|
const tail = snapshot.length > 0 ? new Date(snapshot[snapshot.length - 1].created_at).getTime() : 0;
|
|
6037
6064
|
const extras = prev.filter(
|
|
6038
|
-
(m) => !snapshotIds.has(m.id) && (m
|
|
6065
|
+
(m) => !snapshotIds.has(m.id) && (isOptimistic(m) || new Date(m.created_at).getTime() > tail)
|
|
6039
6066
|
);
|
|
6040
6067
|
return mergeMessages(snapshot, extras);
|
|
6041
6068
|
});
|
|
@@ -6221,9 +6248,9 @@ function useChat(options = {}) {
|
|
|
6221
6248
|
}
|
|
6222
6249
|
const replyTo = opts?.replyTo;
|
|
6223
6250
|
const clientId = chunkVEOAMBEX_js.generateIdempotencyKey();
|
|
6224
|
-
const
|
|
6251
|
+
const localId = optimisticId(clientId);
|
|
6225
6252
|
const optimistic = {
|
|
6226
|
-
id:
|
|
6253
|
+
id: localId,
|
|
6227
6254
|
sender_type: "customer",
|
|
6228
6255
|
content: content.trim(),
|
|
6229
6256
|
content_type: attachments.length > 0 ? "image" : "text",
|
|
@@ -6253,14 +6280,14 @@ function useChat(options = {}) {
|
|
|
6253
6280
|
// The websocket may have already streamed the confirmed message
|
|
6254
6281
|
// in while the POST was in flight; drop the optimistic bubble
|
|
6255
6282
|
// instead of inserting a duplicate.
|
|
6256
|
-
prev.some((m) => m.id === confirmed.id) ? prev.filter((m) => m.id !==
|
|
6283
|
+
prev.some((m) => m.id === confirmed.id) ? prev.filter((m) => m.id !== localId) : prev.map((m) => m.id === localId ? confirmed : m)
|
|
6257
6284
|
)
|
|
6258
6285
|
);
|
|
6259
6286
|
if (aiEnabledRef.current && !humanRequestedRef.current) {
|
|
6260
6287
|
startAwaitingReply();
|
|
6261
6288
|
}
|
|
6262
6289
|
} else {
|
|
6263
|
-
setMessages((prev) => prev.filter((m) => m.id !==
|
|
6290
|
+
setMessages((prev) => prev.filter((m) => m.id !== localId));
|
|
6264
6291
|
setError(result.error);
|
|
6265
6292
|
}
|
|
6266
6293
|
setIsSending(false);
|
|
@@ -6279,7 +6306,7 @@ function useChat(options = {}) {
|
|
|
6279
6306
|
);
|
|
6280
6307
|
const react = React14.useCallback(
|
|
6281
6308
|
async (message, emoji) => {
|
|
6282
|
-
if (!client || message
|
|
6309
|
+
if (!client || isOptimistic(message)) return false;
|
|
6283
6310
|
const result = await client.support.reactToMessage(message.id, emoji);
|
|
6284
6311
|
if (result.ok) appendMessages([result.value]);
|
|
6285
6312
|
return result.ok;
|
|
@@ -10453,7 +10480,7 @@ function CartLineItemRow({
|
|
|
10453
10480
|
});
|
|
10454
10481
|
const hasComposite = item.compositeSelections && item.compositeSelections.length > 0;
|
|
10455
10482
|
const hasBundle = item.bundleSelections && item.bundleSelections.length > 0;
|
|
10456
|
-
const
|
|
10483
|
+
const isOptimistic2 = item.isOptimistic === true;
|
|
10457
10484
|
const lineTotal = chunkPTPOQVXS_js.parsePrice(unitPrice) * item.quantity;
|
|
10458
10485
|
const imageUrl = item.product.image_url;
|
|
10459
10486
|
const variantLabel2 = item.variant ? chunkHKN3YG76_js.getVariantDisplayName(item.variant, item.product.name) : null;
|
|
@@ -10461,10 +10488,10 @@ function CartLineItemRow({
|
|
|
10461
10488
|
"div",
|
|
10462
10489
|
{
|
|
10463
10490
|
"data-cimplify-cart-item": true,
|
|
10464
|
-
"data-optimistic":
|
|
10491
|
+
"data-optimistic": isOptimistic2 ? "true" : void 0,
|
|
10465
10492
|
className: cn(
|
|
10466
10493
|
"group relative flex gap-3 py-4 border-b border-border last:border-b-0 transition-opacity",
|
|
10467
|
-
|
|
10494
|
+
isOptimistic2 && "opacity-60",
|
|
10468
10495
|
classNames?.item
|
|
10469
10496
|
),
|
|
10470
10497
|
children: [
|
|
@@ -10631,7 +10658,7 @@ function CartSummary({
|
|
|
10631
10658
|
tax,
|
|
10632
10659
|
total,
|
|
10633
10660
|
isEmpty,
|
|
10634
|
-
isOptimistic,
|
|
10661
|
+
isOptimistic: isOptimistic2,
|
|
10635
10662
|
pendingOpCount,
|
|
10636
10663
|
removeItem,
|
|
10637
10664
|
updateQuantity
|
|
@@ -10657,7 +10684,7 @@ function CartSummary({
|
|
|
10657
10684
|
"div",
|
|
10658
10685
|
{
|
|
10659
10686
|
"data-cimplify-cart-summary": true,
|
|
10660
|
-
"data-optimistic":
|
|
10687
|
+
"data-optimistic": isOptimistic2 ? "true" : void 0,
|
|
10661
10688
|
"data-pending-ops": pendingOpCount > 0 ? pendingOpCount : void 0,
|
|
10662
10689
|
className: cn(className, classNames?.root),
|
|
10663
10690
|
children: isEmpty ? /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-cart-empty": true, className: classNames?.empty, children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: emptyMessage }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -17143,25 +17170,12 @@ var ChatMarkdown = React14.memo(function ChatMarkdown2({
|
|
|
17143
17170
|
}
|
|
17144
17171
|
return /* @__PURE__ */ jsxRuntime.jsx(SettledMarkdown, { text, className });
|
|
17145
17172
|
});
|
|
17146
|
-
|
|
17147
|
-
// src/react/chat-model.ts
|
|
17148
|
-
function partitionMessages(messages) {
|
|
17149
|
-
const bubbles = [];
|
|
17150
|
-
const reactionsByTarget = /* @__PURE__ */ new Map();
|
|
17151
|
-
for (const message of messages) {
|
|
17152
|
-
if (message.content_type === "reaction") {
|
|
17153
|
-
const targetId = message.reply_to?.id;
|
|
17154
|
-
if (!targetId) continue;
|
|
17155
|
-
const list = reactionsByTarget.get(targetId);
|
|
17156
|
-
if (list) list.push(message);
|
|
17157
|
-
else reactionsByTarget.set(targetId, [message]);
|
|
17158
|
-
continue;
|
|
17159
|
-
}
|
|
17160
|
-
bubbles.push(message);
|
|
17161
|
-
}
|
|
17162
|
-
return { bubbles, reactionsByTarget };
|
|
17163
|
-
}
|
|
17164
17173
|
var MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;
|
|
17174
|
+
var KEEP_DISMISSED_KEY = "cimplify_keep_chat_dismissed";
|
|
17175
|
+
var AT_BOTTOM_SLACK_PX = 48;
|
|
17176
|
+
function proseOf(content) {
|
|
17177
|
+
return chunkXREIZ56K_js.splitGenUIContent(content).filter((segment) => segment.kind === "prose").map((segment) => segment.text).join("\n\n").trim();
|
|
17178
|
+
}
|
|
17165
17179
|
function ChatWidget({
|
|
17166
17180
|
client: clientProp,
|
|
17167
17181
|
businessName = "Support",
|
|
@@ -17180,6 +17194,7 @@ function ChatWidget({
|
|
|
17180
17194
|
const [open, setOpen] = React14.useState(defaultOpen);
|
|
17181
17195
|
const [readAt, setReadAt] = React14.useState(null);
|
|
17182
17196
|
const [attachError, setAttachError] = React14.useState(null);
|
|
17197
|
+
const [replyTo, setReplyTo] = React14.useState(null);
|
|
17183
17198
|
const {
|
|
17184
17199
|
messages,
|
|
17185
17200
|
isLoading,
|
|
@@ -17188,11 +17203,15 @@ function ChatWidget({
|
|
|
17188
17203
|
seenAt,
|
|
17189
17204
|
error,
|
|
17190
17205
|
isActive,
|
|
17206
|
+
humanRequested,
|
|
17191
17207
|
claimedAs,
|
|
17192
17208
|
signOut,
|
|
17193
17209
|
send,
|
|
17194
17210
|
react,
|
|
17211
|
+
requestHuman,
|
|
17195
17212
|
startConversation,
|
|
17213
|
+
loadOlder,
|
|
17214
|
+
mayHaveOlder,
|
|
17196
17215
|
openIfExisting
|
|
17197
17216
|
} = useChat({ client, pollInterval });
|
|
17198
17217
|
const showTyping = isSending || awaitingReply;
|
|
@@ -17227,9 +17246,11 @@ function ChatWidget({
|
|
|
17227
17246
|
const handleSend = React14.useCallback(
|
|
17228
17247
|
(content) => {
|
|
17229
17248
|
setAttachError(null);
|
|
17230
|
-
|
|
17249
|
+
const target = replyTo;
|
|
17250
|
+
setReplyTo(null);
|
|
17251
|
+
return send(content, target ? { replyTo: target } : void 0);
|
|
17231
17252
|
},
|
|
17232
|
-
[send]
|
|
17253
|
+
[send, replyTo]
|
|
17233
17254
|
);
|
|
17234
17255
|
const unread = open ? 0 : messages.filter(
|
|
17235
17256
|
(m) => m.sender_type !== "customer" && (!readAt || m.created_at > readAt)
|
|
@@ -17268,6 +17289,7 @@ function ChatWidget({
|
|
|
17268
17289
|
open && /* @__PURE__ */ jsxRuntime.jsx(
|
|
17269
17290
|
Panel,
|
|
17270
17291
|
{
|
|
17292
|
+
client,
|
|
17271
17293
|
businessName,
|
|
17272
17294
|
greeting,
|
|
17273
17295
|
subtitle,
|
|
@@ -17281,11 +17303,18 @@ function ChatWidget({
|
|
|
17281
17303
|
error: error ?? attachError,
|
|
17282
17304
|
isActive,
|
|
17283
17305
|
isLeft,
|
|
17306
|
+
humanRequested,
|
|
17307
|
+
mayHaveOlder,
|
|
17308
|
+
replyTo,
|
|
17284
17309
|
claimedAs,
|
|
17285
17310
|
onSignOut: () => void signOut(),
|
|
17286
17311
|
onClose: handleClose,
|
|
17287
17312
|
onSend: handleSend,
|
|
17288
17313
|
onReact: react,
|
|
17314
|
+
onReply: setReplyTo,
|
|
17315
|
+
onCancelReply: () => setReplyTo(null),
|
|
17316
|
+
onRequestHuman: () => void requestHuman(),
|
|
17317
|
+
onLoadOlder: () => void loadOlder(),
|
|
17289
17318
|
onAttach: attachPhoto,
|
|
17290
17319
|
onStartConversation: startConversation,
|
|
17291
17320
|
classNames
|
|
@@ -17320,7 +17349,7 @@ function Bubble({
|
|
|
17320
17349
|
{
|
|
17321
17350
|
className: cn(
|
|
17322
17351
|
"absolute -right-0.5 -top-0.5 flex h-5 w-5 items-center justify-center",
|
|
17323
|
-
"rounded-full bg-
|
|
17352
|
+
"rounded-full bg-destructive text-[10px] font-bold text-white",
|
|
17324
17353
|
"ring-2 ring-background"
|
|
17325
17354
|
),
|
|
17326
17355
|
children: unread > 9 ? "9+" : unread
|
|
@@ -17331,6 +17360,7 @@ function Bubble({
|
|
|
17331
17360
|
);
|
|
17332
17361
|
}
|
|
17333
17362
|
function Panel({
|
|
17363
|
+
client,
|
|
17334
17364
|
businessName,
|
|
17335
17365
|
greeting,
|
|
17336
17366
|
subtitle,
|
|
@@ -17344,11 +17374,18 @@ function Panel({
|
|
|
17344
17374
|
error,
|
|
17345
17375
|
isActive,
|
|
17346
17376
|
isLeft,
|
|
17377
|
+
humanRequested,
|
|
17378
|
+
mayHaveOlder,
|
|
17379
|
+
replyTo,
|
|
17347
17380
|
claimedAs,
|
|
17348
17381
|
onSignOut,
|
|
17349
17382
|
onClose,
|
|
17350
17383
|
onSend,
|
|
17351
17384
|
onReact,
|
|
17385
|
+
onReply,
|
|
17386
|
+
onCancelReply,
|
|
17387
|
+
onRequestHuman,
|
|
17388
|
+
onLoadOlder,
|
|
17352
17389
|
onAttach,
|
|
17353
17390
|
onStartConversation,
|
|
17354
17391
|
classNames
|
|
@@ -17388,20 +17425,56 @@ function Panel({
|
|
|
17388
17425
|
MessageList,
|
|
17389
17426
|
{
|
|
17390
17427
|
messages,
|
|
17428
|
+
businessName,
|
|
17391
17429
|
isLoading,
|
|
17392
17430
|
isSending: isTyping,
|
|
17393
17431
|
seenAt,
|
|
17432
|
+
humanRequested,
|
|
17433
|
+
mayHaveOlder,
|
|
17394
17434
|
onReact,
|
|
17435
|
+
onReply,
|
|
17436
|
+
onRequestHuman,
|
|
17437
|
+
onLoadOlder,
|
|
17395
17438
|
className: classNames?.messages
|
|
17396
17439
|
}
|
|
17397
17440
|
),
|
|
17398
|
-
|
|
17441
|
+
isActive && !claimedAs && client && /* @__PURE__ */ jsxRuntime.jsx(KeepConversation, { client }),
|
|
17442
|
+
humanRequested && isActive && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border-t border-border bg-muted/50 px-4 py-2 text-[11px] text-muted-foreground", children: [
|
|
17443
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 flex-shrink-0 animate-pulse rounded-full bg-amber-500" }),
|
|
17444
|
+
"Waiting for ",
|
|
17445
|
+
businessName,
|
|
17446
|
+
" \u2014 usually replies in minutes"
|
|
17447
|
+
] }),
|
|
17448
|
+
error != null && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-destructive/20 bg-destructive/10 px-4 py-2 text-xs text-destructive", children: error.userMessage || "Something went wrong. Please try again." }),
|
|
17449
|
+
replyTo && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 border-t border-border bg-muted/50 px-4 py-2", children: [
|
|
17450
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-[3px] shrink-0 self-stretch rounded-full bg-foreground/40" }),
|
|
17451
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
17452
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[11px] font-semibold text-foreground", children: [
|
|
17453
|
+
"Replying to ",
|
|
17454
|
+
replyTo.sender_type === "customer" ? "yourself" : businessName
|
|
17455
|
+
] }),
|
|
17456
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[11px] text-muted-foreground", children: proseOf(replyTo.content) || "Photo" })
|
|
17457
|
+
] }),
|
|
17458
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17459
|
+
"button",
|
|
17460
|
+
{
|
|
17461
|
+
type: "button",
|
|
17462
|
+
"aria-label": "Cancel reply",
|
|
17463
|
+
onClick: onCancelReply,
|
|
17464
|
+
className: "flex-shrink-0 p-0.5 text-muted-foreground hover:text-foreground",
|
|
17465
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, { className: "h-3.5 w-3.5" })
|
|
17466
|
+
}
|
|
17467
|
+
)
|
|
17468
|
+
] }),
|
|
17399
17469
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17400
17470
|
ChatInput,
|
|
17401
17471
|
{
|
|
17402
17472
|
placeholder,
|
|
17403
17473
|
isSending,
|
|
17404
|
-
onSend: isActive ? onSend :
|
|
17474
|
+
onSend: isActive ? onSend : async (text) => {
|
|
17475
|
+
await onStartConversation(text);
|
|
17476
|
+
return true;
|
|
17477
|
+
},
|
|
17405
17478
|
onAttach,
|
|
17406
17479
|
className: classNames?.input
|
|
17407
17480
|
}
|
|
@@ -17430,10 +17503,10 @@ function Header({
|
|
|
17430
17503
|
className
|
|
17431
17504
|
),
|
|
17432
17505
|
children: [
|
|
17433
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-[10px] bg-
|
|
17506
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-[10px] bg-background/15", children: /* @__PURE__ */ jsxRuntime.jsx(ChatIcon, { className: "h-5 w-5" }) }),
|
|
17434
17507
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
17435
17508
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[15px] font-semibold leading-tight", children: businessName }),
|
|
17436
|
-
claimedAs ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 truncate text-xs text-
|
|
17509
|
+
claimedAs ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 truncate text-xs text-background/60", children: [
|
|
17437
17510
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate", children: [
|
|
17438
17511
|
"Chatting as ",
|
|
17439
17512
|
claimedAs
|
|
@@ -17444,11 +17517,11 @@ function Header({
|
|
|
17444
17517
|
{
|
|
17445
17518
|
type: "button",
|
|
17446
17519
|
onClick: onSignOut,
|
|
17447
|
-
className: "flex-shrink-0 underline underline-offset-2 hover:text-
|
|
17520
|
+
className: "flex-shrink-0 underline underline-offset-2 hover:text-background",
|
|
17448
17521
|
children: "Not you?"
|
|
17449
17522
|
}
|
|
17450
17523
|
)
|
|
17451
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-xs text-
|
|
17524
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-xs text-background/60", children: [
|
|
17452
17525
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-[7px] w-[7px] flex-shrink-0 rounded-full bg-emerald-400" }),
|
|
17453
17526
|
"Online"
|
|
17454
17527
|
] })
|
|
@@ -17461,7 +17534,7 @@ function Header({
|
|
|
17461
17534
|
"aria-label": "Close chat",
|
|
17462
17535
|
className: cn(
|
|
17463
17536
|
"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg",
|
|
17464
|
-
"bg-
|
|
17537
|
+
"bg-background/10 text-background/70 transition-colors hover:bg-background/20 hover:text-background"
|
|
17465
17538
|
),
|
|
17466
17539
|
children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, { className: "h-[18px] w-[18px]" })
|
|
17467
17540
|
}
|
|
@@ -17524,32 +17597,73 @@ function lastSeenCustomerMessageId(bubbles, seenAt) {
|
|
|
17524
17597
|
}
|
|
17525
17598
|
return null;
|
|
17526
17599
|
}
|
|
17600
|
+
function dayLabel(at) {
|
|
17601
|
+
const date = new Date(at);
|
|
17602
|
+
const today = /* @__PURE__ */ new Date();
|
|
17603
|
+
if (date.toDateString() === today.toDateString()) return "Today";
|
|
17604
|
+
const yesterday = new Date(today);
|
|
17605
|
+
yesterday.setDate(today.getDate() - 1);
|
|
17606
|
+
if (date.toDateString() === yesterday.toDateString()) return "Yesterday";
|
|
17607
|
+
return date.toLocaleDateString([], { weekday: "short", day: "numeric", month: "short" });
|
|
17608
|
+
}
|
|
17527
17609
|
function MessageList({
|
|
17528
17610
|
messages,
|
|
17611
|
+
businessName,
|
|
17529
17612
|
isLoading,
|
|
17530
17613
|
isSending,
|
|
17531
17614
|
seenAt,
|
|
17615
|
+
humanRequested,
|
|
17616
|
+
mayHaveOlder,
|
|
17532
17617
|
onReact,
|
|
17618
|
+
onReply,
|
|
17619
|
+
onRequestHuman,
|
|
17620
|
+
onLoadOlder,
|
|
17533
17621
|
className
|
|
17534
17622
|
}) {
|
|
17535
|
-
const
|
|
17623
|
+
const scrolledUpRef = React14.useRef(false);
|
|
17624
|
+
const partitioned = partitionMessages(messages);
|
|
17625
|
+
const bubbles = partitioned.bubbles.filter(
|
|
17626
|
+
(m) => m.sender_type === "system" || m.attachments.length > 0 || (m.sender_type === "customer" ? m.content.trim() !== "" : proseOf(m.content) !== "")
|
|
17627
|
+
);
|
|
17628
|
+
const reactionsByTarget = partitioned.reactionsByTarget;
|
|
17536
17629
|
const seenMessageId = lastSeenCustomerMessageId(bubbles, seenAt);
|
|
17630
|
+
const lastBubble = bubbles[bubbles.length - 1];
|
|
17631
|
+
const offerHuman = !humanRequested && lastBubble?.sender_type === "bot" && !isSending;
|
|
17632
|
+
const handleScroll = (event) => {
|
|
17633
|
+
const node = event.currentTarget;
|
|
17634
|
+
scrolledUpRef.current = node.scrollHeight - node.scrollTop - node.clientHeight >= AT_BOTTOM_SLACK_PX;
|
|
17635
|
+
};
|
|
17537
17636
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17538
17637
|
"div",
|
|
17539
17638
|
{
|
|
17639
|
+
onScroll: handleScroll,
|
|
17540
17640
|
className: cn(
|
|
17541
17641
|
"flex flex-1 flex-col gap-1 overflow-y-auto bg-muted/50 px-5 py-5",
|
|
17542
17642
|
className
|
|
17543
17643
|
),
|
|
17544
17644
|
children: [
|
|
17545
17645
|
isLoading && messages.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
|
|
17646
|
+
mayHaveOlder && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
17647
|
+
"button",
|
|
17648
|
+
{
|
|
17649
|
+
type: "button",
|
|
17650
|
+
onClick: onLoadOlder,
|
|
17651
|
+
className: "rounded-full border border-border bg-background px-3 py-1 text-[11px] font-semibold text-muted-foreground hover:text-foreground",
|
|
17652
|
+
children: "Show earlier messages"
|
|
17653
|
+
}
|
|
17654
|
+
) }),
|
|
17546
17655
|
bubbles.map((msg, i) => {
|
|
17547
17656
|
const isCustomer = msg.sender_type === "customer";
|
|
17548
17657
|
const prev = bubbles[i - 1];
|
|
17549
17658
|
const sameSenderAsPrev = prev?.sender_type === msg.sender_type;
|
|
17550
|
-
const
|
|
17659
|
+
const optimistic = isOptimistic(msg);
|
|
17551
17660
|
const reactions = reactionsByTarget.get(msg.id);
|
|
17661
|
+
const newDay = !prev || !sameDay(prev.created_at, msg.created_at);
|
|
17662
|
+
if (msg.sender_type === "system") {
|
|
17663
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-2 text-center text-[11px] font-medium text-muted-foreground", children: msg.content }, msg.id);
|
|
17664
|
+
}
|
|
17552
17665
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
17666
|
+
newDay && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-2 text-center text-[10px] font-semibold tracking-wide text-muted-foreground", children: dayLabel(msg.created_at) }),
|
|
17553
17667
|
!sameSenderAsPrev && i > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3" }),
|
|
17554
17668
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
17555
17669
|
"div",
|
|
@@ -17559,17 +17673,7 @@ function MessageList({
|
|
|
17559
17673
|
isCustomer ? "ml-auto" : "mr-auto"
|
|
17560
17674
|
),
|
|
17561
17675
|
children: [
|
|
17562
|
-
!isCustomer && !
|
|
17563
|
-
"button",
|
|
17564
|
-
{
|
|
17565
|
-
type: "button",
|
|
17566
|
-
"aria-label": `React ${emoji}`,
|
|
17567
|
-
onClick: () => void onReact(msg, emoji),
|
|
17568
|
-
className: "flex h-6 w-6 items-center justify-center rounded-full text-[13px] hover:bg-muted",
|
|
17569
|
-
children: emoji
|
|
17570
|
-
},
|
|
17571
|
-
emoji
|
|
17572
|
-
)) }),
|
|
17676
|
+
!isCustomer && !optimistic && /* @__PURE__ */ jsxRuntime.jsx(HoverRail, { message: msg, onReact, onReply }),
|
|
17573
17677
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
17574
17678
|
"div",
|
|
17575
17679
|
{
|
|
@@ -17577,13 +17681,10 @@ function MessageList({
|
|
|
17577
17681
|
"rounded-2xl px-3.5 py-2.5 text-sm leading-relaxed",
|
|
17578
17682
|
isCustomer ? "bg-foreground text-background" : "border border-border bg-background text-foreground",
|
|
17579
17683
|
isCustomer ? sameSenderAsPrev ? "rounded-tr" : "rounded-br" : sameSenderAsPrev ? "rounded-tl" : "rounded-bl",
|
|
17580
|
-
|
|
17684
|
+
optimistic && "opacity-70"
|
|
17581
17685
|
),
|
|
17582
17686
|
children: [
|
|
17583
|
-
!
|
|
17584
|
-
/* @__PURE__ */ jsxRuntime.jsx(SparkleIcon, { className: "h-[11px] w-[11px]" }),
|
|
17585
|
-
"AI"
|
|
17586
|
-
] }),
|
|
17687
|
+
!sameSenderAsPrev && /* @__PURE__ */ jsxRuntime.jsx(SenderChip, { sender: msg.sender_type, businessName }),
|
|
17587
17688
|
msg.reply_to && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17588
17689
|
"div",
|
|
17589
17690
|
{
|
|
@@ -17631,7 +17732,7 @@ function MessageList({
|
|
|
17631
17732
|
isCustomer ? msg.content && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap", children: msg.content }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
17632
17733
|
ChatMarkdown,
|
|
17633
17734
|
{
|
|
17634
|
-
text: msg.content,
|
|
17735
|
+
text: proseOf(msg.content),
|
|
17635
17736
|
className: "[&_p]:my-0 [&_p+p]:mt-1.5 [&_ul]:my-1 [&_ul]:list-disc [&_ul]:pl-4 [&_ol]:my-1 [&_ol]:list-decimal [&_ol]:pl-4 [&_li]:my-0.5 [&_h1]:text-sm [&_h1]:font-bold [&_h2]:text-sm [&_h2]:font-bold [&_h3]:text-sm [&_h3]:font-semibold [&_hr]:my-2 [&_table]:my-1 [&_table]:w-full [&_th]:pb-1 [&_th]:text-left [&_th]:text-[10px] [&_th]:font-semibold [&_th]:uppercase [&_td]:py-1 [&_td:last-child]:text-right [&_td:last-child]:font-semibold [&_td:last-child]:tabular-nums [&_a]:underline"
|
|
17636
17737
|
}
|
|
17637
17738
|
)
|
|
@@ -17653,11 +17754,20 @@ function MessageList({
|
|
|
17653
17754
|
] }, msg.id);
|
|
17654
17755
|
}),
|
|
17655
17756
|
isSending && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
|
|
17757
|
+
offerHuman && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
17758
|
+
"button",
|
|
17759
|
+
{
|
|
17760
|
+
type: "button",
|
|
17761
|
+
onClick: onRequestHuman,
|
|
17762
|
+
className: "rounded-full border border-border bg-background px-3 py-1.5 text-xs font-semibold text-foreground hover:border-foreground",
|
|
17763
|
+
children: "Talk to a person"
|
|
17764
|
+
}
|
|
17765
|
+
) }),
|
|
17656
17766
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17657
17767
|
"div",
|
|
17658
17768
|
{
|
|
17659
17769
|
ref: (node) => {
|
|
17660
|
-
node
|
|
17770
|
+
if (node && !scrolledUpRef.current) node.scrollIntoView({ behavior: "smooth" });
|
|
17661
17771
|
}
|
|
17662
17772
|
},
|
|
17663
17773
|
`${messages.length}:${isSending}`
|
|
@@ -17666,6 +17776,190 @@ function MessageList({
|
|
|
17666
17776
|
}
|
|
17667
17777
|
);
|
|
17668
17778
|
}
|
|
17779
|
+
function sameDay(a, b) {
|
|
17780
|
+
return new Date(a).toDateString() === new Date(b).toDateString();
|
|
17781
|
+
}
|
|
17782
|
+
function SenderChip({
|
|
17783
|
+
sender,
|
|
17784
|
+
businessName
|
|
17785
|
+
}) {
|
|
17786
|
+
if (sender === "bot") {
|
|
17787
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mb-1 inline-flex items-center gap-1 rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-semibold text-primary", children: [
|
|
17788
|
+
/* @__PURE__ */ jsxRuntime.jsx(SparkleIcon, { className: "h-[11px] w-[11px]" }),
|
|
17789
|
+
"AI"
|
|
17790
|
+
] });
|
|
17791
|
+
}
|
|
17792
|
+
if (sender === "agent") {
|
|
17793
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 inline-flex items-center rounded bg-muted px-1.5 py-0.5 text-[10px] font-semibold text-muted-foreground", children: businessName });
|
|
17794
|
+
}
|
|
17795
|
+
return null;
|
|
17796
|
+
}
|
|
17797
|
+
function HoverRail({
|
|
17798
|
+
message,
|
|
17799
|
+
onReact,
|
|
17800
|
+
onReply
|
|
17801
|
+
}) {
|
|
17802
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute -top-3 right-0 z-10 hidden gap-0.5 rounded-full border border-border bg-background px-1 py-0.5 shadow-sm group-hover:flex", children: [
|
|
17803
|
+
QUICK_REACTIONS.map((emoji) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
17804
|
+
"button",
|
|
17805
|
+
{
|
|
17806
|
+
type: "button",
|
|
17807
|
+
"aria-label": `React ${emoji}`,
|
|
17808
|
+
onClick: () => void onReact(message, emoji),
|
|
17809
|
+
className: "flex h-6 w-6 items-center justify-center rounded-full text-[13px] hover:bg-muted",
|
|
17810
|
+
children: emoji
|
|
17811
|
+
},
|
|
17812
|
+
emoji
|
|
17813
|
+
)),
|
|
17814
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17815
|
+
"button",
|
|
17816
|
+
{
|
|
17817
|
+
type: "button",
|
|
17818
|
+
"aria-label": "Reply",
|
|
17819
|
+
onClick: () => onReply(message),
|
|
17820
|
+
className: "flex h-6 w-6 items-center justify-center rounded-full text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
17821
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ReplyIcon, { className: "h-3.5 w-3.5" })
|
|
17822
|
+
}
|
|
17823
|
+
)
|
|
17824
|
+
] });
|
|
17825
|
+
}
|
|
17826
|
+
function readKeepDismissed() {
|
|
17827
|
+
try {
|
|
17828
|
+
return typeof window !== "undefined" && window.localStorage.getItem(KEEP_DISMISSED_KEY) === "1";
|
|
17829
|
+
} catch {
|
|
17830
|
+
return false;
|
|
17831
|
+
}
|
|
17832
|
+
}
|
|
17833
|
+
function writeKeepDismissed() {
|
|
17834
|
+
try {
|
|
17835
|
+
window.localStorage.setItem(KEEP_DISMISSED_KEY, "1");
|
|
17836
|
+
} catch {
|
|
17837
|
+
}
|
|
17838
|
+
}
|
|
17839
|
+
function KeepConversation({ client }) {
|
|
17840
|
+
const [step, setStep] = React14.useState("row");
|
|
17841
|
+
const [dismissed, setDismissed] = React14.useState(readKeepDismissed);
|
|
17842
|
+
const [contact, setContact] = React14.useState("");
|
|
17843
|
+
const [code, setCode] = React14.useState("");
|
|
17844
|
+
const [busy, setBusy] = React14.useState(false);
|
|
17845
|
+
const [verifyError, setVerifyError] = React14.useState(null);
|
|
17846
|
+
if (dismissed) return null;
|
|
17847
|
+
const contactType = contact.includes("@") ? "email" : "phone";
|
|
17848
|
+
const requestCode = async () => {
|
|
17849
|
+
const trimmed = contact.trim();
|
|
17850
|
+
if (!trimmed || busy) return;
|
|
17851
|
+
setBusy(true);
|
|
17852
|
+
setVerifyError(null);
|
|
17853
|
+
const result = await client.link.requestOtp({ contact: trimmed, contact_type: contactType });
|
|
17854
|
+
setBusy(false);
|
|
17855
|
+
if (result.ok) {
|
|
17856
|
+
setCode("");
|
|
17857
|
+
setStep("code");
|
|
17858
|
+
} else {
|
|
17859
|
+
setVerifyError(result.error.userMessage || "Couldn't send a code \u2014 try again.");
|
|
17860
|
+
}
|
|
17861
|
+
};
|
|
17862
|
+
const verify = async (value) => {
|
|
17863
|
+
if (value.length !== 6 || busy) return;
|
|
17864
|
+
setBusy(true);
|
|
17865
|
+
setVerifyError(null);
|
|
17866
|
+
const result = await client.link.verifyOtp({
|
|
17867
|
+
contact: contact.trim(),
|
|
17868
|
+
contact_type: contactType,
|
|
17869
|
+
otp_code: value
|
|
17870
|
+
});
|
|
17871
|
+
setBusy(false);
|
|
17872
|
+
if (!result.ok) {
|
|
17873
|
+
setCode("");
|
|
17874
|
+
setVerifyError(result.error.userMessage || "Invalid or expired code.");
|
|
17875
|
+
}
|
|
17876
|
+
};
|
|
17877
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0 border-t border-border bg-muted/50 px-4 py-2.5", children: [
|
|
17878
|
+
step === "row" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
|
|
17879
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
17880
|
+
"button",
|
|
17881
|
+
{
|
|
17882
|
+
type: "button",
|
|
17883
|
+
onClick: () => setStep("contact"),
|
|
17884
|
+
className: "flex min-w-0 flex-1 flex-col text-left",
|
|
17885
|
+
children: [
|
|
17886
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-foreground", children: "Keep this conversation" }),
|
|
17887
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: "Verify your number and it follows you \u2014 any device" })
|
|
17888
|
+
]
|
|
17889
|
+
}
|
|
17890
|
+
),
|
|
17891
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17892
|
+
"button",
|
|
17893
|
+
{
|
|
17894
|
+
type: "button",
|
|
17895
|
+
"aria-label": "Dismiss",
|
|
17896
|
+
onClick: () => {
|
|
17897
|
+
writeKeepDismissed();
|
|
17898
|
+
setDismissed(true);
|
|
17899
|
+
},
|
|
17900
|
+
className: "flex-shrink-0 p-1 text-muted-foreground hover:text-foreground",
|
|
17901
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, { className: "h-3.5 w-3.5" })
|
|
17902
|
+
}
|
|
17903
|
+
)
|
|
17904
|
+
] }),
|
|
17905
|
+
step === "contact" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
17906
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17907
|
+
"input",
|
|
17908
|
+
{
|
|
17909
|
+
type: "text",
|
|
17910
|
+
inputMode: "email",
|
|
17911
|
+
value: contact,
|
|
17912
|
+
onChange: (e) => setContact(e.target.value),
|
|
17913
|
+
onKeyDown: (e) => e.key === "Enter" && void requestCode(),
|
|
17914
|
+
placeholder: "Phone or email",
|
|
17915
|
+
autoFocus: true,
|
|
17916
|
+
className: "h-9 min-w-0 flex-1 rounded-lg border border-border bg-background px-3 text-xs text-foreground outline-none placeholder:text-muted-foreground focus:border-foreground"
|
|
17917
|
+
}
|
|
17918
|
+
),
|
|
17919
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17920
|
+
"button",
|
|
17921
|
+
{
|
|
17922
|
+
type: "button",
|
|
17923
|
+
onClick: () => void requestCode(),
|
|
17924
|
+
disabled: !contact.trim() || busy,
|
|
17925
|
+
className: "h-9 flex-shrink-0 rounded-lg bg-foreground px-3 text-xs font-semibold text-background disabled:opacity-50",
|
|
17926
|
+
children: busy ? "Sending\u2026" : "Send code"
|
|
17927
|
+
}
|
|
17928
|
+
)
|
|
17929
|
+
] }),
|
|
17930
|
+
step === "code" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
17931
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17932
|
+
"input",
|
|
17933
|
+
{
|
|
17934
|
+
type: "text",
|
|
17935
|
+
inputMode: "numeric",
|
|
17936
|
+
autoComplete: "one-time-code",
|
|
17937
|
+
maxLength: 6,
|
|
17938
|
+
value: code,
|
|
17939
|
+
onChange: (e) => {
|
|
17940
|
+
const digits = e.target.value.replace(/\D/g, "").slice(0, 6);
|
|
17941
|
+
setCode(digits);
|
|
17942
|
+
if (digits.length === 6) void verify(digits);
|
|
17943
|
+
},
|
|
17944
|
+
placeholder: "6-digit code",
|
|
17945
|
+
autoFocus: true,
|
|
17946
|
+
className: "h-9 min-w-0 flex-1 rounded-lg border border-border bg-background px-3 text-xs tracking-[0.2em] text-foreground outline-none placeholder:tracking-normal placeholder:text-muted-foreground focus:border-foreground"
|
|
17947
|
+
}
|
|
17948
|
+
),
|
|
17949
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17950
|
+
"button",
|
|
17951
|
+
{
|
|
17952
|
+
type: "button",
|
|
17953
|
+
onClick: () => void requestCode(),
|
|
17954
|
+
disabled: busy,
|
|
17955
|
+
className: "h-9 flex-shrink-0 rounded-lg px-2 text-[11px] font-medium text-muted-foreground hover:text-foreground disabled:opacity-50",
|
|
17956
|
+
children: "Resend"
|
|
17957
|
+
}
|
|
17958
|
+
)
|
|
17959
|
+
] }),
|
|
17960
|
+
verifyError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1.5 text-[11px] text-destructive", children: verifyError })
|
|
17961
|
+
] });
|
|
17962
|
+
}
|
|
17669
17963
|
function ChatInput({
|
|
17670
17964
|
placeholder,
|
|
17671
17965
|
isSending,
|
|
@@ -17688,14 +17982,15 @@ function ChatInput({
|
|
|
17688
17982
|
},
|
|
17689
17983
|
[onAttach]
|
|
17690
17984
|
);
|
|
17691
|
-
const handleSend = React14.useCallback(() => {
|
|
17985
|
+
const handleSend = React14.useCallback(async () => {
|
|
17692
17986
|
const trimmed = value.trim();
|
|
17693
17987
|
if (!trimmed || isSending) return;
|
|
17694
17988
|
setValue("");
|
|
17695
17989
|
if (textareaRef.current) {
|
|
17696
17990
|
textareaRef.current.style.height = "auto";
|
|
17697
17991
|
}
|
|
17698
|
-
onSend(trimmed);
|
|
17992
|
+
const sent = await onSend(trimmed);
|
|
17993
|
+
if (!sent) setValue((current) => current || trimmed);
|
|
17699
17994
|
}, [value, isSending, onSend]);
|
|
17700
17995
|
const handleKeyDown = React14.useCallback(
|
|
17701
17996
|
(e) => {
|
|
@@ -17873,6 +18168,24 @@ function ImageIcon({ className }) {
|
|
|
17873
18168
|
}
|
|
17874
18169
|
);
|
|
17875
18170
|
}
|
|
18171
|
+
function ReplyIcon({ className }) {
|
|
18172
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
18173
|
+
"svg",
|
|
18174
|
+
{
|
|
18175
|
+
className,
|
|
18176
|
+
viewBox: "0 0 24 24",
|
|
18177
|
+
fill: "none",
|
|
18178
|
+
stroke: "currentColor",
|
|
18179
|
+
strokeWidth: 2,
|
|
18180
|
+
strokeLinecap: "round",
|
|
18181
|
+
strokeLinejoin: "round",
|
|
18182
|
+
children: [
|
|
18183
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "9 17 4 12 9 7" }),
|
|
18184
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 18v-2a4 4 0 0 0-4-4H4" })
|
|
18185
|
+
]
|
|
18186
|
+
}
|
|
18187
|
+
);
|
|
18188
|
+
}
|
|
17876
18189
|
function SparkleIcon({ className }) {
|
|
17877
18190
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
17878
18191
|
"svg",
|