@cimplify/sdk 0.95.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.d.mts +10 -2
- package/dist/react.d.ts +10 -2
- package/dist/react.js +392 -69
- package/dist/react.mjs +393 -70
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/registry/chat-widget.json +1 -1
- package/registry/date-slot-picker.json +1 -1
- package/registry/slot-picker.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;
|
|
@@ -9263,6 +9290,7 @@ function SlotPicker({
|
|
|
9263
9290
|
hideElapsedSlots = true,
|
|
9264
9291
|
minLeadMinutes = 0,
|
|
9265
9292
|
emptyMessage = "No available slots",
|
|
9293
|
+
contactPhone,
|
|
9266
9294
|
className,
|
|
9267
9295
|
classNames
|
|
9268
9296
|
}) {
|
|
@@ -9295,13 +9323,20 @@ function SlotPicker({
|
|
|
9295
9323
|
);
|
|
9296
9324
|
}
|
|
9297
9325
|
if (slots.length === 0) {
|
|
9298
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
9326
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9299
9327
|
"div",
|
|
9300
9328
|
{
|
|
9301
9329
|
"data-cimplify-slot-picker": true,
|
|
9302
9330
|
"data-empty": true,
|
|
9303
9331
|
className: cn(className, classNames?.root, classNames?.empty),
|
|
9304
|
-
children:
|
|
9332
|
+
children: [
|
|
9333
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: emptyMessage }),
|
|
9334
|
+
contactPhone ? /* @__PURE__ */ jsxRuntime.jsx("p", { children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: `tel:${contactPhone.replace(/\s+/g, "")}`, children: [
|
|
9335
|
+
"Call ",
|
|
9336
|
+
contactPhone,
|
|
9337
|
+
" to book"
|
|
9338
|
+
] }) }) : null
|
|
9339
|
+
]
|
|
9305
9340
|
}
|
|
9306
9341
|
);
|
|
9307
9342
|
}
|
|
@@ -9428,6 +9463,7 @@ function DateSlotPicker({
|
|
|
9428
9463
|
durationValue,
|
|
9429
9464
|
hideElapsedSlots = true,
|
|
9430
9465
|
minLeadMinutes = 0,
|
|
9466
|
+
contactPhone,
|
|
9431
9467
|
className,
|
|
9432
9468
|
classNames
|
|
9433
9469
|
}) {
|
|
@@ -9577,7 +9613,8 @@ function DateSlotPicker({
|
|
|
9577
9613
|
durationUnit,
|
|
9578
9614
|
durationValue,
|
|
9579
9615
|
hideElapsedSlots,
|
|
9580
|
-
minLeadMinutes
|
|
9616
|
+
minLeadMinutes,
|
|
9617
|
+
contactPhone
|
|
9581
9618
|
}
|
|
9582
9619
|
) })
|
|
9583
9620
|
]
|
|
@@ -10443,7 +10480,7 @@ function CartLineItemRow({
|
|
|
10443
10480
|
});
|
|
10444
10481
|
const hasComposite = item.compositeSelections && item.compositeSelections.length > 0;
|
|
10445
10482
|
const hasBundle = item.bundleSelections && item.bundleSelections.length > 0;
|
|
10446
|
-
const
|
|
10483
|
+
const isOptimistic2 = item.isOptimistic === true;
|
|
10447
10484
|
const lineTotal = chunkPTPOQVXS_js.parsePrice(unitPrice) * item.quantity;
|
|
10448
10485
|
const imageUrl = item.product.image_url;
|
|
10449
10486
|
const variantLabel2 = item.variant ? chunkHKN3YG76_js.getVariantDisplayName(item.variant, item.product.name) : null;
|
|
@@ -10451,10 +10488,10 @@ function CartLineItemRow({
|
|
|
10451
10488
|
"div",
|
|
10452
10489
|
{
|
|
10453
10490
|
"data-cimplify-cart-item": true,
|
|
10454
|
-
"data-optimistic":
|
|
10491
|
+
"data-optimistic": isOptimistic2 ? "true" : void 0,
|
|
10455
10492
|
className: cn(
|
|
10456
10493
|
"group relative flex gap-3 py-4 border-b border-border last:border-b-0 transition-opacity",
|
|
10457
|
-
|
|
10494
|
+
isOptimistic2 && "opacity-60",
|
|
10458
10495
|
classNames?.item
|
|
10459
10496
|
),
|
|
10460
10497
|
children: [
|
|
@@ -10621,7 +10658,7 @@ function CartSummary({
|
|
|
10621
10658
|
tax,
|
|
10622
10659
|
total,
|
|
10623
10660
|
isEmpty,
|
|
10624
|
-
isOptimistic,
|
|
10661
|
+
isOptimistic: isOptimistic2,
|
|
10625
10662
|
pendingOpCount,
|
|
10626
10663
|
removeItem,
|
|
10627
10664
|
updateQuantity
|
|
@@ -10647,7 +10684,7 @@ function CartSummary({
|
|
|
10647
10684
|
"div",
|
|
10648
10685
|
{
|
|
10649
10686
|
"data-cimplify-cart-summary": true,
|
|
10650
|
-
"data-optimistic":
|
|
10687
|
+
"data-optimistic": isOptimistic2 ? "true" : void 0,
|
|
10651
10688
|
"data-pending-ops": pendingOpCount > 0 ? pendingOpCount : void 0,
|
|
10652
10689
|
className: cn(className, classNames?.root),
|
|
10653
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: [
|
|
@@ -17133,25 +17170,12 @@ var ChatMarkdown = React14.memo(function ChatMarkdown2({
|
|
|
17133
17170
|
}
|
|
17134
17171
|
return /* @__PURE__ */ jsxRuntime.jsx(SettledMarkdown, { text, className });
|
|
17135
17172
|
});
|
|
17136
|
-
|
|
17137
|
-
// src/react/chat-model.ts
|
|
17138
|
-
function partitionMessages(messages) {
|
|
17139
|
-
const bubbles = [];
|
|
17140
|
-
const reactionsByTarget = /* @__PURE__ */ new Map();
|
|
17141
|
-
for (const message of messages) {
|
|
17142
|
-
if (message.content_type === "reaction") {
|
|
17143
|
-
const targetId = message.reply_to?.id;
|
|
17144
|
-
if (!targetId) continue;
|
|
17145
|
-
const list = reactionsByTarget.get(targetId);
|
|
17146
|
-
if (list) list.push(message);
|
|
17147
|
-
else reactionsByTarget.set(targetId, [message]);
|
|
17148
|
-
continue;
|
|
17149
|
-
}
|
|
17150
|
-
bubbles.push(message);
|
|
17151
|
-
}
|
|
17152
|
-
return { bubbles, reactionsByTarget };
|
|
17153
|
-
}
|
|
17154
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
|
+
}
|
|
17155
17179
|
function ChatWidget({
|
|
17156
17180
|
client: clientProp,
|
|
17157
17181
|
businessName = "Support",
|
|
@@ -17170,6 +17194,7 @@ function ChatWidget({
|
|
|
17170
17194
|
const [open, setOpen] = React14.useState(defaultOpen);
|
|
17171
17195
|
const [readAt, setReadAt] = React14.useState(null);
|
|
17172
17196
|
const [attachError, setAttachError] = React14.useState(null);
|
|
17197
|
+
const [replyTo, setReplyTo] = React14.useState(null);
|
|
17173
17198
|
const {
|
|
17174
17199
|
messages,
|
|
17175
17200
|
isLoading,
|
|
@@ -17178,11 +17203,15 @@ function ChatWidget({
|
|
|
17178
17203
|
seenAt,
|
|
17179
17204
|
error,
|
|
17180
17205
|
isActive,
|
|
17206
|
+
humanRequested,
|
|
17181
17207
|
claimedAs,
|
|
17182
17208
|
signOut,
|
|
17183
17209
|
send,
|
|
17184
17210
|
react,
|
|
17211
|
+
requestHuman,
|
|
17185
17212
|
startConversation,
|
|
17213
|
+
loadOlder,
|
|
17214
|
+
mayHaveOlder,
|
|
17186
17215
|
openIfExisting
|
|
17187
17216
|
} = useChat({ client, pollInterval });
|
|
17188
17217
|
const showTyping = isSending || awaitingReply;
|
|
@@ -17217,9 +17246,11 @@ function ChatWidget({
|
|
|
17217
17246
|
const handleSend = React14.useCallback(
|
|
17218
17247
|
(content) => {
|
|
17219
17248
|
setAttachError(null);
|
|
17220
|
-
|
|
17249
|
+
const target = replyTo;
|
|
17250
|
+
setReplyTo(null);
|
|
17251
|
+
return send(content, target ? { replyTo: target } : void 0);
|
|
17221
17252
|
},
|
|
17222
|
-
[send]
|
|
17253
|
+
[send, replyTo]
|
|
17223
17254
|
);
|
|
17224
17255
|
const unread = open ? 0 : messages.filter(
|
|
17225
17256
|
(m) => m.sender_type !== "customer" && (!readAt || m.created_at > readAt)
|
|
@@ -17258,6 +17289,7 @@ function ChatWidget({
|
|
|
17258
17289
|
open && /* @__PURE__ */ jsxRuntime.jsx(
|
|
17259
17290
|
Panel,
|
|
17260
17291
|
{
|
|
17292
|
+
client,
|
|
17261
17293
|
businessName,
|
|
17262
17294
|
greeting,
|
|
17263
17295
|
subtitle,
|
|
@@ -17271,11 +17303,18 @@ function ChatWidget({
|
|
|
17271
17303
|
error: error ?? attachError,
|
|
17272
17304
|
isActive,
|
|
17273
17305
|
isLeft,
|
|
17306
|
+
humanRequested,
|
|
17307
|
+
mayHaveOlder,
|
|
17308
|
+
replyTo,
|
|
17274
17309
|
claimedAs,
|
|
17275
17310
|
onSignOut: () => void signOut(),
|
|
17276
17311
|
onClose: handleClose,
|
|
17277
17312
|
onSend: handleSend,
|
|
17278
17313
|
onReact: react,
|
|
17314
|
+
onReply: setReplyTo,
|
|
17315
|
+
onCancelReply: () => setReplyTo(null),
|
|
17316
|
+
onRequestHuman: () => void requestHuman(),
|
|
17317
|
+
onLoadOlder: () => void loadOlder(),
|
|
17279
17318
|
onAttach: attachPhoto,
|
|
17280
17319
|
onStartConversation: startConversation,
|
|
17281
17320
|
classNames
|
|
@@ -17310,7 +17349,7 @@ function Bubble({
|
|
|
17310
17349
|
{
|
|
17311
17350
|
className: cn(
|
|
17312
17351
|
"absolute -right-0.5 -top-0.5 flex h-5 w-5 items-center justify-center",
|
|
17313
|
-
"rounded-full bg-
|
|
17352
|
+
"rounded-full bg-destructive text-[10px] font-bold text-white",
|
|
17314
17353
|
"ring-2 ring-background"
|
|
17315
17354
|
),
|
|
17316
17355
|
children: unread > 9 ? "9+" : unread
|
|
@@ -17321,6 +17360,7 @@ function Bubble({
|
|
|
17321
17360
|
);
|
|
17322
17361
|
}
|
|
17323
17362
|
function Panel({
|
|
17363
|
+
client,
|
|
17324
17364
|
businessName,
|
|
17325
17365
|
greeting,
|
|
17326
17366
|
subtitle,
|
|
@@ -17334,11 +17374,18 @@ function Panel({
|
|
|
17334
17374
|
error,
|
|
17335
17375
|
isActive,
|
|
17336
17376
|
isLeft,
|
|
17377
|
+
humanRequested,
|
|
17378
|
+
mayHaveOlder,
|
|
17379
|
+
replyTo,
|
|
17337
17380
|
claimedAs,
|
|
17338
17381
|
onSignOut,
|
|
17339
17382
|
onClose,
|
|
17340
17383
|
onSend,
|
|
17341
17384
|
onReact,
|
|
17385
|
+
onReply,
|
|
17386
|
+
onCancelReply,
|
|
17387
|
+
onRequestHuman,
|
|
17388
|
+
onLoadOlder,
|
|
17342
17389
|
onAttach,
|
|
17343
17390
|
onStartConversation,
|
|
17344
17391
|
classNames
|
|
@@ -17378,20 +17425,56 @@ function Panel({
|
|
|
17378
17425
|
MessageList,
|
|
17379
17426
|
{
|
|
17380
17427
|
messages,
|
|
17428
|
+
businessName,
|
|
17381
17429
|
isLoading,
|
|
17382
17430
|
isSending: isTyping,
|
|
17383
17431
|
seenAt,
|
|
17432
|
+
humanRequested,
|
|
17433
|
+
mayHaveOlder,
|
|
17384
17434
|
onReact,
|
|
17435
|
+
onReply,
|
|
17436
|
+
onRequestHuman,
|
|
17437
|
+
onLoadOlder,
|
|
17385
17438
|
className: classNames?.messages
|
|
17386
17439
|
}
|
|
17387
17440
|
),
|
|
17388
|
-
|
|
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
|
+
] }),
|
|
17389
17469
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17390
17470
|
ChatInput,
|
|
17391
17471
|
{
|
|
17392
17472
|
placeholder,
|
|
17393
17473
|
isSending,
|
|
17394
|
-
onSend: isActive ? onSend :
|
|
17474
|
+
onSend: isActive ? onSend : async (text) => {
|
|
17475
|
+
await onStartConversation(text);
|
|
17476
|
+
return true;
|
|
17477
|
+
},
|
|
17395
17478
|
onAttach,
|
|
17396
17479
|
className: classNames?.input
|
|
17397
17480
|
}
|
|
@@ -17420,10 +17503,10 @@ function Header({
|
|
|
17420
17503
|
className
|
|
17421
17504
|
),
|
|
17422
17505
|
children: [
|
|
17423
|
-
/* @__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" }) }),
|
|
17424
17507
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
17425
17508
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[15px] font-semibold leading-tight", children: businessName }),
|
|
17426
|
-
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: [
|
|
17427
17510
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate", children: [
|
|
17428
17511
|
"Chatting as ",
|
|
17429
17512
|
claimedAs
|
|
@@ -17434,11 +17517,11 @@ function Header({
|
|
|
17434
17517
|
{
|
|
17435
17518
|
type: "button",
|
|
17436
17519
|
onClick: onSignOut,
|
|
17437
|
-
className: "flex-shrink-0 underline underline-offset-2 hover:text-
|
|
17520
|
+
className: "flex-shrink-0 underline underline-offset-2 hover:text-background",
|
|
17438
17521
|
children: "Not you?"
|
|
17439
17522
|
}
|
|
17440
17523
|
)
|
|
17441
|
-
] }) : /* @__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: [
|
|
17442
17525
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-[7px] w-[7px] flex-shrink-0 rounded-full bg-emerald-400" }),
|
|
17443
17526
|
"Online"
|
|
17444
17527
|
] })
|
|
@@ -17451,7 +17534,7 @@ function Header({
|
|
|
17451
17534
|
"aria-label": "Close chat",
|
|
17452
17535
|
className: cn(
|
|
17453
17536
|
"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg",
|
|
17454
|
-
"bg-
|
|
17537
|
+
"bg-background/10 text-background/70 transition-colors hover:bg-background/20 hover:text-background"
|
|
17455
17538
|
),
|
|
17456
17539
|
children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, { className: "h-[18px] w-[18px]" })
|
|
17457
17540
|
}
|
|
@@ -17514,32 +17597,73 @@ function lastSeenCustomerMessageId(bubbles, seenAt) {
|
|
|
17514
17597
|
}
|
|
17515
17598
|
return null;
|
|
17516
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
|
+
}
|
|
17517
17609
|
function MessageList({
|
|
17518
17610
|
messages,
|
|
17611
|
+
businessName,
|
|
17519
17612
|
isLoading,
|
|
17520
17613
|
isSending,
|
|
17521
17614
|
seenAt,
|
|
17615
|
+
humanRequested,
|
|
17616
|
+
mayHaveOlder,
|
|
17522
17617
|
onReact,
|
|
17618
|
+
onReply,
|
|
17619
|
+
onRequestHuman,
|
|
17620
|
+
onLoadOlder,
|
|
17523
17621
|
className
|
|
17524
17622
|
}) {
|
|
17525
|
-
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;
|
|
17526
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
|
+
};
|
|
17527
17636
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17528
17637
|
"div",
|
|
17529
17638
|
{
|
|
17639
|
+
onScroll: handleScroll,
|
|
17530
17640
|
className: cn(
|
|
17531
17641
|
"flex flex-1 flex-col gap-1 overflow-y-auto bg-muted/50 px-5 py-5",
|
|
17532
17642
|
className
|
|
17533
17643
|
),
|
|
17534
17644
|
children: [
|
|
17535
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
|
+
) }),
|
|
17536
17655
|
bubbles.map((msg, i) => {
|
|
17537
17656
|
const isCustomer = msg.sender_type === "customer";
|
|
17538
17657
|
const prev = bubbles[i - 1];
|
|
17539
17658
|
const sameSenderAsPrev = prev?.sender_type === msg.sender_type;
|
|
17540
|
-
const
|
|
17659
|
+
const optimistic = isOptimistic(msg);
|
|
17541
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
|
+
}
|
|
17542
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) }),
|
|
17543
17667
|
!sameSenderAsPrev && i > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3" }),
|
|
17544
17668
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
17545
17669
|
"div",
|
|
@@ -17549,17 +17673,7 @@ function MessageList({
|
|
|
17549
17673
|
isCustomer ? "ml-auto" : "mr-auto"
|
|
17550
17674
|
),
|
|
17551
17675
|
children: [
|
|
17552
|
-
!isCustomer && !
|
|
17553
|
-
"button",
|
|
17554
|
-
{
|
|
17555
|
-
type: "button",
|
|
17556
|
-
"aria-label": `React ${emoji}`,
|
|
17557
|
-
onClick: () => void onReact(msg, emoji),
|
|
17558
|
-
className: "flex h-6 w-6 items-center justify-center rounded-full text-[13px] hover:bg-muted",
|
|
17559
|
-
children: emoji
|
|
17560
|
-
},
|
|
17561
|
-
emoji
|
|
17562
|
-
)) }),
|
|
17676
|
+
!isCustomer && !optimistic && /* @__PURE__ */ jsxRuntime.jsx(HoverRail, { message: msg, onReact, onReply }),
|
|
17563
17677
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
17564
17678
|
"div",
|
|
17565
17679
|
{
|
|
@@ -17567,13 +17681,10 @@ function MessageList({
|
|
|
17567
17681
|
"rounded-2xl px-3.5 py-2.5 text-sm leading-relaxed",
|
|
17568
17682
|
isCustomer ? "bg-foreground text-background" : "border border-border bg-background text-foreground",
|
|
17569
17683
|
isCustomer ? sameSenderAsPrev ? "rounded-tr" : "rounded-br" : sameSenderAsPrev ? "rounded-tl" : "rounded-bl",
|
|
17570
|
-
|
|
17684
|
+
optimistic && "opacity-70"
|
|
17571
17685
|
),
|
|
17572
17686
|
children: [
|
|
17573
|
-
!
|
|
17574
|
-
/* @__PURE__ */ jsxRuntime.jsx(SparkleIcon, { className: "h-[11px] w-[11px]" }),
|
|
17575
|
-
"AI"
|
|
17576
|
-
] }),
|
|
17687
|
+
!sameSenderAsPrev && /* @__PURE__ */ jsxRuntime.jsx(SenderChip, { sender: msg.sender_type, businessName }),
|
|
17577
17688
|
msg.reply_to && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17578
17689
|
"div",
|
|
17579
17690
|
{
|
|
@@ -17621,7 +17732,7 @@ function MessageList({
|
|
|
17621
17732
|
isCustomer ? msg.content && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap", children: msg.content }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
17622
17733
|
ChatMarkdown,
|
|
17623
17734
|
{
|
|
17624
|
-
text: msg.content,
|
|
17735
|
+
text: proseOf(msg.content),
|
|
17625
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"
|
|
17626
17737
|
}
|
|
17627
17738
|
)
|
|
@@ -17643,11 +17754,20 @@ function MessageList({
|
|
|
17643
17754
|
] }, msg.id);
|
|
17644
17755
|
}),
|
|
17645
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
|
+
) }),
|
|
17646
17766
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17647
17767
|
"div",
|
|
17648
17768
|
{
|
|
17649
17769
|
ref: (node) => {
|
|
17650
|
-
node
|
|
17770
|
+
if (node && !scrolledUpRef.current) node.scrollIntoView({ behavior: "smooth" });
|
|
17651
17771
|
}
|
|
17652
17772
|
},
|
|
17653
17773
|
`${messages.length}:${isSending}`
|
|
@@ -17656,6 +17776,190 @@ function MessageList({
|
|
|
17656
17776
|
}
|
|
17657
17777
|
);
|
|
17658
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
|
+
}
|
|
17659
17963
|
function ChatInput({
|
|
17660
17964
|
placeholder,
|
|
17661
17965
|
isSending,
|
|
@@ -17678,14 +17982,15 @@ function ChatInput({
|
|
|
17678
17982
|
},
|
|
17679
17983
|
[onAttach]
|
|
17680
17984
|
);
|
|
17681
|
-
const handleSend = React14.useCallback(() => {
|
|
17985
|
+
const handleSend = React14.useCallback(async () => {
|
|
17682
17986
|
const trimmed = value.trim();
|
|
17683
17987
|
if (!trimmed || isSending) return;
|
|
17684
17988
|
setValue("");
|
|
17685
17989
|
if (textareaRef.current) {
|
|
17686
17990
|
textareaRef.current.style.height = "auto";
|
|
17687
17991
|
}
|
|
17688
|
-
onSend(trimmed);
|
|
17992
|
+
const sent = await onSend(trimmed);
|
|
17993
|
+
if (!sent) setValue((current) => current || trimmed);
|
|
17689
17994
|
}, [value, isSending, onSend]);
|
|
17690
17995
|
const handleKeyDown = React14.useCallback(
|
|
17691
17996
|
(e) => {
|
|
@@ -17863,6 +18168,24 @@ function ImageIcon({ className }) {
|
|
|
17863
18168
|
}
|
|
17864
18169
|
);
|
|
17865
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
|
+
}
|
|
17866
18189
|
function SparkleIcon({ className }) {
|
|
17867
18190
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
17868
18191
|
"svg",
|