@cimplify/sdk 0.96.0 → 0.98.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/{chunk-RRBY35TJ.mjs → chunk-4G5UWFC3.mjs} +7 -1
- package/dist/{chunk-HKN3YG76.js → chunk-QPRI2NII.js} +7 -0
- package/dist/index.js +22 -25
- package/dist/index.mjs +2 -8
- package/dist/react.d.mts +382 -261
- package/dist/react.d.ts +382 -261
- package/dist/react.js +1016 -218
- package/dist/react.mjs +947 -168
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/registry/chat-widget.json +1 -1
package/dist/react.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, isMultiDayService, serviceBillingMultiplier, PRODUCT_TYPE, BOUNDARY_KIND, resolveGallery, RENDER_HINT, DEFAULT_CDN_BASE_URL } from './chunk-
|
|
3
|
-
export { getVariantDisplayName } from './chunk-
|
|
4
|
-
import { messagePreview } from './chunk-U4MW6PYU.mjs';
|
|
2
|
+
import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, isMultiDayService, serviceBillingMultiplier, PRODUCT_TYPE, BOUNDARY_KIND, resolveGallery, chatMessageCards, RENDER_HINT, DEFAULT_CDN_BASE_URL } from './chunk-4G5UWFC3.mjs';
|
|
3
|
+
export { getVariantDisplayName } from './chunk-4G5UWFC3.mjs';
|
|
4
|
+
import { splitGenUIContent, messagePreview } from './chunk-U4MW6PYU.mjs';
|
|
5
5
|
export { messagePreview, splitGenUIContent } from './chunk-U4MW6PYU.mjs';
|
|
6
6
|
import { SESSION_CLAIMED, SESSION_CLEARED, createCimplifyClient, MUTATION_ERROR } from './chunk-XMDLF5Y6.mjs';
|
|
7
7
|
import { generateIdempotencyKey, ELEMENT_TYPES, EVENT_TYPES, startSignIn } from './chunk-VUSHG3HA.mjs';
|
|
8
8
|
import { parsePrice, formatMoney, formatPrice, getProductCurrency, getUnitPriceAtQuantity, isOnSale, getDiscountPercentage, getBasePrice, getPriceRange, formatPriceRange } from './chunk-KFLGQGMC.mjs';
|
|
9
9
|
import { isSupportedCurrency, CimplifyError } from './chunk-K5EN2W2Q.mjs';
|
|
10
10
|
import './chunk-3G6RQLXK.mjs';
|
|
11
|
-
import React14, { createContext, lazy, memo, Suspense,
|
|
12
|
-
import { jsx,
|
|
11
|
+
import React14, { createContext, lazy, memo, Suspense, useMemo, useContext, useRef, useCallback, useState, useEffect, useId, useSyncExternalStore } from 'react';
|
|
12
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
13
13
|
import * as ReactDOM from 'react-dom';
|
|
14
14
|
import { clsx } from 'clsx';
|
|
15
15
|
import { twMerge } from 'tailwind-merge';
|
|
@@ -3091,8 +3091,8 @@ function fmtScheduleWindow(start, end) {
|
|
|
3091
3091
|
const startStr = format(s, "EEE, MMM d \xB7 h:mm a");
|
|
3092
3092
|
const e = end ? parseISO(end) : null;
|
|
3093
3093
|
if (e && isValid(e)) {
|
|
3094
|
-
const
|
|
3095
|
-
return `${startStr} \u2013 ${format(e,
|
|
3094
|
+
const sameDay2 = s.toDateString() === e.toDateString();
|
|
3095
|
+
return `${startStr} \u2013 ${format(e, sameDay2 ? "h:mm a" : "EEE, MMM d \xB7 h:mm a")}`;
|
|
3096
3096
|
}
|
|
3097
3097
|
return startStr;
|
|
3098
3098
|
}
|
|
@@ -5883,6 +5883,33 @@ function useBootstrap(options = {}) {
|
|
|
5883
5883
|
const { data, isLoading, error, refetch } = bootstrapQuery(options);
|
|
5884
5884
|
return { bootstrap: data, isLoading, error, refetch };
|
|
5885
5885
|
}
|
|
5886
|
+
|
|
5887
|
+
// src/react/chat-model.ts
|
|
5888
|
+
var OPTIMISTIC_ID_PREFIX = "opt_";
|
|
5889
|
+
function optimisticId(clientId) {
|
|
5890
|
+
return `${OPTIMISTIC_ID_PREFIX}${clientId}`;
|
|
5891
|
+
}
|
|
5892
|
+
function isOptimistic(message) {
|
|
5893
|
+
return message.id.startsWith(OPTIMISTIC_ID_PREFIX);
|
|
5894
|
+
}
|
|
5895
|
+
function partitionMessages(messages) {
|
|
5896
|
+
const bubbles = [];
|
|
5897
|
+
const reactionsByTarget = /* @__PURE__ */ new Map();
|
|
5898
|
+
for (const message of messages) {
|
|
5899
|
+
if (message.content_type === "reaction") {
|
|
5900
|
+
const targetId = message.reply_to?.id;
|
|
5901
|
+
if (!targetId) continue;
|
|
5902
|
+
const list = reactionsByTarget.get(targetId);
|
|
5903
|
+
if (list) list.push(message);
|
|
5904
|
+
else reactionsByTarget.set(targetId, [message]);
|
|
5905
|
+
continue;
|
|
5906
|
+
}
|
|
5907
|
+
bubbles.push(message);
|
|
5908
|
+
}
|
|
5909
|
+
return { bubbles, reactionsByTarget };
|
|
5910
|
+
}
|
|
5911
|
+
|
|
5912
|
+
// src/react/hooks/use-chat.ts
|
|
5886
5913
|
var POLL_INTERVAL_MS = 3e3;
|
|
5887
5914
|
var POLL_IDLE_INTERVAL_MS = 15e3;
|
|
5888
5915
|
var IDLE_TIMEOUT_MS = 6e4;
|
|
@@ -5897,10 +5924,10 @@ function mergeMessages(prev, incoming) {
|
|
|
5897
5924
|
(a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
|
5898
5925
|
);
|
|
5899
5926
|
const confirmed = new Set(
|
|
5900
|
-
merged.filter((m) => !m
|
|
5927
|
+
merged.filter((m) => !isOptimistic(m) && m.client_id).map((m) => m.client_id)
|
|
5901
5928
|
);
|
|
5902
5929
|
return merged.filter(
|
|
5903
|
-
(m) => !(m
|
|
5930
|
+
(m) => !(isOptimistic(m) && m.client_id && confirmed.has(m.client_id))
|
|
5904
5931
|
);
|
|
5905
5932
|
}
|
|
5906
5933
|
function useChat(options = {}) {
|
|
@@ -5963,7 +5990,7 @@ function useChat(options = {}) {
|
|
|
5963
5990
|
const lastConfirmedMessage = useCallback(() => {
|
|
5964
5991
|
const list = messagesRef.current;
|
|
5965
5992
|
for (let i = list.length - 1; i >= 0; i--) {
|
|
5966
|
-
if (!list[i]
|
|
5993
|
+
if (!isOptimistic(list[i])) return list[i];
|
|
5967
5994
|
}
|
|
5968
5995
|
return void 0;
|
|
5969
5996
|
}, []);
|
|
@@ -6011,7 +6038,7 @@ function useChat(options = {}) {
|
|
|
6011
6038
|
const snapshotIds = new Set(snapshot.map((m) => m.id));
|
|
6012
6039
|
const tail = snapshot.length > 0 ? new Date(snapshot[snapshot.length - 1].created_at).getTime() : 0;
|
|
6013
6040
|
const extras = prev.filter(
|
|
6014
|
-
(m) => !snapshotIds.has(m.id) && (m
|
|
6041
|
+
(m) => !snapshotIds.has(m.id) && (isOptimistic(m) || new Date(m.created_at).getTime() > tail)
|
|
6015
6042
|
);
|
|
6016
6043
|
return mergeMessages(snapshot, extras);
|
|
6017
6044
|
});
|
|
@@ -6197,9 +6224,9 @@ function useChat(options = {}) {
|
|
|
6197
6224
|
}
|
|
6198
6225
|
const replyTo = opts?.replyTo;
|
|
6199
6226
|
const clientId = generateIdempotencyKey();
|
|
6200
|
-
const
|
|
6227
|
+
const localId = optimisticId(clientId);
|
|
6201
6228
|
const optimistic = {
|
|
6202
|
-
id:
|
|
6229
|
+
id: localId,
|
|
6203
6230
|
sender_type: "customer",
|
|
6204
6231
|
content: content.trim(),
|
|
6205
6232
|
content_type: attachments.length > 0 ? "image" : "text",
|
|
@@ -6229,14 +6256,14 @@ function useChat(options = {}) {
|
|
|
6229
6256
|
// The websocket may have already streamed the confirmed message
|
|
6230
6257
|
// in while the POST was in flight; drop the optimistic bubble
|
|
6231
6258
|
// instead of inserting a duplicate.
|
|
6232
|
-
prev.some((m) => m.id === confirmed.id) ? prev.filter((m) => m.id !==
|
|
6259
|
+
prev.some((m) => m.id === confirmed.id) ? prev.filter((m) => m.id !== localId) : prev.map((m) => m.id === localId ? confirmed : m)
|
|
6233
6260
|
)
|
|
6234
6261
|
);
|
|
6235
6262
|
if (aiEnabledRef.current && !humanRequestedRef.current) {
|
|
6236
6263
|
startAwaitingReply();
|
|
6237
6264
|
}
|
|
6238
6265
|
} else {
|
|
6239
|
-
setMessages((prev) => prev.filter((m) => m.id !==
|
|
6266
|
+
setMessages((prev) => prev.filter((m) => m.id !== localId));
|
|
6240
6267
|
setError(result.error);
|
|
6241
6268
|
}
|
|
6242
6269
|
setIsSending(false);
|
|
@@ -6255,7 +6282,7 @@ function useChat(options = {}) {
|
|
|
6255
6282
|
);
|
|
6256
6283
|
const react = useCallback(
|
|
6257
6284
|
async (message, emoji) => {
|
|
6258
|
-
if (!client || message
|
|
6285
|
+
if (!client || isOptimistic(message)) return false;
|
|
6259
6286
|
const result = await client.support.reactToMessage(message.id, emoji);
|
|
6260
6287
|
if (result.ok) appendMessages([result.value]);
|
|
6261
6288
|
return result.ok;
|
|
@@ -10429,7 +10456,7 @@ function CartLineItemRow({
|
|
|
10429
10456
|
});
|
|
10430
10457
|
const hasComposite = item.compositeSelections && item.compositeSelections.length > 0;
|
|
10431
10458
|
const hasBundle = item.bundleSelections && item.bundleSelections.length > 0;
|
|
10432
|
-
const
|
|
10459
|
+
const isOptimistic2 = item.isOptimistic === true;
|
|
10433
10460
|
const lineTotal = parsePrice(unitPrice) * item.quantity;
|
|
10434
10461
|
const imageUrl = item.product.image_url;
|
|
10435
10462
|
const variantLabel2 = item.variant ? getVariantDisplayName(item.variant, item.product.name) : null;
|
|
@@ -10437,10 +10464,10 @@ function CartLineItemRow({
|
|
|
10437
10464
|
"div",
|
|
10438
10465
|
{
|
|
10439
10466
|
"data-cimplify-cart-item": true,
|
|
10440
|
-
"data-optimistic":
|
|
10467
|
+
"data-optimistic": isOptimistic2 ? "true" : void 0,
|
|
10441
10468
|
className: cn(
|
|
10442
10469
|
"group relative flex gap-3 py-4 border-b border-border last:border-b-0 transition-opacity",
|
|
10443
|
-
|
|
10470
|
+
isOptimistic2 && "opacity-60",
|
|
10444
10471
|
classNames?.item
|
|
10445
10472
|
),
|
|
10446
10473
|
children: [
|
|
@@ -10607,7 +10634,7 @@ function CartSummary({
|
|
|
10607
10634
|
tax,
|
|
10608
10635
|
total,
|
|
10609
10636
|
isEmpty,
|
|
10610
|
-
isOptimistic,
|
|
10637
|
+
isOptimistic: isOptimistic2,
|
|
10611
10638
|
pendingOpCount,
|
|
10612
10639
|
removeItem,
|
|
10613
10640
|
updateQuantity
|
|
@@ -10633,7 +10660,7 @@ function CartSummary({
|
|
|
10633
10660
|
"div",
|
|
10634
10661
|
{
|
|
10635
10662
|
"data-cimplify-cart-summary": true,
|
|
10636
|
-
"data-optimistic":
|
|
10663
|
+
"data-optimistic": isOptimistic2 ? "true" : void 0,
|
|
10637
10664
|
"data-pending-ops": pendingOpCount > 0 ? pendingOpCount : void 0,
|
|
10638
10665
|
className: cn(className, classNames?.root),
|
|
10639
10666
|
children: isEmpty ? /* @__PURE__ */ jsx("div", { "data-cimplify-cart-empty": true, className: classNames?.empty, children: /* @__PURE__ */ jsx("p", { children: emptyMessage }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -17120,24 +17147,581 @@ var ChatMarkdown = memo(function ChatMarkdown2({
|
|
|
17120
17147
|
return /* @__PURE__ */ jsx(SettledMarkdown, { text, className });
|
|
17121
17148
|
});
|
|
17122
17149
|
|
|
17123
|
-
// src/react/chat-
|
|
17124
|
-
function
|
|
17125
|
-
|
|
17126
|
-
|
|
17127
|
-
|
|
17128
|
-
|
|
17129
|
-
|
|
17130
|
-
|
|
17131
|
-
|
|
17132
|
-
|
|
17133
|
-
|
|
17134
|
-
|
|
17150
|
+
// src/react/chat-kit/motion.ts
|
|
17151
|
+
function prefersReducedMotion() {
|
|
17152
|
+
return typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
17153
|
+
}
|
|
17154
|
+
function scrollBehavior() {
|
|
17155
|
+
return prefersReducedMotion() ? "auto" : "smooth";
|
|
17156
|
+
}
|
|
17157
|
+
function QuoteBlock({
|
|
17158
|
+
quote,
|
|
17159
|
+
inCustomerBubble,
|
|
17160
|
+
businessName,
|
|
17161
|
+
onMissing
|
|
17162
|
+
}) {
|
|
17163
|
+
const quotedIsCustomer = quote.sender_type === "customer";
|
|
17164
|
+
const jumpToOriginal = async () => {
|
|
17165
|
+
let original = document.getElementById(`chat-msg-${quote.id}`);
|
|
17166
|
+
if (!original && onMissing && await onMissing()) {
|
|
17167
|
+
original = document.getElementById(`chat-msg-${quote.id}`);
|
|
17168
|
+
}
|
|
17169
|
+
if (!original) return;
|
|
17170
|
+
original.scrollIntoView({ behavior: scrollBehavior(), block: "center" });
|
|
17171
|
+
original.classList.remove("animate-quote-flash");
|
|
17172
|
+
void original.offsetWidth;
|
|
17173
|
+
original.classList.add("animate-quote-flash");
|
|
17174
|
+
};
|
|
17175
|
+
return /* @__PURE__ */ jsxs(
|
|
17176
|
+
"button",
|
|
17177
|
+
{
|
|
17178
|
+
type: "button",
|
|
17179
|
+
onClick: () => void jumpToOriginal(),
|
|
17180
|
+
className: cn(
|
|
17181
|
+
"flex w-full gap-2 rounded-[10px] px-2.5 py-1.5 text-left",
|
|
17182
|
+
inCustomerBubble ? "bg-primary-foreground/15" : "bg-background/70"
|
|
17183
|
+
),
|
|
17184
|
+
children: [
|
|
17185
|
+
/* @__PURE__ */ jsx(
|
|
17186
|
+
"span",
|
|
17187
|
+
{
|
|
17188
|
+
className: cn(
|
|
17189
|
+
"w-[3px] shrink-0 self-stretch rounded-full",
|
|
17190
|
+
inCustomerBubble ? "bg-primary-foreground/50" : quotedIsCustomer ? "bg-primary" : "bg-muted-foreground/50"
|
|
17191
|
+
)
|
|
17192
|
+
}
|
|
17193
|
+
),
|
|
17194
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col", children: [
|
|
17195
|
+
/* @__PURE__ */ jsx(
|
|
17196
|
+
"span",
|
|
17197
|
+
{
|
|
17198
|
+
className: cn(
|
|
17199
|
+
"text-[11px] leading-4 font-semibold",
|
|
17200
|
+
inCustomerBubble ? "text-primary-foreground" : "text-foreground"
|
|
17201
|
+
),
|
|
17202
|
+
children: quotedIsCustomer ? "You" : businessName
|
|
17203
|
+
}
|
|
17204
|
+
),
|
|
17205
|
+
/* @__PURE__ */ jsx(
|
|
17206
|
+
"span",
|
|
17207
|
+
{
|
|
17208
|
+
className: cn(
|
|
17209
|
+
"truncate text-xs leading-4",
|
|
17210
|
+
inCustomerBubble ? "text-primary-foreground/75" : "text-muted-foreground"
|
|
17211
|
+
),
|
|
17212
|
+
children: quote.preview
|
|
17213
|
+
}
|
|
17214
|
+
)
|
|
17215
|
+
] })
|
|
17216
|
+
]
|
|
17135
17217
|
}
|
|
17136
|
-
|
|
17218
|
+
);
|
|
17219
|
+
}
|
|
17220
|
+
var SWIPE_COMMIT_PX = 48;
|
|
17221
|
+
var SWIPE_ARM_PX = 10;
|
|
17222
|
+
var OVERDRAG_RATE = 0.25;
|
|
17223
|
+
var LONG_PRESS_MS = 450;
|
|
17224
|
+
var RETURN_TRANSITION = "transform 0.25s cubic-bezier(0.2, 0, 0, 1)";
|
|
17225
|
+
function swipeTravel(distance) {
|
|
17226
|
+
if (distance <= 0) return 0;
|
|
17227
|
+
if (distance <= SWIPE_COMMIT_PX) return distance;
|
|
17228
|
+
return SWIPE_COMMIT_PX + (distance - SWIPE_COMMIT_PX) * OVERDRAG_RATE;
|
|
17229
|
+
}
|
|
17230
|
+
function swipeCommitted(distance) {
|
|
17231
|
+
return distance >= SWIPE_COMMIT_PX;
|
|
17232
|
+
}
|
|
17233
|
+
function swipeProgress(distance) {
|
|
17234
|
+
return Math.min(Math.max(distance / SWIPE_COMMIT_PX, 0), 1);
|
|
17235
|
+
}
|
|
17236
|
+
function useSwipeReply({ onReply, onLongPress }) {
|
|
17237
|
+
const rowRef = useRef(null);
|
|
17238
|
+
const glyphRef = useRef(null);
|
|
17239
|
+
const gestureRef = useRef(null);
|
|
17240
|
+
const suppressClickRef = useRef(false);
|
|
17241
|
+
const optionsRef = useRef({ onReply, onLongPress });
|
|
17242
|
+
optionsRef.current = { onReply, onLongPress };
|
|
17243
|
+
const settle = useCallback((glyphVisible) => {
|
|
17244
|
+
const row = rowRef.current;
|
|
17245
|
+
const glyph = glyphRef.current;
|
|
17246
|
+
if (row) {
|
|
17247
|
+
row.style.transition = RETURN_TRANSITION;
|
|
17248
|
+
row.style.transform = "translateX(0px)";
|
|
17249
|
+
}
|
|
17250
|
+
if (glyph && !glyphVisible) {
|
|
17251
|
+
glyph.style.transition = "opacity 0.2s ease-out";
|
|
17252
|
+
glyph.style.opacity = "0";
|
|
17253
|
+
}
|
|
17254
|
+
}, []);
|
|
17255
|
+
const clearGesture = useCallback(() => {
|
|
17256
|
+
const gesture = gestureRef.current;
|
|
17257
|
+
if (gesture?.longPressTimer) clearTimeout(gesture.longPressTimer);
|
|
17258
|
+
gestureRef.current = null;
|
|
17259
|
+
}, []);
|
|
17260
|
+
const onPointerDown = useCallback((event) => {
|
|
17261
|
+
if (!event.isPrimary) return;
|
|
17262
|
+
suppressClickRef.current = false;
|
|
17263
|
+
gestureRef.current = {
|
|
17264
|
+
pointerId: event.pointerId,
|
|
17265
|
+
startX: event.clientX,
|
|
17266
|
+
startY: event.clientY,
|
|
17267
|
+
armed: false,
|
|
17268
|
+
distance: 0,
|
|
17269
|
+
longPressTimer: setTimeout(() => {
|
|
17270
|
+
gestureRef.current = null;
|
|
17271
|
+
suppressClickRef.current = true;
|
|
17272
|
+
optionsRef.current.onLongPress();
|
|
17273
|
+
}, LONG_PRESS_MS)
|
|
17274
|
+
};
|
|
17275
|
+
}, []);
|
|
17276
|
+
const onPointerMove = useCallback((event) => {
|
|
17277
|
+
const gesture = gestureRef.current;
|
|
17278
|
+
if (!gesture || gesture.pointerId !== event.pointerId) return;
|
|
17279
|
+
const leftward = gesture.startX - event.clientX;
|
|
17280
|
+
const vertical = Math.abs(event.clientY - gesture.startY);
|
|
17281
|
+
if (!gesture.armed) {
|
|
17282
|
+
const scrolling = vertical > SWIPE_ARM_PX && vertical >= leftward;
|
|
17283
|
+
const rightward = leftward < -SWIPE_ARM_PX;
|
|
17284
|
+
if (scrolling || rightward) {
|
|
17285
|
+
clearGesture();
|
|
17286
|
+
return;
|
|
17287
|
+
}
|
|
17288
|
+
if (leftward <= SWIPE_ARM_PX || leftward <= vertical) return;
|
|
17289
|
+
if (prefersReducedMotion()) {
|
|
17290
|
+
clearGesture();
|
|
17291
|
+
return;
|
|
17292
|
+
}
|
|
17293
|
+
gesture.armed = true;
|
|
17294
|
+
if (gesture.longPressTimer) clearTimeout(gesture.longPressTimer);
|
|
17295
|
+
gesture.longPressTimer = null;
|
|
17296
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
17297
|
+
if (rowRef.current) rowRef.current.style.transition = "none";
|
|
17298
|
+
if (glyphRef.current) glyphRef.current.style.transition = "none";
|
|
17299
|
+
}
|
|
17300
|
+
gesture.distance = leftward;
|
|
17301
|
+
if (rowRef.current) {
|
|
17302
|
+
rowRef.current.style.transform = `translateX(-${swipeTravel(leftward)}px)`;
|
|
17303
|
+
}
|
|
17304
|
+
if (glyphRef.current) {
|
|
17305
|
+
glyphRef.current.style.opacity = String(swipeProgress(leftward));
|
|
17306
|
+
}
|
|
17307
|
+
}, [clearGesture]);
|
|
17308
|
+
const onPointerUp = useCallback(
|
|
17309
|
+
(event) => {
|
|
17310
|
+
const gesture = gestureRef.current;
|
|
17311
|
+
if (!gesture || gesture.pointerId !== event.pointerId) return;
|
|
17312
|
+
const commit = gesture.armed && swipeCommitted(gesture.distance);
|
|
17313
|
+
const wasArmed = gesture.armed;
|
|
17314
|
+
clearGesture();
|
|
17315
|
+
if (wasArmed) {
|
|
17316
|
+
suppressClickRef.current = true;
|
|
17317
|
+
settle(false);
|
|
17318
|
+
}
|
|
17319
|
+
if (commit) optionsRef.current.onReply();
|
|
17320
|
+
},
|
|
17321
|
+
[clearGesture, settle]
|
|
17322
|
+
);
|
|
17323
|
+
const onPointerCancel = useCallback(
|
|
17324
|
+
(event) => {
|
|
17325
|
+
const gesture = gestureRef.current;
|
|
17326
|
+
if (!gesture || gesture.pointerId !== event.pointerId) return;
|
|
17327
|
+
const wasArmed = gesture.armed;
|
|
17328
|
+
clearGesture();
|
|
17329
|
+
if (wasArmed) settle(false);
|
|
17330
|
+
},
|
|
17331
|
+
[clearGesture, settle]
|
|
17332
|
+
);
|
|
17333
|
+
const onClickCapture = useCallback((event) => {
|
|
17334
|
+
if (!suppressClickRef.current) return;
|
|
17335
|
+
suppressClickRef.current = false;
|
|
17336
|
+
event.preventDefault();
|
|
17337
|
+
event.stopPropagation();
|
|
17338
|
+
}, []);
|
|
17339
|
+
return {
|
|
17340
|
+
rowRef,
|
|
17341
|
+
glyphRef,
|
|
17342
|
+
handlers: { onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onClickCapture }
|
|
17343
|
+
};
|
|
17344
|
+
}
|
|
17345
|
+
var PROSE_CLASS = "[&_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-[15px] [&_h1]:font-bold [&_h2]:text-[15px] [&_h2]:font-bold [&_h3]:text-sm [&_h3]:font-semibold [&_hr]:my-2 [&_hr]:border-border [&_table]:my-1 [&_table]:w-full [&_th]:pb-1 [&_th]:text-left [&_th]:text-[11px] [&_th]:font-semibold [&_th]:uppercase [&_th]:text-muted-foreground [&_td]:border-b [&_td]:border-border/50 [&_td]:py-1 [&_td:last-child]:text-right [&_td:last-child]:font-semibold [&_td:last-child]:tabular-nums [&_tr:last-child_td]:border-b-0 [&_a]:underline [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:text-[13px]";
|
|
17346
|
+
function bubbleShape(isCustomer, group) {
|
|
17347
|
+
if (!hasTail(group)) return "rounded-[18px]";
|
|
17348
|
+
return isCustomer ? "rounded-[18px] rounded-br-none" : "rounded-[18px] rounded-bl-none";
|
|
17349
|
+
}
|
|
17350
|
+
function hasTail(group) {
|
|
17351
|
+
return group === "single" || group === "last";
|
|
17352
|
+
}
|
|
17353
|
+
var TAIL_SWEEP = "M21 0 L21 5 C21 15 25 16 25 16 C20.93 16.43 16.84 14.94 13.96 11.96 C9 16 5 16 0 16";
|
|
17354
|
+
var TAIL_FILL = `${TAIL_SWEEP} Z`;
|
|
17355
|
+
var TAIL_NOTCH = "M25 16 C20.93 16.43 16.84 14.94 13.96 11.96 C9 16 5 16 0 16 Z";
|
|
17356
|
+
function BubbleTail({ isCustomer }) {
|
|
17357
|
+
return /* @__PURE__ */ jsxs(
|
|
17358
|
+
"svg",
|
|
17359
|
+
{
|
|
17360
|
+
width: "25",
|
|
17361
|
+
height: "16",
|
|
17362
|
+
viewBox: "0 0 25 16",
|
|
17363
|
+
"aria-hidden": "true",
|
|
17364
|
+
className: cn(
|
|
17365
|
+
"absolute bottom-0",
|
|
17366
|
+
isCustomer ? "-right-[4px] fill-primary" : "-left-[4px] -scale-x-100 fill-background"
|
|
17367
|
+
),
|
|
17368
|
+
children: [
|
|
17369
|
+
/* @__PURE__ */ jsx("path", { d: TAIL_FILL }),
|
|
17370
|
+
/* @__PURE__ */ jsx("path", { d: TAIL_NOTCH, style: { fill: "var(--color-chat-canvas, var(--color-muted, #f3f5f3))" } }),
|
|
17371
|
+
!isCustomer && /* @__PURE__ */ jsx("path", { d: TAIL_SWEEP, fill: "none", strokeWidth: "1", className: "stroke-foreground/[0.07]" })
|
|
17372
|
+
]
|
|
17373
|
+
}
|
|
17374
|
+
);
|
|
17375
|
+
}
|
|
17376
|
+
function clockLabel(at) {
|
|
17377
|
+
const date = new Date(at);
|
|
17378
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
17379
|
+
const hour = date.getHours() % 12 || 12;
|
|
17380
|
+
return `${hour}:${String(date.getMinutes()).padStart(2, "0")}`;
|
|
17381
|
+
}
|
|
17382
|
+
function DoubleTick() {
|
|
17383
|
+
return /* @__PURE__ */ jsxs("svg", { width: "15", height: "9", viewBox: "0 0 15 9", fill: "none", "aria-hidden": "true", children: [
|
|
17384
|
+
/* @__PURE__ */ jsx("path", { d: "M1 4.5L4 7.5L9.5 1.5", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
17385
|
+
/* @__PURE__ */ jsx("path", { d: "M6.5 4.5L9.5 7.5L14 2.5", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
17386
|
+
] });
|
|
17387
|
+
}
|
|
17388
|
+
function SendingClock() {
|
|
17389
|
+
return /* @__PURE__ */ jsxs("svg", { width: "11", height: "11", viewBox: "0 0 11 11", fill: "none", "aria-hidden": "true", children: [
|
|
17390
|
+
/* @__PURE__ */ jsx("circle", { cx: "5.5", cy: "5.5", r: "4.5", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
17391
|
+
/* @__PURE__ */ jsx("path", { d: "M5.5 3v2.7l1.8 1", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
|
|
17392
|
+
] });
|
|
17393
|
+
}
|
|
17394
|
+
function PaperclipIcon() {
|
|
17395
|
+
return /* @__PURE__ */ jsx(
|
|
17396
|
+
"svg",
|
|
17397
|
+
{
|
|
17398
|
+
width: "14",
|
|
17399
|
+
height: "14",
|
|
17400
|
+
viewBox: "0 0 24 24",
|
|
17401
|
+
fill: "none",
|
|
17402
|
+
stroke: "currentColor",
|
|
17403
|
+
strokeWidth: "2",
|
|
17404
|
+
strokeLinecap: "round",
|
|
17405
|
+
strokeLinejoin: "round",
|
|
17406
|
+
"aria-hidden": "true",
|
|
17407
|
+
children: /* @__PURE__ */ jsx("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" })
|
|
17408
|
+
}
|
|
17409
|
+
);
|
|
17410
|
+
}
|
|
17411
|
+
function ReplyGlyph() {
|
|
17412
|
+
return /* @__PURE__ */ jsxs(
|
|
17413
|
+
"svg",
|
|
17414
|
+
{
|
|
17415
|
+
width: "18",
|
|
17416
|
+
height: "18",
|
|
17417
|
+
viewBox: "0 0 24 24",
|
|
17418
|
+
fill: "none",
|
|
17419
|
+
stroke: "currentColor",
|
|
17420
|
+
strokeWidth: "2.2",
|
|
17421
|
+
strokeLinecap: "round",
|
|
17422
|
+
strokeLinejoin: "round",
|
|
17423
|
+
"aria-hidden": "true",
|
|
17424
|
+
children: [
|
|
17425
|
+
/* @__PURE__ */ jsx("polyline", { points: "9 17 4 12 9 7" }),
|
|
17426
|
+
/* @__PURE__ */ jsx("path", { d: "M20 18v-2a4 4 0 0 0-4-4H4" })
|
|
17427
|
+
]
|
|
17428
|
+
}
|
|
17429
|
+
);
|
|
17430
|
+
}
|
|
17431
|
+
var DELIVERY_LABELS = {
|
|
17432
|
+
sending: "Sending",
|
|
17433
|
+
delivered: "Delivered",
|
|
17434
|
+
seen: "Seen"
|
|
17435
|
+
};
|
|
17436
|
+
function BubbleMeta({
|
|
17437
|
+
at,
|
|
17438
|
+
isCustomer,
|
|
17439
|
+
delivery
|
|
17440
|
+
}) {
|
|
17441
|
+
return /* @__PURE__ */ jsxs(
|
|
17442
|
+
"span",
|
|
17443
|
+
{
|
|
17444
|
+
className: cn(
|
|
17445
|
+
"ml-auto flex shrink-0 items-center gap-[3px] self-end pb-px whitespace-nowrap",
|
|
17446
|
+
isCustomer ? "text-primary-foreground/70" : "text-muted-foreground"
|
|
17447
|
+
),
|
|
17448
|
+
children: [
|
|
17449
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10.5px] leading-[13px] font-medium", children: clockLabel(at) }),
|
|
17450
|
+
isCustomer && delivery && /* @__PURE__ */ jsx(
|
|
17451
|
+
"span",
|
|
17452
|
+
{
|
|
17453
|
+
role: "img",
|
|
17454
|
+
"aria-label": DELIVERY_LABELS[delivery],
|
|
17455
|
+
className: cn(
|
|
17456
|
+
"flex items-center text-primary-foreground",
|
|
17457
|
+
delivery !== "seen" && "opacity-55"
|
|
17458
|
+
),
|
|
17459
|
+
children: delivery === "sending" ? /* @__PURE__ */ jsx(SendingClock, {}) : /* @__PURE__ */ jsx(DoubleTick, {})
|
|
17460
|
+
}
|
|
17461
|
+
)
|
|
17462
|
+
]
|
|
17463
|
+
}
|
|
17464
|
+
);
|
|
17465
|
+
}
|
|
17466
|
+
function reactionPills(reactions) {
|
|
17467
|
+
const counts = /* @__PURE__ */ new Map();
|
|
17468
|
+
for (const reaction of reactions) {
|
|
17469
|
+
const emoji = reaction.content.trim();
|
|
17470
|
+
if (emoji) counts.set(emoji, (counts.get(emoji) ?? 0) + 1);
|
|
17137
17471
|
}
|
|
17138
|
-
return {
|
|
17472
|
+
return [...counts.entries()].map(([emoji, count]) => ({ emoji, count }));
|
|
17473
|
+
}
|
|
17474
|
+
var MessageBubble = memo(function MessageBubble2({
|
|
17475
|
+
message,
|
|
17476
|
+
reactions,
|
|
17477
|
+
businessName,
|
|
17478
|
+
group = "single",
|
|
17479
|
+
delivery,
|
|
17480
|
+
onReply,
|
|
17481
|
+
onLongPress,
|
|
17482
|
+
avatar,
|
|
17483
|
+
overlay,
|
|
17484
|
+
renderCards,
|
|
17485
|
+
renderProgram,
|
|
17486
|
+
onQuoteMiss
|
|
17487
|
+
}) {
|
|
17488
|
+
const showAvatar = group === "single" || group === "last";
|
|
17489
|
+
const isCustomer = message.sender_type === "customer";
|
|
17490
|
+
const isBusiness = !isCustomer;
|
|
17491
|
+
const segments = useMemo(
|
|
17492
|
+
() => isCustomer ? [{ kind: "prose", text: message.content }] : splitGenUIContent(message.content),
|
|
17493
|
+
[isCustomer, message.content]
|
|
17494
|
+
);
|
|
17495
|
+
const drawable = useMemo(
|
|
17496
|
+
() => segments.filter(
|
|
17497
|
+
(segment) => segment.kind === "prose" && segment.text.trim() !== "" || segment.kind === "program" && renderProgram != null
|
|
17498
|
+
),
|
|
17499
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
17500
|
+
[segments, renderProgram != null]
|
|
17501
|
+
);
|
|
17502
|
+
const swipe = useSwipeReply({
|
|
17503
|
+
onReply: () => onReply(message),
|
|
17504
|
+
onLongPress: () => onLongPress?.(message)
|
|
17505
|
+
});
|
|
17506
|
+
const quote = message.reply_to ?? null;
|
|
17507
|
+
const pills = reactions ? reactionPills(reactions) : [];
|
|
17508
|
+
const cards = renderCards?.(message);
|
|
17509
|
+
const proseSegments = drawable.filter((segment) => segment.kind === "prose");
|
|
17510
|
+
const lastProseIndex = drawable.lastIndexOf(proseSegments[proseSegments.length - 1]);
|
|
17511
|
+
return /* @__PURE__ */ jsxs("div", { id: `chat-msg-${message.id}`, className: "relative rounded-xl", children: [
|
|
17512
|
+
/* @__PURE__ */ jsx(
|
|
17513
|
+
"span",
|
|
17514
|
+
{
|
|
17515
|
+
ref: swipe.glyphRef,
|
|
17516
|
+
"aria-hidden": "true",
|
|
17517
|
+
className: "absolute top-1/2 right-1 -translate-y-1/2 text-muted-foreground opacity-0",
|
|
17518
|
+
children: /* @__PURE__ */ jsx(ReplyGlyph, {})
|
|
17519
|
+
}
|
|
17520
|
+
),
|
|
17521
|
+
overlay,
|
|
17522
|
+
/* @__PURE__ */ jsxs(
|
|
17523
|
+
"div",
|
|
17524
|
+
{
|
|
17525
|
+
ref: swipe.rowRef,
|
|
17526
|
+
...swipe.handlers,
|
|
17527
|
+
onContextMenu: onLongPress ? (event) => {
|
|
17528
|
+
event.preventDefault();
|
|
17529
|
+
onLongPress(message);
|
|
17530
|
+
} : void 0,
|
|
17531
|
+
className: cn(
|
|
17532
|
+
"flex touch-pan-y flex-col gap-[3px] select-none [-webkit-touch-callout:none]",
|
|
17533
|
+
isCustomer ? "items-end" : "items-start"
|
|
17534
|
+
),
|
|
17535
|
+
children: [
|
|
17536
|
+
quote && drawable[0]?.kind !== "prose" && /* @__PURE__ */ jsx("div", { className: "max-w-[280px] rounded-[14px] border border-foreground/[0.07] bg-background p-1.5 shadow-[0_1px_1px_rgba(15,23,42,0.03)]", children: /* @__PURE__ */ jsx(QuoteBlock, { quote, inCustomerBubble: false, businessName, onMissing: onQuoteMiss }) }),
|
|
17537
|
+
message.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex max-w-[280px] flex-wrap gap-1.5", children: message.attachments.map((attachment, index) => {
|
|
17538
|
+
const url = safeAttachmentUrl(attachment.url);
|
|
17539
|
+
if (!url) return null;
|
|
17540
|
+
return isImageAttachment(attachment) ? /* @__PURE__ */ jsx("a", { href: url, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsx(
|
|
17541
|
+
"img",
|
|
17542
|
+
{
|
|
17543
|
+
src: url,
|
|
17544
|
+
alt: attachment.filename ?? "Photo",
|
|
17545
|
+
loading: "lazy",
|
|
17546
|
+
className: "max-h-[240px] max-w-[240px] rounded-[16px] object-cover"
|
|
17547
|
+
}
|
|
17548
|
+
) }, index) : /* @__PURE__ */ jsxs(
|
|
17549
|
+
"a",
|
|
17550
|
+
{
|
|
17551
|
+
href: url,
|
|
17552
|
+
target: "_blank",
|
|
17553
|
+
rel: "noreferrer",
|
|
17554
|
+
className: "flex items-center gap-2 rounded-[14px] border border-foreground/[0.07] bg-background px-3 py-2 text-[13px] font-medium text-foreground shadow-[0_1px_1px_rgba(15,23,42,0.03)]",
|
|
17555
|
+
children: [
|
|
17556
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground", children: /* @__PURE__ */ jsx(PaperclipIcon, {}) }),
|
|
17557
|
+
/* @__PURE__ */ jsx("span", { className: "max-w-[200px] truncate", children: attachment.filename ?? "Attachment" })
|
|
17558
|
+
]
|
|
17559
|
+
},
|
|
17560
|
+
index
|
|
17561
|
+
);
|
|
17562
|
+
}) }),
|
|
17563
|
+
drawable.map(
|
|
17564
|
+
(segment, index) => segment.kind === "program" ? /* @__PURE__ */ jsx(React14.Fragment, { children: renderProgram?.(segment.source) }, index) : /* @__PURE__ */ jsxs("div", { className: "flex max-w-full items-end gap-1.5", children: [
|
|
17565
|
+
isBusiness && avatar && /* @__PURE__ */ jsx(
|
|
17566
|
+
"span",
|
|
17567
|
+
{
|
|
17568
|
+
"aria-label": businessName,
|
|
17569
|
+
className: cn(
|
|
17570
|
+
"shrink-0",
|
|
17571
|
+
// One avatar per run, anchored to its last bubble.
|
|
17572
|
+
showAvatar && index === lastProseIndex ? "visible" : "invisible"
|
|
17573
|
+
),
|
|
17574
|
+
children: avatar
|
|
17575
|
+
}
|
|
17576
|
+
),
|
|
17577
|
+
/* @__PURE__ */ jsxs(
|
|
17578
|
+
"div",
|
|
17579
|
+
{
|
|
17580
|
+
className: cn(
|
|
17581
|
+
"relative flex max-w-[280px] min-w-0 flex-col gap-1.5 py-2 pr-[11px] pl-3.5 text-[15px] leading-[21px] font-[450] break-words",
|
|
17582
|
+
bubbleShape(isCustomer, index === lastProseIndex ? group : "middle"),
|
|
17583
|
+
isCustomer ? "bg-primary text-primary-foreground" : "border border-foreground/[0.07] bg-background text-foreground shadow-[0_1px_1px_rgba(15,23,42,0.03)]"
|
|
17584
|
+
),
|
|
17585
|
+
children: [
|
|
17586
|
+
index === 0 && quote && /* @__PURE__ */ jsx(QuoteBlock, { quote, inCustomerBubble: isCustomer, businessName, onMissing: onQuoteMiss }),
|
|
17587
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-end gap-x-2 gap-y-0.5", children: [
|
|
17588
|
+
isCustomer ? /* @__PURE__ */ jsx("span", { className: "whitespace-pre-wrap", children: segment.text }) : /* @__PURE__ */ jsx(ChatMarkdown, { text: segment.text, className: cn("min-w-0", PROSE_CLASS) }),
|
|
17589
|
+
index === lastProseIndex && /* @__PURE__ */ jsx(BubbleMeta, { at: message.created_at, isCustomer, delivery })
|
|
17590
|
+
] }),
|
|
17591
|
+
index === lastProseIndex && hasTail(group) && /* @__PURE__ */ jsx(BubbleTail, { isCustomer })
|
|
17592
|
+
]
|
|
17593
|
+
}
|
|
17594
|
+
)
|
|
17595
|
+
] }, index)
|
|
17596
|
+
),
|
|
17597
|
+
cards,
|
|
17598
|
+
pills.length > 0 && /* @__PURE__ */ jsx(
|
|
17599
|
+
"div",
|
|
17600
|
+
{
|
|
17601
|
+
className: cn(
|
|
17602
|
+
"z-[1] -mt-3 flex gap-1",
|
|
17603
|
+
// Overlap the bubble corner by ~9px, tucked inside the tail side.
|
|
17604
|
+
isCustomer ? "mr-2.5" : "ml-10"
|
|
17605
|
+
),
|
|
17606
|
+
children: pills.map((pill) => /* @__PURE__ */ jsxs(
|
|
17607
|
+
"span",
|
|
17608
|
+
{
|
|
17609
|
+
className: "flex items-center gap-[3px] rounded-full border border-foreground/[0.08] bg-background px-[7px] py-[2px] shadow-[0_1px_3px_rgba(15,23,42,0.08)]",
|
|
17610
|
+
children: [
|
|
17611
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs leading-4", children: pill.emoji }),
|
|
17612
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] leading-[13px] font-semibold text-muted-foreground", children: pill.count })
|
|
17613
|
+
]
|
|
17614
|
+
},
|
|
17615
|
+
pill.emoji
|
|
17616
|
+
))
|
|
17617
|
+
}
|
|
17618
|
+
)
|
|
17619
|
+
]
|
|
17620
|
+
}
|
|
17621
|
+
)
|
|
17622
|
+
] });
|
|
17623
|
+
});
|
|
17624
|
+
function Seam({
|
|
17625
|
+
label,
|
|
17626
|
+
variant = "line"
|
|
17627
|
+
}) {
|
|
17628
|
+
if (variant === "chip") {
|
|
17629
|
+
return /* @__PURE__ */ jsx("div", { className: "flex justify-center py-1.5", children: /* @__PURE__ */ jsx("span", { className: "rounded-full border border-foreground/[0.06] bg-background px-3 py-1 text-[11px] leading-[14px] font-semibold tracking-[0.04em] text-muted-foreground uppercase shadow-[0_1px_2px_rgba(15,23,42,0.04)]", children: label }) });
|
|
17630
|
+
}
|
|
17631
|
+
const unread = variant === "unread";
|
|
17632
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 py-1.5", children: [
|
|
17633
|
+
/* @__PURE__ */ jsx(
|
|
17634
|
+
"span",
|
|
17635
|
+
{
|
|
17636
|
+
className: cn("h-px flex-1", unread ? "bg-primary/35" : "bg-foreground/[0.08]"),
|
|
17637
|
+
"aria-hidden": "true"
|
|
17638
|
+
}
|
|
17639
|
+
),
|
|
17640
|
+
/* @__PURE__ */ jsx(
|
|
17641
|
+
"span",
|
|
17642
|
+
{
|
|
17643
|
+
className: cn(
|
|
17644
|
+
"whitespace-nowrap",
|
|
17645
|
+
unread ? "text-[11px] leading-[14px] font-semibold tracking-[0.04em] text-primary uppercase" : "text-[11.5px] leading-[15px] font-medium text-muted-foreground"
|
|
17646
|
+
),
|
|
17647
|
+
children: label
|
|
17648
|
+
}
|
|
17649
|
+
),
|
|
17650
|
+
/* @__PURE__ */ jsx(
|
|
17651
|
+
"span",
|
|
17652
|
+
{
|
|
17653
|
+
className: cn("h-px flex-1", unread ? "bg-primary/35" : "bg-foreground/[0.08]"),
|
|
17654
|
+
"aria-hidden": "true"
|
|
17655
|
+
}
|
|
17656
|
+
)
|
|
17657
|
+
] });
|
|
17658
|
+
}
|
|
17659
|
+
|
|
17660
|
+
// src/react/chat-kit/timeline.ts
|
|
17661
|
+
function sameDay(a, b) {
|
|
17662
|
+
return new Date(a).toDateString() === new Date(b).toDateString();
|
|
17663
|
+
}
|
|
17664
|
+
function dayLabel(at) {
|
|
17665
|
+
const date = new Date(at);
|
|
17666
|
+
const today = /* @__PURE__ */ new Date();
|
|
17667
|
+
if (date.toDateString() === today.toDateString()) return "Today";
|
|
17668
|
+
const yesterday = new Date(today);
|
|
17669
|
+
yesterday.setDate(today.getDate() - 1);
|
|
17670
|
+
if (date.toDateString() === yesterday.toDateString()) return "Yesterday";
|
|
17671
|
+
return date.toLocaleDateString([], { weekday: "short", day: "numeric", month: "short" });
|
|
17672
|
+
}
|
|
17673
|
+
function proseOf(content) {
|
|
17674
|
+
return splitGenUIContent(content).filter((segment) => segment.kind === "prose").map((segment) => segment.text).join("\n\n").trim();
|
|
17675
|
+
}
|
|
17676
|
+
function hasProgram(content) {
|
|
17677
|
+
return splitGenUIContent(content).some((segment) => segment.kind === "program");
|
|
17678
|
+
}
|
|
17679
|
+
function chatTimeline(messages, options = {}) {
|
|
17680
|
+
const { readAt = null, rendersPrograms = false } = options;
|
|
17681
|
+
const partitioned = partitionMessages(messages);
|
|
17682
|
+
const bubbles = partitioned.bubbles.filter(
|
|
17683
|
+
(message) => message.sender_type === "system" || message.attachments.length > 0 || chatMessageCards(message).length > 0 || rendersPrograms && hasProgram(message.content) || (message.sender_type === "customer" ? message.content.trim() !== "" : proseOf(message.content) !== "")
|
|
17684
|
+
);
|
|
17685
|
+
let firstUnreadId;
|
|
17686
|
+
let unreadOnOpen = 0;
|
|
17687
|
+
if (readAt) {
|
|
17688
|
+
const readAtMs = new Date(readAt).getTime();
|
|
17689
|
+
for (const message of bubbles) {
|
|
17690
|
+
if (message.sender_type === "customer" || new Date(message.created_at).getTime() <= readAtMs)
|
|
17691
|
+
continue;
|
|
17692
|
+
firstUnreadId ?? (firstUnreadId = message.id);
|
|
17693
|
+
unreadOnOpen += 1;
|
|
17694
|
+
}
|
|
17695
|
+
}
|
|
17696
|
+
return {
|
|
17697
|
+
bubbles,
|
|
17698
|
+
reactionsByTarget: partitioned.reactionsByTarget,
|
|
17699
|
+
firstUnreadId,
|
|
17700
|
+
unreadOnOpen,
|
|
17701
|
+
firstAgentMessageId: bubbles.find((m) => m.sender_type === "agent")?.id
|
|
17702
|
+
};
|
|
17703
|
+
}
|
|
17704
|
+
function groupPosition(timeline, index) {
|
|
17705
|
+
const { bubbles, firstAgentMessageId, firstUnreadId } = timeline;
|
|
17706
|
+
const message = bubbles[index];
|
|
17707
|
+
const previous = bubbles[index - 1];
|
|
17708
|
+
const next = bubbles[index + 1];
|
|
17709
|
+
const startsRun = !previous || !sameDay(previous.created_at, message.created_at) || previous.sender_type !== message.sender_type || message.id === firstAgentMessageId || message.id === firstUnreadId || previous.sender_type === "system";
|
|
17710
|
+
const endsRun = !next || next.sender_type !== message.sender_type || next.sender_type === "system" || !sameDay(message.created_at, next.created_at);
|
|
17711
|
+
if (startsRun && endsRun) return "single";
|
|
17712
|
+
if (startsRun) return "first";
|
|
17713
|
+
if (endsRun) return "last";
|
|
17714
|
+
return "middle";
|
|
17715
|
+
}
|
|
17716
|
+
function deliveryState(message, timeline, seenAt, isOptimistic2) {
|
|
17717
|
+
if (message.sender_type !== "customer") return void 0;
|
|
17718
|
+
if (isOptimistic2(message)) return "sending";
|
|
17719
|
+
const seenCutoff = timeline.firstAgentMessageId ? seenAt : null;
|
|
17720
|
+
return seenCutoff && message.created_at <= seenCutoff ? "seen" : "delivered";
|
|
17139
17721
|
}
|
|
17140
17722
|
var MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;
|
|
17723
|
+
var KEEP_DISMISSED_KEY = "cimplify_keep_chat_dismissed";
|
|
17724
|
+
var AT_BOTTOM_SLACK_PX = 48;
|
|
17141
17725
|
function ChatWidget({
|
|
17142
17726
|
client: clientProp,
|
|
17143
17727
|
businessName = "Support",
|
|
@@ -17156,6 +17740,7 @@ function ChatWidget({
|
|
|
17156
17740
|
const [open, setOpen] = useState(defaultOpen);
|
|
17157
17741
|
const [readAt, setReadAt] = useState(null);
|
|
17158
17742
|
const [attachError, setAttachError] = useState(null);
|
|
17743
|
+
const [replyTo, setReplyTo] = useState(null);
|
|
17159
17744
|
const {
|
|
17160
17745
|
messages,
|
|
17161
17746
|
isLoading,
|
|
@@ -17164,11 +17749,15 @@ function ChatWidget({
|
|
|
17164
17749
|
seenAt,
|
|
17165
17750
|
error,
|
|
17166
17751
|
isActive,
|
|
17752
|
+
humanRequested,
|
|
17167
17753
|
claimedAs,
|
|
17168
17754
|
signOut,
|
|
17169
17755
|
send,
|
|
17170
17756
|
react,
|
|
17757
|
+
requestHuman,
|
|
17171
17758
|
startConversation,
|
|
17759
|
+
loadOlder,
|
|
17760
|
+
mayHaveOlder,
|
|
17172
17761
|
openIfExisting
|
|
17173
17762
|
} = useChat({ client, pollInterval });
|
|
17174
17763
|
const showTyping = isSending || awaitingReply;
|
|
@@ -17203,9 +17792,11 @@ function ChatWidget({
|
|
|
17203
17792
|
const handleSend = useCallback(
|
|
17204
17793
|
(content) => {
|
|
17205
17794
|
setAttachError(null);
|
|
17206
|
-
|
|
17795
|
+
const target = replyTo;
|
|
17796
|
+
setReplyTo(null);
|
|
17797
|
+
return send(content, target ? { replyTo: target } : void 0);
|
|
17207
17798
|
},
|
|
17208
|
-
[send]
|
|
17799
|
+
[send, replyTo]
|
|
17209
17800
|
);
|
|
17210
17801
|
const unread = open ? 0 : messages.filter(
|
|
17211
17802
|
(m) => m.sender_type !== "customer" && (!readAt || m.created_at > readAt)
|
|
@@ -17244,6 +17835,7 @@ function ChatWidget({
|
|
|
17244
17835
|
open && /* @__PURE__ */ jsx(
|
|
17245
17836
|
Panel,
|
|
17246
17837
|
{
|
|
17838
|
+
client,
|
|
17247
17839
|
businessName,
|
|
17248
17840
|
greeting,
|
|
17249
17841
|
subtitle,
|
|
@@ -17254,14 +17846,22 @@ function ChatWidget({
|
|
|
17254
17846
|
isSending,
|
|
17255
17847
|
isTyping: showTyping,
|
|
17256
17848
|
seenAt,
|
|
17849
|
+
readAt,
|
|
17257
17850
|
error: error ?? attachError,
|
|
17258
17851
|
isActive,
|
|
17259
17852
|
isLeft,
|
|
17853
|
+
humanRequested,
|
|
17854
|
+
mayHaveOlder,
|
|
17855
|
+
replyTo,
|
|
17260
17856
|
claimedAs,
|
|
17261
17857
|
onSignOut: () => void signOut(),
|
|
17262
17858
|
onClose: handleClose,
|
|
17263
17859
|
onSend: handleSend,
|
|
17264
17860
|
onReact: react,
|
|
17861
|
+
onReply: setReplyTo,
|
|
17862
|
+
onCancelReply: () => setReplyTo(null),
|
|
17863
|
+
onRequestHuman: () => void requestHuman(),
|
|
17864
|
+
onLoadOlder: () => void loadOlder(),
|
|
17265
17865
|
onAttach: attachPhoto,
|
|
17266
17866
|
onStartConversation: startConversation,
|
|
17267
17867
|
classNames
|
|
@@ -17296,7 +17896,7 @@ function Bubble({
|
|
|
17296
17896
|
{
|
|
17297
17897
|
className: cn(
|
|
17298
17898
|
"absolute -right-0.5 -top-0.5 flex h-5 w-5 items-center justify-center",
|
|
17299
|
-
"rounded-full bg-
|
|
17899
|
+
"rounded-full bg-destructive text-[10px] font-bold text-white",
|
|
17300
17900
|
"ring-2 ring-background"
|
|
17301
17901
|
),
|
|
17302
17902
|
children: unread > 9 ? "9+" : unread
|
|
@@ -17307,6 +17907,7 @@ function Bubble({
|
|
|
17307
17907
|
);
|
|
17308
17908
|
}
|
|
17309
17909
|
function Panel({
|
|
17910
|
+
client,
|
|
17310
17911
|
businessName,
|
|
17311
17912
|
greeting,
|
|
17312
17913
|
subtitle,
|
|
@@ -17317,14 +17918,22 @@ function Panel({
|
|
|
17317
17918
|
isSending,
|
|
17318
17919
|
isTyping,
|
|
17319
17920
|
seenAt,
|
|
17921
|
+
readAt,
|
|
17320
17922
|
error,
|
|
17321
17923
|
isActive,
|
|
17322
17924
|
isLeft,
|
|
17925
|
+
humanRequested,
|
|
17926
|
+
mayHaveOlder,
|
|
17927
|
+
replyTo,
|
|
17323
17928
|
claimedAs,
|
|
17324
17929
|
onSignOut,
|
|
17325
17930
|
onClose,
|
|
17326
17931
|
onSend,
|
|
17327
17932
|
onReact,
|
|
17933
|
+
onReply,
|
|
17934
|
+
onCancelReply,
|
|
17935
|
+
onRequestHuman,
|
|
17936
|
+
onLoadOlder,
|
|
17328
17937
|
onAttach,
|
|
17329
17938
|
onStartConversation,
|
|
17330
17939
|
classNames
|
|
@@ -17364,20 +17973,57 @@ function Panel({
|
|
|
17364
17973
|
MessageList,
|
|
17365
17974
|
{
|
|
17366
17975
|
messages,
|
|
17976
|
+
businessName,
|
|
17367
17977
|
isLoading,
|
|
17368
17978
|
isSending: isTyping,
|
|
17369
17979
|
seenAt,
|
|
17980
|
+
readAt,
|
|
17981
|
+
humanRequested,
|
|
17982
|
+
mayHaveOlder,
|
|
17370
17983
|
onReact,
|
|
17984
|
+
onReply,
|
|
17985
|
+
onRequestHuman,
|
|
17986
|
+
onLoadOlder,
|
|
17371
17987
|
className: classNames?.messages
|
|
17372
17988
|
}
|
|
17373
17989
|
),
|
|
17374
|
-
|
|
17990
|
+
isActive && !claimedAs && client && /* @__PURE__ */ jsx(KeepConversation, { client }),
|
|
17991
|
+
humanRequested && isActive && /* @__PURE__ */ 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: [
|
|
17992
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 flex-shrink-0 animate-pulse rounded-full bg-amber-500" }),
|
|
17993
|
+
"Waiting for ",
|
|
17994
|
+
businessName,
|
|
17995
|
+
" \u2014 usually replies in minutes"
|
|
17996
|
+
] }),
|
|
17997
|
+
error != null && /* @__PURE__ */ 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." }),
|
|
17998
|
+
replyTo && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 border-t border-border bg-muted/50 px-4 py-2", children: [
|
|
17999
|
+
/* @__PURE__ */ jsx("span", { className: "w-[3px] shrink-0 self-stretch rounded-full bg-foreground/40" }),
|
|
18000
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
18001
|
+
/* @__PURE__ */ jsxs("div", { className: "text-[11px] font-semibold text-foreground", children: [
|
|
18002
|
+
"Replying to ",
|
|
18003
|
+
replyTo.sender_type === "customer" ? "yourself" : businessName
|
|
18004
|
+
] }),
|
|
18005
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-[11px] text-muted-foreground", children: proseOf(replyTo.content) || "Photo" })
|
|
18006
|
+
] }),
|
|
18007
|
+
/* @__PURE__ */ jsx(
|
|
18008
|
+
"button",
|
|
18009
|
+
{
|
|
18010
|
+
type: "button",
|
|
18011
|
+
"aria-label": "Cancel reply",
|
|
18012
|
+
onClick: onCancelReply,
|
|
18013
|
+
className: "flex-shrink-0 p-0.5 text-muted-foreground hover:text-foreground",
|
|
18014
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { className: "h-3.5 w-3.5" })
|
|
18015
|
+
}
|
|
18016
|
+
)
|
|
18017
|
+
] }),
|
|
17375
18018
|
/* @__PURE__ */ jsx(
|
|
17376
18019
|
ChatInput,
|
|
17377
18020
|
{
|
|
17378
18021
|
placeholder,
|
|
17379
18022
|
isSending,
|
|
17380
|
-
onSend: isActive ? onSend :
|
|
18023
|
+
onSend: isActive ? onSend : async (text) => {
|
|
18024
|
+
await onStartConversation(text);
|
|
18025
|
+
return true;
|
|
18026
|
+
},
|
|
17381
18027
|
onAttach,
|
|
17382
18028
|
className: classNames?.input
|
|
17383
18029
|
}
|
|
@@ -17406,10 +18052,10 @@ function Header({
|
|
|
17406
18052
|
className
|
|
17407
18053
|
),
|
|
17408
18054
|
children: [
|
|
17409
|
-
/* @__PURE__ */ jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-[10px] bg-
|
|
18055
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-[10px] bg-background/15", children: /* @__PURE__ */ jsx(ChatIcon, { className: "h-5 w-5" }) }),
|
|
17410
18056
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
17411
18057
|
/* @__PURE__ */ jsx("div", { className: "truncate text-[15px] font-semibold leading-tight", children: businessName }),
|
|
17412
|
-
claimedAs ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 truncate text-xs text-
|
|
18058
|
+
claimedAs ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 truncate text-xs text-background/60", children: [
|
|
17413
18059
|
/* @__PURE__ */ jsxs("span", { className: "truncate", children: [
|
|
17414
18060
|
"Chatting as ",
|
|
17415
18061
|
claimedAs
|
|
@@ -17420,11 +18066,11 @@ function Header({
|
|
|
17420
18066
|
{
|
|
17421
18067
|
type: "button",
|
|
17422
18068
|
onClick: onSignOut,
|
|
17423
|
-
className: "flex-shrink-0 underline underline-offset-2 hover:text-
|
|
18069
|
+
className: "flex-shrink-0 underline underline-offset-2 hover:text-background",
|
|
17424
18070
|
children: "Not you?"
|
|
17425
18071
|
}
|
|
17426
18072
|
)
|
|
17427
|
-
] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-
|
|
18073
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-background/60", children: [
|
|
17428
18074
|
/* @__PURE__ */ jsx("span", { className: "h-[7px] w-[7px] flex-shrink-0 rounded-full bg-emerald-400" }),
|
|
17429
18075
|
"Online"
|
|
17430
18076
|
] })
|
|
@@ -17437,7 +18083,7 @@ function Header({
|
|
|
17437
18083
|
"aria-label": "Close chat",
|
|
17438
18084
|
className: cn(
|
|
17439
18085
|
"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg",
|
|
17440
|
-
"bg-
|
|
18086
|
+
"bg-background/10 text-background/70 transition-colors hover:bg-background/20 hover:text-background"
|
|
17441
18087
|
),
|
|
17442
18088
|
children: /* @__PURE__ */ jsx(CloseIcon, { className: "h-[18px] w-[18px]" })
|
|
17443
18089
|
}
|
|
@@ -17492,148 +18138,109 @@ function Welcome({
|
|
|
17492
18138
|
);
|
|
17493
18139
|
}
|
|
17494
18140
|
var QUICK_REACTIONS = ["\u{1F44D}", "\u2764\uFE0F"];
|
|
17495
|
-
function
|
|
17496
|
-
|
|
17497
|
-
for (let i = bubbles.length - 1; i >= 0; i--) {
|
|
17498
|
-
if (bubbles[i].sender_type !== "customer") continue;
|
|
17499
|
-
return bubbles[i].created_at <= seenAt ? bubbles[i].id : null;
|
|
17500
|
-
}
|
|
17501
|
-
return null;
|
|
18141
|
+
function LetterAvatar({ name }) {
|
|
18142
|
+
return /* @__PURE__ */ jsx("span", { className: "flex size-6 items-center justify-center rounded-full bg-foreground text-[10px] font-bold text-background", children: name.charAt(0).toUpperCase() });
|
|
17502
18143
|
}
|
|
17503
18144
|
function MessageList({
|
|
17504
18145
|
messages,
|
|
18146
|
+
businessName,
|
|
17505
18147
|
isLoading,
|
|
17506
18148
|
isSending,
|
|
17507
18149
|
seenAt,
|
|
18150
|
+
readAt,
|
|
18151
|
+
humanRequested,
|
|
18152
|
+
mayHaveOlder,
|
|
17508
18153
|
onReact,
|
|
18154
|
+
onReply,
|
|
18155
|
+
onRequestHuman,
|
|
18156
|
+
onLoadOlder,
|
|
17509
18157
|
className
|
|
17510
18158
|
}) {
|
|
17511
|
-
const
|
|
17512
|
-
const
|
|
18159
|
+
const scrolledUpRef = useRef(false);
|
|
18160
|
+
const timeline = chatTimeline(messages, { readAt });
|
|
18161
|
+
const { bubbles, reactionsByTarget, firstUnreadId, unreadOnOpen, firstAgentMessageId } = timeline;
|
|
18162
|
+
const lastBubble = bubbles[bubbles.length - 1];
|
|
18163
|
+
const offerHuman = !humanRequested && lastBubble?.sender_type === "bot" && !isSending;
|
|
18164
|
+
const handleScroll = (event) => {
|
|
18165
|
+
const node = event.currentTarget;
|
|
18166
|
+
scrolledUpRef.current = node.scrollHeight - node.scrollTop - node.clientHeight >= AT_BOTTOM_SLACK_PX;
|
|
18167
|
+
};
|
|
17513
18168
|
return /* @__PURE__ */ jsxs(
|
|
17514
18169
|
"div",
|
|
17515
18170
|
{
|
|
17516
|
-
|
|
17517
|
-
|
|
17518
|
-
|
|
17519
|
-
),
|
|
18171
|
+
onScroll: handleScroll,
|
|
18172
|
+
style: { background: "var(--color-chat-canvas, var(--color-muted, #f3f5f3))" },
|
|
18173
|
+
className: cn("flex flex-1 flex-col gap-0.5 overflow-y-auto px-3.5 py-3.5", className),
|
|
17520
18174
|
children: [
|
|
17521
18175
|
isLoading && messages.length === 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsx(TypingDots, {}) }),
|
|
18176
|
+
mayHaveOlder && /* @__PURE__ */ jsx("div", { className: "flex justify-center pb-1", children: /* @__PURE__ */ jsx(
|
|
18177
|
+
"button",
|
|
18178
|
+
{
|
|
18179
|
+
type: "button",
|
|
18180
|
+
onClick: onLoadOlder,
|
|
18181
|
+
className: "rounded-full border border-foreground/[0.06] bg-background px-3.5 py-[7px] text-xs font-semibold text-muted-foreground shadow-[0_1px_2px_rgba(15,23,42,0.04)]",
|
|
18182
|
+
children: "Show earlier messages"
|
|
18183
|
+
}
|
|
18184
|
+
) }),
|
|
17522
18185
|
bubbles.map((msg, i) => {
|
|
17523
|
-
const isCustomer = msg.sender_type === "customer";
|
|
17524
18186
|
const prev = bubbles[i - 1];
|
|
17525
|
-
const
|
|
17526
|
-
const
|
|
17527
|
-
const
|
|
17528
|
-
|
|
17529
|
-
|
|
17530
|
-
|
|
17531
|
-
|
|
17532
|
-
|
|
17533
|
-
|
|
17534
|
-
|
|
17535
|
-
|
|
18187
|
+
const newDay = !prev || !sameDay(prev.created_at, msg.created_at);
|
|
18188
|
+
const group = groupPosition(timeline, i);
|
|
18189
|
+
const startsRun = group === "single" || group === "first";
|
|
18190
|
+
const delivery = deliveryState(msg, timeline, seenAt, isOptimistic);
|
|
18191
|
+
return /* @__PURE__ */ jsxs(
|
|
18192
|
+
"div",
|
|
18193
|
+
{
|
|
18194
|
+
className: cn("flex flex-col gap-1.5", startsRun && i > 0 && "mt-2.5"),
|
|
18195
|
+
children: [
|
|
18196
|
+
newDay && /* @__PURE__ */ jsx(Seam, { variant: "chip", label: dayLabel(msg.created_at) }),
|
|
18197
|
+
msg.id === firstUnreadId && /* @__PURE__ */ jsx(
|
|
18198
|
+
Seam,
|
|
18199
|
+
{
|
|
18200
|
+
variant: "unread",
|
|
18201
|
+
label: `${unreadOnOpen} new message${unreadOnOpen === 1 ? "" : "s"}`
|
|
18202
|
+
}
|
|
17536
18203
|
),
|
|
17537
|
-
|
|
17538
|
-
|
|
17539
|
-
|
|
17540
|
-
|
|
17541
|
-
|
|
17542
|
-
"aria-label": `React ${emoji}`,
|
|
17543
|
-
onClick: () => void onReact(msg, emoji),
|
|
17544
|
-
className: "flex h-6 w-6 items-center justify-center rounded-full text-[13px] hover:bg-muted",
|
|
17545
|
-
children: emoji
|
|
17546
|
-
},
|
|
17547
|
-
emoji
|
|
17548
|
-
)) }),
|
|
17549
|
-
/* @__PURE__ */ jsxs(
|
|
17550
|
-
"div",
|
|
18204
|
+
msg.id === firstAgentMessageId && /* @__PURE__ */ jsx(Seam, { label: "A person joined" }),
|
|
18205
|
+
msg.sender_type === "system" ? /* @__PURE__ */ jsx(Seam, { label: "Passing you to the team" }) : /* @__PURE__ */ jsxs("div", { className: "group relative", children: [
|
|
18206
|
+
msg.sender_type !== "customer" && !isOptimistic(msg) && /* @__PURE__ */ jsx(HoverRail, { message: msg, onReact, onReply }),
|
|
18207
|
+
/* @__PURE__ */ jsx(
|
|
18208
|
+
MessageBubble,
|
|
17551
18209
|
{
|
|
17552
|
-
|
|
17553
|
-
|
|
17554
|
-
|
|
17555
|
-
|
|
17556
|
-
|
|
17557
|
-
|
|
17558
|
-
|
|
17559
|
-
|
|
17560
|
-
|
|
17561
|
-
|
|
17562
|
-
|
|
17563
|
-
msg.reply_to && /* @__PURE__ */ jsxs(
|
|
17564
|
-
"div",
|
|
17565
|
-
{
|
|
17566
|
-
className: cn(
|
|
17567
|
-
"mb-1.5 flex gap-1.5 rounded-lg px-2 py-1 text-xs",
|
|
17568
|
-
isCustomer ? "bg-background/15" : "bg-muted"
|
|
17569
|
-
),
|
|
17570
|
-
children: [
|
|
17571
|
-
/* @__PURE__ */ jsx(
|
|
17572
|
-
"span",
|
|
17573
|
-
{
|
|
17574
|
-
className: cn(
|
|
17575
|
-
"w-[3px] shrink-0 self-stretch rounded-full",
|
|
17576
|
-
isCustomer ? "bg-background/40" : "bg-muted-foreground/40"
|
|
17577
|
-
)
|
|
17578
|
-
}
|
|
17579
|
-
),
|
|
17580
|
-
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate opacity-80", children: msg.reply_to.preview })
|
|
17581
|
-
]
|
|
17582
|
-
}
|
|
17583
|
-
),
|
|
17584
|
-
msg.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap gap-1.5", msg.content && "mb-1.5"), children: msg.attachments.map((attachment, index) => {
|
|
17585
|
-
const url = safeAttachmentUrl(attachment.url);
|
|
17586
|
-
if (!url) return null;
|
|
17587
|
-
return attachment.mime_type?.startsWith("image/") ? /* @__PURE__ */ jsx("a", { href: url, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsx(
|
|
17588
|
-
"img",
|
|
17589
|
-
{
|
|
17590
|
-
src: url,
|
|
17591
|
-
alt: attachment.filename ?? "Photo",
|
|
17592
|
-
loading: "lazy",
|
|
17593
|
-
className: "max-h-48 max-w-full rounded-lg object-cover"
|
|
17594
|
-
}
|
|
17595
|
-
) }, index) : /* @__PURE__ */ jsx(
|
|
17596
|
-
"a",
|
|
17597
|
-
{
|
|
17598
|
-
href: url,
|
|
17599
|
-
target: "_blank",
|
|
17600
|
-
rel: "noreferrer",
|
|
17601
|
-
className: "truncate text-xs underline",
|
|
17602
|
-
children: attachment.filename ?? "Attachment"
|
|
17603
|
-
},
|
|
17604
|
-
index
|
|
17605
|
-
);
|
|
17606
|
-
}) }),
|
|
17607
|
-
isCustomer ? msg.content && /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap", children: msg.content }) : /* @__PURE__ */ jsx(
|
|
17608
|
-
ChatMarkdown,
|
|
17609
|
-
{
|
|
17610
|
-
text: msg.content,
|
|
17611
|
-
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"
|
|
17612
|
-
}
|
|
17613
|
-
)
|
|
17614
|
-
]
|
|
18210
|
+
message: msg,
|
|
18211
|
+
reactions: reactionsByTarget.get(msg.id),
|
|
18212
|
+
businessName,
|
|
18213
|
+
group,
|
|
18214
|
+
delivery,
|
|
18215
|
+
onReply,
|
|
18216
|
+
avatar: /* @__PURE__ */ jsx(LetterAvatar, { name: businessName }),
|
|
18217
|
+
onQuoteMiss: async () => {
|
|
18218
|
+
onLoadOlder();
|
|
18219
|
+
return true;
|
|
18220
|
+
}
|
|
17615
18221
|
}
|
|
17616
18222
|
)
|
|
17617
|
-
]
|
|
17618
|
-
|
|
17619
|
-
|
|
17620
|
-
|
|
17621
|
-
|
|
17622
|
-
{
|
|
17623
|
-
className: "rounded-full border border-border bg-background px-1.5 py-0.5 text-xs shadow-sm",
|
|
17624
|
-
children: reaction.content
|
|
17625
|
-
},
|
|
17626
|
-
reaction.id
|
|
17627
|
-
)) }),
|
|
17628
|
-
msg.id === seenMessageId && /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-right text-[10px] font-medium text-muted-foreground", children: "Seen" })
|
|
17629
|
-
] }, msg.id);
|
|
18223
|
+
] })
|
|
18224
|
+
]
|
|
18225
|
+
},
|
|
18226
|
+
msg.id
|
|
18227
|
+
);
|
|
17630
18228
|
}),
|
|
17631
18229
|
isSending && /* @__PURE__ */ jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsx(TypingDots, {}) }),
|
|
18230
|
+
offerHuman && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(
|
|
18231
|
+
"button",
|
|
18232
|
+
{
|
|
18233
|
+
type: "button",
|
|
18234
|
+
onClick: onRequestHuman,
|
|
18235
|
+
className: "rounded-full border border-border bg-background px-3 py-1.5 text-xs font-semibold text-foreground hover:border-foreground",
|
|
18236
|
+
children: "Talk to a person"
|
|
18237
|
+
}
|
|
18238
|
+
) }),
|
|
17632
18239
|
/* @__PURE__ */ jsx(
|
|
17633
18240
|
"div",
|
|
17634
18241
|
{
|
|
17635
18242
|
ref: (node) => {
|
|
17636
|
-
node
|
|
18243
|
+
if (node && !scrolledUpRef.current) node.scrollIntoView({ behavior: "smooth" });
|
|
17637
18244
|
}
|
|
17638
18245
|
},
|
|
17639
18246
|
`${messages.length}:${isSending}`
|
|
@@ -17642,6 +18249,172 @@ function MessageList({
|
|
|
17642
18249
|
}
|
|
17643
18250
|
);
|
|
17644
18251
|
}
|
|
18252
|
+
function HoverRail({
|
|
18253
|
+
message,
|
|
18254
|
+
onReact,
|
|
18255
|
+
onReply
|
|
18256
|
+
}) {
|
|
18257
|
+
return /* @__PURE__ */ 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: [
|
|
18258
|
+
QUICK_REACTIONS.map((emoji) => /* @__PURE__ */ jsx(
|
|
18259
|
+
"button",
|
|
18260
|
+
{
|
|
18261
|
+
type: "button",
|
|
18262
|
+
"aria-label": `React ${emoji}`,
|
|
18263
|
+
onClick: () => void onReact(message, emoji),
|
|
18264
|
+
className: "flex h-6 w-6 items-center justify-center rounded-full text-[13px] hover:bg-muted",
|
|
18265
|
+
children: emoji
|
|
18266
|
+
},
|
|
18267
|
+
emoji
|
|
18268
|
+
)),
|
|
18269
|
+
/* @__PURE__ */ jsx(
|
|
18270
|
+
"button",
|
|
18271
|
+
{
|
|
18272
|
+
type: "button",
|
|
18273
|
+
"aria-label": "Reply",
|
|
18274
|
+
onClick: () => onReply(message),
|
|
18275
|
+
className: "flex h-6 w-6 items-center justify-center rounded-full text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
18276
|
+
children: /* @__PURE__ */ jsx(ReplyIcon, { className: "h-3.5 w-3.5" })
|
|
18277
|
+
}
|
|
18278
|
+
)
|
|
18279
|
+
] });
|
|
18280
|
+
}
|
|
18281
|
+
function readKeepDismissed() {
|
|
18282
|
+
try {
|
|
18283
|
+
return typeof window !== "undefined" && window.localStorage.getItem(KEEP_DISMISSED_KEY) === "1";
|
|
18284
|
+
} catch {
|
|
18285
|
+
return false;
|
|
18286
|
+
}
|
|
18287
|
+
}
|
|
18288
|
+
function writeKeepDismissed() {
|
|
18289
|
+
try {
|
|
18290
|
+
window.localStorage.setItem(KEEP_DISMISSED_KEY, "1");
|
|
18291
|
+
} catch {
|
|
18292
|
+
}
|
|
18293
|
+
}
|
|
18294
|
+
function KeepConversation({ client }) {
|
|
18295
|
+
const [step, setStep] = useState("row");
|
|
18296
|
+
const [dismissed, setDismissed] = useState(readKeepDismissed);
|
|
18297
|
+
const [contact, setContact] = useState("");
|
|
18298
|
+
const [code, setCode] = useState("");
|
|
18299
|
+
const [busy, setBusy] = useState(false);
|
|
18300
|
+
const [verifyError, setVerifyError] = useState(null);
|
|
18301
|
+
if (dismissed) return null;
|
|
18302
|
+
const contactType = contact.includes("@") ? "email" : "phone";
|
|
18303
|
+
const requestCode = async () => {
|
|
18304
|
+
const trimmed = contact.trim();
|
|
18305
|
+
if (!trimmed || busy) return;
|
|
18306
|
+
setBusy(true);
|
|
18307
|
+
setVerifyError(null);
|
|
18308
|
+
const result = await client.link.requestOtp({ contact: trimmed, contact_type: contactType });
|
|
18309
|
+
setBusy(false);
|
|
18310
|
+
if (result.ok) {
|
|
18311
|
+
setCode("");
|
|
18312
|
+
setStep("code");
|
|
18313
|
+
} else {
|
|
18314
|
+
setVerifyError(result.error.userMessage || "Couldn't send a code \u2014 try again.");
|
|
18315
|
+
}
|
|
18316
|
+
};
|
|
18317
|
+
const verify = async (value) => {
|
|
18318
|
+
if (value.length !== 6 || busy) return;
|
|
18319
|
+
setBusy(true);
|
|
18320
|
+
setVerifyError(null);
|
|
18321
|
+
const result = await client.link.verifyOtp({
|
|
18322
|
+
contact: contact.trim(),
|
|
18323
|
+
contact_type: contactType,
|
|
18324
|
+
otp_code: value
|
|
18325
|
+
});
|
|
18326
|
+
setBusy(false);
|
|
18327
|
+
if (!result.ok) {
|
|
18328
|
+
setCode("");
|
|
18329
|
+
setVerifyError(result.error.userMessage || "Invalid or expired code.");
|
|
18330
|
+
}
|
|
18331
|
+
};
|
|
18332
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex-shrink-0 border-t border-border bg-muted/50 px-4 py-2.5", children: [
|
|
18333
|
+
step === "row" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
|
|
18334
|
+
/* @__PURE__ */ jsxs(
|
|
18335
|
+
"button",
|
|
18336
|
+
{
|
|
18337
|
+
type: "button",
|
|
18338
|
+
onClick: () => setStep("contact"),
|
|
18339
|
+
className: "flex min-w-0 flex-1 flex-col text-left",
|
|
18340
|
+
children: [
|
|
18341
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-foreground", children: "Keep this conversation" }),
|
|
18342
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: "Verify your number and it follows you \u2014 any device" })
|
|
18343
|
+
]
|
|
18344
|
+
}
|
|
18345
|
+
),
|
|
18346
|
+
/* @__PURE__ */ jsx(
|
|
18347
|
+
"button",
|
|
18348
|
+
{
|
|
18349
|
+
type: "button",
|
|
18350
|
+
"aria-label": "Dismiss",
|
|
18351
|
+
onClick: () => {
|
|
18352
|
+
writeKeepDismissed();
|
|
18353
|
+
setDismissed(true);
|
|
18354
|
+
},
|
|
18355
|
+
className: "flex-shrink-0 p-1 text-muted-foreground hover:text-foreground",
|
|
18356
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { className: "h-3.5 w-3.5" })
|
|
18357
|
+
}
|
|
18358
|
+
)
|
|
18359
|
+
] }),
|
|
18360
|
+
step === "contact" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
18361
|
+
/* @__PURE__ */ jsx(
|
|
18362
|
+
"input",
|
|
18363
|
+
{
|
|
18364
|
+
type: "text",
|
|
18365
|
+
inputMode: "email",
|
|
18366
|
+
value: contact,
|
|
18367
|
+
onChange: (e) => setContact(e.target.value),
|
|
18368
|
+
onKeyDown: (e) => e.key === "Enter" && void requestCode(),
|
|
18369
|
+
placeholder: "Phone or email",
|
|
18370
|
+
autoFocus: true,
|
|
18371
|
+
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"
|
|
18372
|
+
}
|
|
18373
|
+
),
|
|
18374
|
+
/* @__PURE__ */ jsx(
|
|
18375
|
+
"button",
|
|
18376
|
+
{
|
|
18377
|
+
type: "button",
|
|
18378
|
+
onClick: () => void requestCode(),
|
|
18379
|
+
disabled: !contact.trim() || busy,
|
|
18380
|
+
className: "h-9 flex-shrink-0 rounded-lg bg-foreground px-3 text-xs font-semibold text-background disabled:opacity-50",
|
|
18381
|
+
children: busy ? "Sending\u2026" : "Send code"
|
|
18382
|
+
}
|
|
18383
|
+
)
|
|
18384
|
+
] }),
|
|
18385
|
+
step === "code" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
18386
|
+
/* @__PURE__ */ jsx(
|
|
18387
|
+
"input",
|
|
18388
|
+
{
|
|
18389
|
+
type: "text",
|
|
18390
|
+
inputMode: "numeric",
|
|
18391
|
+
autoComplete: "one-time-code",
|
|
18392
|
+
maxLength: 6,
|
|
18393
|
+
value: code,
|
|
18394
|
+
onChange: (e) => {
|
|
18395
|
+
const digits = e.target.value.replace(/\D/g, "").slice(0, 6);
|
|
18396
|
+
setCode(digits);
|
|
18397
|
+
if (digits.length === 6) void verify(digits);
|
|
18398
|
+
},
|
|
18399
|
+
placeholder: "6-digit code",
|
|
18400
|
+
autoFocus: true,
|
|
18401
|
+
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"
|
|
18402
|
+
}
|
|
18403
|
+
),
|
|
18404
|
+
/* @__PURE__ */ jsx(
|
|
18405
|
+
"button",
|
|
18406
|
+
{
|
|
18407
|
+
type: "button",
|
|
18408
|
+
onClick: () => void requestCode(),
|
|
18409
|
+
disabled: busy,
|
|
18410
|
+
className: "h-9 flex-shrink-0 rounded-lg px-2 text-[11px] font-medium text-muted-foreground hover:text-foreground disabled:opacity-50",
|
|
18411
|
+
children: "Resend"
|
|
18412
|
+
}
|
|
18413
|
+
)
|
|
18414
|
+
] }),
|
|
18415
|
+
verifyError && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-[11px] text-destructive", children: verifyError })
|
|
18416
|
+
] });
|
|
18417
|
+
}
|
|
17645
18418
|
function ChatInput({
|
|
17646
18419
|
placeholder,
|
|
17647
18420
|
isSending,
|
|
@@ -17664,14 +18437,15 @@ function ChatInput({
|
|
|
17664
18437
|
},
|
|
17665
18438
|
[onAttach]
|
|
17666
18439
|
);
|
|
17667
|
-
const handleSend = useCallback(() => {
|
|
18440
|
+
const handleSend = useCallback(async () => {
|
|
17668
18441
|
const trimmed = value.trim();
|
|
17669
18442
|
if (!trimmed || isSending) return;
|
|
17670
18443
|
setValue("");
|
|
17671
18444
|
if (textareaRef.current) {
|
|
17672
18445
|
textareaRef.current.style.height = "auto";
|
|
17673
18446
|
}
|
|
17674
|
-
onSend(trimmed);
|
|
18447
|
+
const sent = await onSend(trimmed);
|
|
18448
|
+
if (!sent) setValue((current) => current || trimmed);
|
|
17675
18449
|
}, [value, isSending, onSend]);
|
|
17676
18450
|
const handleKeyDown = useCallback(
|
|
17677
18451
|
(e) => {
|
|
@@ -17849,16 +18623,21 @@ function ImageIcon({ className }) {
|
|
|
17849
18623
|
}
|
|
17850
18624
|
);
|
|
17851
18625
|
}
|
|
17852
|
-
function
|
|
17853
|
-
return /* @__PURE__ */
|
|
18626
|
+
function ReplyIcon({ className }) {
|
|
18627
|
+
return /* @__PURE__ */ jsxs(
|
|
17854
18628
|
"svg",
|
|
17855
18629
|
{
|
|
17856
18630
|
className,
|
|
17857
18631
|
viewBox: "0 0 24 24",
|
|
17858
18632
|
fill: "none",
|
|
17859
18633
|
stroke: "currentColor",
|
|
17860
|
-
strokeWidth: 2
|
|
17861
|
-
|
|
18634
|
+
strokeWidth: 2,
|
|
18635
|
+
strokeLinecap: "round",
|
|
18636
|
+
strokeLinejoin: "round",
|
|
18637
|
+
children: [
|
|
18638
|
+
/* @__PURE__ */ jsx("polyline", { points: "9 17 4 12 9 7" }),
|
|
18639
|
+
/* @__PURE__ */ jsx("path", { d: "M20 18v-2a4 4 0 0 0-4-4H4" })
|
|
18640
|
+
]
|
|
17862
18641
|
}
|
|
17863
18642
|
);
|
|
17864
18643
|
}
|
|
@@ -19684,4 +20463,4 @@ async function genUIFingerprint(manifest) {
|
|
|
19684
20463
|
return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 16);
|
|
19685
20464
|
}
|
|
19686
20465
|
|
|
19687
|
-
export { AccommodationCard, AccountContent, AccountDashboardPage, AccountHero, AccountIdentityStrip, AccountLineItem, AccountLineItems, AccountNav, AccountOrderDetailPage, AccountOrderView, AccountOrdersPage, AccountShell, AccountSidebar, AccountSignedOutPrompt, AccountSubscriptionDetailPage, AccountSubscriptionsPage, Ad, AdProvider, AddOnSelector, AddressAutocomplete, AddressElement, AuthElement, AvailabilityBadge, BackLink, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingSummary, BookingsPage, BoxIcon24, BundleProductCard, BundleProductLayout, BundleSelector, Button, CIMPLIFY_GENUI_REGISTRY, CardImage, CardShell, CardVariant, CartDrawer, CartDrawerProvider, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatMarkdown, ChatWidget, CheckIcon, CheckoutPage, CimplifyCheckout, CimplifyProvider, CimplifySignInButton, CimplifyUserButton, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, ContextCard, ContinuityCard, CurrencySelector, CustomAttributesTable, CustomerInputFields, DashboardIcon, DashboardIcon20, DatePicker, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DeliveryTracker, DeliveryTruckIcon, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, EmptyState, ErrorState, FeaturedCollectionLayout, FilterTabs, FoodProductCard, FoodProductLayout, GenUIView, HappeningCard, IdentityIcon, IdentityOfframp, InventoryBadge, ItemsCard, LeaseServiceCard, LineItemDetails, LocationPicker, MediaGallery, MetadataStringList, MobileAccountShell, MobileBackButton, MobileMoreButton, MobileTabBar, NextDeliveryCard, OrderDetailHeader, OrderDetailMetaPipe, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderList, OrderRow, OrderSummary, OrderTimeline, OrderTotals, OrdersIcon, OrdersIcon20, OvenIcon, PackageIcon, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductModel3D, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, RichText, SaleBadge, SalePrice, SaleTeaser, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, Section, SectionMore, ServiceProductLayout, SessionMessageBanner, SignOutIcon, Skeleton, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, Status, StoreNav, StoreVideo, SubscriptionCard, SubscriptionList, SubscriptionRow, SubscriptionsIcon, SubscriptionsIcon20, TagPills, TimePicker, Toast, TwoColumnGrid, VariantSelector, VolumePricing, WelcomeCard, WholesaleProductCard, WholesaleProductLayout, WishlistButton, clearOrdersCache, cn, createPlacesSessionToken, defaultBoundaryKind, genUICatalog, genUIFingerprint, genUIManifest, genUIManifestJson, getBoundaryLabels, isImageAttachment, isTerminalOrderStatus, parse as parseGenUIProgram, partitionMessages, prefetchProduct, prefetchProducts, resolveBoundaryKind, roomToResource, safeAttachmentUrl, sanitizeRichTextHtml, stripHtmlToText, toAccountLineItem, toAccountLineItems, toAccountOrderView, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCartDrawer, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCimplifySession, useCollection, useCollections, useDeals, useDeliveryFee, useDeliveryOptions, useElements, useElementsReady, useFxRate, useGenUIProgram, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useStoreCredit, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
|
|
20466
|
+
export { AccommodationCard, AccountContent, AccountDashboardPage, AccountHero, AccountIdentityStrip, AccountLineItem, AccountLineItems, AccountNav, AccountOrderDetailPage, AccountOrderView, AccountOrdersPage, AccountShell, AccountSidebar, AccountSignedOutPrompt, AccountSubscriptionDetailPage, AccountSubscriptionsPage, Ad, AdProvider, AddOnSelector, AddressAutocomplete, AddressElement, AuthElement, AvailabilityBadge, BackLink, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingSummary, BookingsPage, BoxIcon24, BubbleTail, BundleProductCard, BundleProductLayout, BundleSelector, Button, CIMPLIFY_GENUI_REGISTRY, CardImage, CardShell, CardVariant, CartDrawer, CartDrawerProvider, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatMarkdown, ChatWidget, CheckIcon, CheckoutPage, CimplifyCheckout, CimplifyProvider, CimplifySignInButton, CimplifyUserButton, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, ContextCard, ContinuityCard, CurrencySelector, CustomAttributesTable, CustomerInputFields, DashboardIcon, DashboardIcon20, DatePicker, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DeliveryTracker, DeliveryTruckIcon, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, EmptyState, ErrorState, FeaturedCollectionLayout, FilterTabs, FoodProductCard, FoodProductLayout, GenUIView, HappeningCard, IdentityIcon, IdentityOfframp, InventoryBadge, ItemsCard, LeaseServiceCard, LineItemDetails, LocationPicker, MediaGallery, MessageBubble, MetadataStringList, MobileAccountShell, MobileBackButton, MobileMoreButton, MobileTabBar, NextDeliveryCard, OrderDetailHeader, OrderDetailMetaPipe, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderList, OrderRow, OrderSummary, OrderTimeline, OrderTotals, OrdersIcon, OrdersIcon20, OvenIcon, PackageIcon, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductModel3D, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, QuoteBlock, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, RichText, SWIPE_COMMIT_PX, SaleBadge, SalePrice, SaleTeaser, ScheduleServiceCard, Seam, SearchInput, SearchPage, SearchTemplate, Section, SectionMore, ServiceProductLayout, SessionMessageBanner, SignOutIcon, Skeleton, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, Status, StoreNav, StoreVideo, SubscriptionCard, SubscriptionList, SubscriptionRow, SubscriptionsIcon, SubscriptionsIcon20, TagPills, TimePicker, Toast, TwoColumnGrid, VariantSelector, VolumePricing, WelcomeCard, WholesaleProductCard, WholesaleProductLayout, WishlistButton, chatTimeline, clearOrdersCache, cn, createPlacesSessionToken, dayLabel, defaultBoundaryKind, deliveryState, genUICatalog, genUIFingerprint, genUIManifest, genUIManifestJson, getBoundaryLabels, groupPosition, isImageAttachment, isOptimistic, isTerminalOrderStatus, optimisticId, parse as parseGenUIProgram, partitionMessages, prefersReducedMotion, prefetchProduct, prefetchProducts, proseOf, resolveBoundaryKind, roomToResource, safeAttachmentUrl, sameDay, sanitizeRichTextHtml, scrollBehavior, stripHtmlToText, swipeCommitted, swipeProgress, swipeTravel, toAccountLineItem, toAccountLineItems, toAccountOrderView, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCartDrawer, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCimplifySession, useCollection, useCollections, useDeals, useDeliveryFee, useDeliveryOptions, useElements, useElementsReady, useFxRate, useGenUIProgram, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useStoreCredit, useSubscription, useSubscriptions, useSwipeReply, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
|