@customerhero/react 2.2.0 → 2.4.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/README.md +19 -0
- package/dist/chunk-M7MROJ5Z.js +3138 -0
- package/dist/index.cjs +463 -145
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +20 -2820
- package/dist/preview.cjs +3182 -0
- package/dist/preview.d.cts +32 -0
- package/dist/preview.d.ts +32 -0
- package/dist/preview.js +53 -0
- package/package.json +12 -2
package/dist/index.cjs
CHANGED
|
@@ -18,16 +18,16 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
|
|
20
20
|
// src/index.ts
|
|
21
|
-
var
|
|
22
|
-
__export(
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
23
|
ActionConfirmationCard: () => ActionConfirmationCard,
|
|
24
24
|
ChatWidget: () => ChatWidget,
|
|
25
25
|
useChat: () => useChat
|
|
26
26
|
});
|
|
27
|
-
module.exports = __toCommonJS(
|
|
27
|
+
module.exports = __toCommonJS(src_exports);
|
|
28
28
|
|
|
29
29
|
// src/components/chat-widget.tsx
|
|
30
|
-
var
|
|
30
|
+
var import_react12 = require("react");
|
|
31
31
|
|
|
32
32
|
// src/context.tsx
|
|
33
33
|
var import_react = require("react");
|
|
@@ -36,6 +36,7 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
36
36
|
var CustomerHeroContext = (0, import_react.createContext)(null);
|
|
37
37
|
function CustomerHeroProvider({
|
|
38
38
|
children,
|
|
39
|
+
disableAutoFetch,
|
|
39
40
|
...config
|
|
40
41
|
}) {
|
|
41
42
|
const clientRef = (0, import_react.useRef)(null);
|
|
@@ -43,8 +44,9 @@ function CustomerHeroProvider({
|
|
|
43
44
|
clientRef.current = new import_js.CustomerHeroChat(config);
|
|
44
45
|
}
|
|
45
46
|
(0, import_react.useEffect)(() => {
|
|
47
|
+
if (disableAutoFetch) return;
|
|
46
48
|
clientRef.current?.fetchConfig();
|
|
47
|
-
}, []);
|
|
49
|
+
}, [disableAutoFetch]);
|
|
48
50
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CustomerHeroContext.Provider, { value: clientRef.current, children });
|
|
49
51
|
}
|
|
50
52
|
function useCustomerHeroClient() {
|
|
@@ -127,7 +129,7 @@ function useChat() {
|
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
// src/components/chat-bubble.tsx
|
|
130
|
-
var
|
|
132
|
+
var import_react5 = require("react");
|
|
131
133
|
|
|
132
134
|
// src/use-reduced-motion.ts
|
|
133
135
|
var import_react3 = require("react");
|
|
@@ -145,41 +147,110 @@ function useReducedMotion() {
|
|
|
145
147
|
return reduced;
|
|
146
148
|
}
|
|
147
149
|
|
|
150
|
+
// src/use-effective-theme.ts
|
|
151
|
+
var import_js2 = require("@customerhero/js");
|
|
152
|
+
|
|
153
|
+
// src/use-prefers-dark.ts
|
|
154
|
+
var import_react4 = require("react");
|
|
155
|
+
function usePrefersDark() {
|
|
156
|
+
const [prefersDark, setPrefersDark] = (0, import_react4.useState)(false);
|
|
157
|
+
(0, import_react4.useEffect)(() => {
|
|
158
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
162
|
+
const update = () => setPrefersDark(mq.matches);
|
|
163
|
+
update();
|
|
164
|
+
if (typeof mq.addEventListener === "function") {
|
|
165
|
+
mq.addEventListener("change", update);
|
|
166
|
+
return () => mq.removeEventListener("change", update);
|
|
167
|
+
}
|
|
168
|
+
mq.addListener(update);
|
|
169
|
+
return () => mq.removeListener(update);
|
|
170
|
+
}, []);
|
|
171
|
+
return prefersDark;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/use-effective-theme.ts
|
|
175
|
+
function useEffectiveTheme() {
|
|
176
|
+
const { config } = useChat();
|
|
177
|
+
const prefersDark = usePrefersDark();
|
|
178
|
+
const scheme = (0, import_js2.resolveScheme)(config.colorScheme, prefersDark);
|
|
179
|
+
const colors = (0, import_js2.effectiveColors)(config, scheme);
|
|
180
|
+
const isDark = scheme === "dark";
|
|
181
|
+
return {
|
|
182
|
+
scheme,
|
|
183
|
+
primary: colors.primary,
|
|
184
|
+
background: colors.background,
|
|
185
|
+
text: colors.text,
|
|
186
|
+
bubbleBackground: isDark ? "rgba(255,255,255,0.08)" : "#f0f0f0",
|
|
187
|
+
divider: isDark ? "rgba(255,255,255,0.12)" : "#e0e0e0",
|
|
188
|
+
size: (0, import_js2.sizePreset)(config.size),
|
|
189
|
+
radius: (0, import_js2.panelRadius)(config.cornerStyle)
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
148
193
|
// src/components/chat-bubble.tsx
|
|
149
194
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
150
|
-
function ChatBubble() {
|
|
195
|
+
function ChatBubble({ embedded } = {}) {
|
|
151
196
|
const { toggle, config, t, isRtl } = useChat();
|
|
152
197
|
const reduced = useReducedMotion();
|
|
153
|
-
const
|
|
154
|
-
(0,
|
|
198
|
+
const theme = useEffectiveTheme();
|
|
199
|
+
const [mounted, setMounted] = (0, import_react5.useState)(false);
|
|
200
|
+
(0, import_react5.useEffect)(() => {
|
|
155
201
|
const id = requestAnimationFrame(() => setMounted(true));
|
|
156
202
|
return () => cancelAnimationFrame(id);
|
|
157
203
|
}, []);
|
|
158
204
|
const visible = mounted;
|
|
159
205
|
const effectivePosition = isRtl ? config.position === "bottom-right" ? "bottom-left" : "bottom-right" : config.position;
|
|
206
|
+
const colors = theme;
|
|
207
|
+
const preset = theme.size;
|
|
208
|
+
const hasLabel = !!config.launcher.label;
|
|
209
|
+
const width = hasLabel ? "auto" : preset.bubble;
|
|
210
|
+
const height = preset.bubble;
|
|
211
|
+
const borderRadius = hasLabel ? preset.bubble / 2 : "50%";
|
|
160
212
|
const style = {
|
|
161
|
-
position: "fixed",
|
|
162
|
-
bottom:
|
|
163
|
-
[effectivePosition === "bottom-left" ? "left" : "right"]:
|
|
164
|
-
width
|
|
165
|
-
height
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
213
|
+
position: embedded ? "absolute" : "fixed",
|
|
214
|
+
bottom: config.offset.bottom,
|
|
215
|
+
[effectivePosition === "bottom-left" ? "left" : "right"]: config.offset.side,
|
|
216
|
+
width,
|
|
217
|
+
height,
|
|
218
|
+
minWidth: preset.bubble,
|
|
219
|
+
paddingInline: hasLabel ? Math.round(preset.bubble * 0.35) : 0,
|
|
220
|
+
borderRadius,
|
|
221
|
+
background: colors.primary,
|
|
222
|
+
color: "#FFFFFF",
|
|
169
223
|
display: "flex",
|
|
170
224
|
alignItems: "center",
|
|
171
225
|
justifyContent: "center",
|
|
226
|
+
gap: hasLabel ? 8 : 0,
|
|
172
227
|
cursor: "pointer",
|
|
173
|
-
boxShadow: "0 4px 20px rgba(0,0,0,0.15)",
|
|
174
|
-
zIndex:
|
|
228
|
+
boxShadow: theme.scheme === "dark" ? "0 6px 20px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.06)" : "0 4px 20px rgba(0,0,0,0.15)",
|
|
229
|
+
zIndex: config.zIndex,
|
|
175
230
|
border: "none",
|
|
176
|
-
|
|
231
|
+
fontSize: preset.fontSize,
|
|
232
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
233
|
+
fontWeight: 500,
|
|
177
234
|
opacity: visible ? 1 : 0,
|
|
178
235
|
transform: visible ? "scale(1)" : "scale(0.6)",
|
|
179
236
|
transition: reduced ? "none" : "opacity 0.25s ease, transform 0.25s ease",
|
|
180
237
|
pointerEvents: visible ? "auto" : "none"
|
|
181
238
|
};
|
|
182
|
-
|
|
239
|
+
const iconSize = Math.round(preset.bubble * 0.43);
|
|
240
|
+
const dotSize = Math.max(10, Math.round(preset.bubble * 0.22));
|
|
241
|
+
const dotOffset = Math.round(preset.bubble * 0.08);
|
|
242
|
+
const dotStyle = {
|
|
243
|
+
position: "absolute",
|
|
244
|
+
top: dotOffset,
|
|
245
|
+
[effectivePosition === "bottom-left" ? "left" : "right"]: dotOffset,
|
|
246
|
+
width: dotSize,
|
|
247
|
+
height: dotSize,
|
|
248
|
+
borderRadius: "50%",
|
|
249
|
+
background: "#22c55e",
|
|
250
|
+
border: "2px solid rgba(255,255,255,0.9)",
|
|
251
|
+
pointerEvents: "none"
|
|
252
|
+
};
|
|
253
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
183
254
|
"button",
|
|
184
255
|
{
|
|
185
256
|
onClick: toggle,
|
|
@@ -187,41 +258,55 @@ function ChatBubble() {
|
|
|
187
258
|
dir: isRtl ? "rtl" : "ltr",
|
|
188
259
|
"aria-label": t("open_chat"),
|
|
189
260
|
onMouseEnter: (e) => {
|
|
190
|
-
if (!reduced) e.currentTarget.style.transform = "scale(1.
|
|
261
|
+
if (!reduced) e.currentTarget.style.transform = "scale(1.06)";
|
|
191
262
|
},
|
|
192
263
|
onMouseLeave: (e) => {
|
|
193
264
|
if (!reduced) e.currentTarget.style.transform = "scale(1)";
|
|
194
265
|
},
|
|
195
|
-
children:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
266
|
+
children: [
|
|
267
|
+
config.launcher.iconUrl ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
268
|
+
"img",
|
|
269
|
+
{
|
|
270
|
+
src: config.launcher.iconUrl,
|
|
271
|
+
alt: "",
|
|
272
|
+
width: iconSize,
|
|
273
|
+
height: iconSize,
|
|
274
|
+
style: { display: "block" }
|
|
275
|
+
}
|
|
276
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
277
|
+
"svg",
|
|
278
|
+
{
|
|
279
|
+
width: iconSize,
|
|
280
|
+
height: iconSize,
|
|
281
|
+
viewBox: "0 0 24 24",
|
|
282
|
+
fill: "none",
|
|
283
|
+
stroke: "currentColor",
|
|
284
|
+
strokeWidth: "2",
|
|
285
|
+
strokeLinecap: "round",
|
|
286
|
+
strokeLinejoin: "round",
|
|
287
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
|
|
288
|
+
}
|
|
289
|
+
),
|
|
290
|
+
hasLabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: config.launcher.label }),
|
|
291
|
+
config.launcher.showOnlineDot && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: dotStyle, "aria-hidden": true })
|
|
292
|
+
]
|
|
209
293
|
}
|
|
210
294
|
);
|
|
211
295
|
}
|
|
212
296
|
|
|
213
297
|
// src/components/chat-window.tsx
|
|
214
|
-
var
|
|
298
|
+
var import_react11 = require("react");
|
|
215
299
|
|
|
216
300
|
// src/components/chat-header.tsx
|
|
217
|
-
var
|
|
301
|
+
var import_react6 = require("react");
|
|
218
302
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
219
303
|
function ChatHeader() {
|
|
220
304
|
const { config, close, reset, t } = useChat();
|
|
221
305
|
const reduced = useReducedMotion();
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
(0,
|
|
306
|
+
const theme = useEffectiveTheme();
|
|
307
|
+
const [menuOpen, setMenuOpen] = (0, import_react6.useState)(false);
|
|
308
|
+
const menuRef = (0, import_react6.useRef)(null);
|
|
309
|
+
(0, import_react6.useEffect)(() => {
|
|
225
310
|
if (!menuOpen) return;
|
|
226
311
|
const handleClick = (e) => {
|
|
227
312
|
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
@@ -232,7 +317,7 @@ function ChatHeader() {
|
|
|
232
317
|
return () => document.removeEventListener("mousedown", handleClick);
|
|
233
318
|
}, [menuOpen]);
|
|
234
319
|
const headerStyle = {
|
|
235
|
-
background:
|
|
320
|
+
background: theme.primary,
|
|
236
321
|
padding: 16,
|
|
237
322
|
display: "flex",
|
|
238
323
|
alignItems: "center",
|
|
@@ -267,14 +352,16 @@ function ChatHeader() {
|
|
|
267
352
|
opacity: 0.7,
|
|
268
353
|
padding: 4
|
|
269
354
|
};
|
|
355
|
+
const isDark = theme.scheme === "dark";
|
|
270
356
|
const menuStyle = {
|
|
271
357
|
position: "absolute",
|
|
272
358
|
top: "100%",
|
|
273
359
|
right: 0,
|
|
274
360
|
marginTop: 4,
|
|
275
|
-
background:
|
|
361
|
+
background: theme.background,
|
|
362
|
+
border: `1px solid ${theme.divider}`,
|
|
276
363
|
borderRadius: 8,
|
|
277
|
-
boxShadow: "0 4px 16px rgba(0,0,0,0.15)",
|
|
364
|
+
boxShadow: isDark ? "0 4px 16px rgba(0,0,0,0.5)" : "0 4px 16px rgba(0,0,0,0.15)",
|
|
278
365
|
minWidth: 180,
|
|
279
366
|
overflow: "hidden",
|
|
280
367
|
zIndex: 10,
|
|
@@ -291,7 +378,7 @@ function ChatHeader() {
|
|
|
291
378
|
background: "none",
|
|
292
379
|
cursor: "pointer",
|
|
293
380
|
fontSize: 13,
|
|
294
|
-
color:
|
|
381
|
+
color: theme.text,
|
|
295
382
|
textAlign: "left",
|
|
296
383
|
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
297
384
|
};
|
|
@@ -368,7 +455,7 @@ function ChatHeader() {
|
|
|
368
455
|
reset();
|
|
369
456
|
},
|
|
370
457
|
onMouseEnter: (e) => {
|
|
371
|
-
e.currentTarget.style.background =
|
|
458
|
+
e.currentTarget.style.background = theme.bubbleBackground;
|
|
372
459
|
},
|
|
373
460
|
onMouseLeave: (e) => {
|
|
374
461
|
e.currentTarget.style.background = "none";
|
|
@@ -430,7 +517,7 @@ function ChatHeader() {
|
|
|
430
517
|
}
|
|
431
518
|
|
|
432
519
|
// src/components/chat-messages.tsx
|
|
433
|
-
var
|
|
520
|
+
var import_react8 = require("react");
|
|
434
521
|
|
|
435
522
|
// src/markdown/render.tsx
|
|
436
523
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
@@ -797,7 +884,7 @@ function renderMarkdown(source, opts = {}) {
|
|
|
797
884
|
}
|
|
798
885
|
|
|
799
886
|
// src/components/action-confirmation-card.tsx
|
|
800
|
-
var
|
|
887
|
+
var import_react7 = require("react");
|
|
801
888
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
802
889
|
function ActionConfirmationCard({
|
|
803
890
|
block,
|
|
@@ -806,10 +893,10 @@ function ActionConfirmationCard({
|
|
|
806
893
|
onApprove,
|
|
807
894
|
onCancel
|
|
808
895
|
}) {
|
|
809
|
-
const [chosen, setChosen] = (0,
|
|
810
|
-
const [showSpinner, setShowSpinner] = (0,
|
|
811
|
-
const [error, setError] = (0,
|
|
812
|
-
const [showSummary, setShowSummary] = (0,
|
|
896
|
+
const [chosen, setChosen] = (0, import_react7.useState)(null);
|
|
897
|
+
const [showSpinner, setShowSpinner] = (0, import_react7.useState)(false);
|
|
898
|
+
const [error, setError] = (0, import_react7.useState)(null);
|
|
899
|
+
const [showSummary, setShowSummary] = (0, import_react7.useState)(false);
|
|
813
900
|
const handle = async (choice) => {
|
|
814
901
|
if (chosen) return;
|
|
815
902
|
setChosen(choice);
|
|
@@ -1037,7 +1124,7 @@ function MessageRatingButtons({
|
|
|
1037
1124
|
t,
|
|
1038
1125
|
reduced
|
|
1039
1126
|
}) {
|
|
1040
|
-
const [rated, setRated] = (0,
|
|
1127
|
+
const [rated, setRated] = (0, import_react8.useState)(null);
|
|
1041
1128
|
const handleRate = (rating) => {
|
|
1042
1129
|
setRated(rating);
|
|
1043
1130
|
onRate(messageId, rating);
|
|
@@ -1118,8 +1205,8 @@ function AnimatedMessage({
|
|
|
1118
1205
|
animate,
|
|
1119
1206
|
reduced
|
|
1120
1207
|
}) {
|
|
1121
|
-
const [visible, setVisible] = (0,
|
|
1122
|
-
(0,
|
|
1208
|
+
const [visible, setVisible] = (0, import_react8.useState)(!animate);
|
|
1209
|
+
(0, import_react8.useEffect)(() => {
|
|
1123
1210
|
if (!animate || reduced) {
|
|
1124
1211
|
setVisible(true);
|
|
1125
1212
|
return;
|
|
@@ -1239,6 +1326,201 @@ function StreamingCursor({ reduced }) {
|
|
|
1239
1326
|
)
|
|
1240
1327
|
] });
|
|
1241
1328
|
}
|
|
1329
|
+
function formatBytes(n) {
|
|
1330
|
+
if (n == null || n <= 0) return null;
|
|
1331
|
+
if (n < 1024) return `${n} B`;
|
|
1332
|
+
if (n < 1024 * 1024) return `${Math.round(n / 1024)} KB`;
|
|
1333
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
1334
|
+
}
|
|
1335
|
+
function AttachmentList({
|
|
1336
|
+
attachments,
|
|
1337
|
+
isUser,
|
|
1338
|
+
primaryColor,
|
|
1339
|
+
t
|
|
1340
|
+
}) {
|
|
1341
|
+
const containerStyle = {
|
|
1342
|
+
display: "flex",
|
|
1343
|
+
flexDirection: "column",
|
|
1344
|
+
gap: 6,
|
|
1345
|
+
marginTop: 6,
|
|
1346
|
+
alignItems: isUser ? "flex-end" : "flex-start"
|
|
1347
|
+
};
|
|
1348
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: containerStyle, children: attachments.map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1349
|
+
AttachmentTile,
|
|
1350
|
+
{
|
|
1351
|
+
attachment: a,
|
|
1352
|
+
isUser,
|
|
1353
|
+
primaryColor,
|
|
1354
|
+
t
|
|
1355
|
+
},
|
|
1356
|
+
a.id
|
|
1357
|
+
)) });
|
|
1358
|
+
}
|
|
1359
|
+
function AttachmentTile({
|
|
1360
|
+
attachment,
|
|
1361
|
+
isUser,
|
|
1362
|
+
primaryColor,
|
|
1363
|
+
t
|
|
1364
|
+
}) {
|
|
1365
|
+
const tileBg = isUser ? `${primaryColor}11` : "#f3f4f6";
|
|
1366
|
+
const tileBorder = isUser ? `${primaryColor}33` : "#e5e7eb";
|
|
1367
|
+
if (attachment.kind === "image" && attachment.url) {
|
|
1368
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1369
|
+
"a",
|
|
1370
|
+
{
|
|
1371
|
+
href: attachment.url,
|
|
1372
|
+
target: "_blank",
|
|
1373
|
+
rel: "noopener noreferrer",
|
|
1374
|
+
style: { display: "inline-block", maxWidth: 220, lineHeight: 0 },
|
|
1375
|
+
"aria-label": attachment.filename ?? t("attachment_image_alt"),
|
|
1376
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1377
|
+
"img",
|
|
1378
|
+
{
|
|
1379
|
+
src: attachment.url,
|
|
1380
|
+
alt: attachment.filename ?? t("attachment_image_alt"),
|
|
1381
|
+
style: {
|
|
1382
|
+
display: "block",
|
|
1383
|
+
maxWidth: 220,
|
|
1384
|
+
maxHeight: 220,
|
|
1385
|
+
width: "auto",
|
|
1386
|
+
height: "auto",
|
|
1387
|
+
borderRadius: 12,
|
|
1388
|
+
border: `1px solid ${tileBorder}`,
|
|
1389
|
+
objectFit: "cover"
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
)
|
|
1393
|
+
}
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
if (attachment.kind === "location" && attachment.location) {
|
|
1397
|
+
const { latitude, longitude, label } = attachment.location;
|
|
1398
|
+
const href = `https://www.openstreetmap.org/?mlat=${latitude}&mlon=${longitude}#map=15/${latitude}/${longitude}`;
|
|
1399
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1400
|
+
"a",
|
|
1401
|
+
{
|
|
1402
|
+
href,
|
|
1403
|
+
target: "_blank",
|
|
1404
|
+
rel: "noopener noreferrer",
|
|
1405
|
+
style: tileLinkStyle(tileBg, tileBorder),
|
|
1406
|
+
children: [
|
|
1407
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PinIcon, {}),
|
|
1408
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
|
|
1409
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: tileTitleStyle(), children: label || t("attachment_location") }),
|
|
1410
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: tileSubStyle(), children: [
|
|
1411
|
+
latitude.toFixed(4),
|
|
1412
|
+
", ",
|
|
1413
|
+
longitude.toFixed(4)
|
|
1414
|
+
] })
|
|
1415
|
+
] })
|
|
1416
|
+
]
|
|
1417
|
+
}
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
if (!attachment.url) return null;
|
|
1421
|
+
const sub = [attachment.mimeType, formatBytes(attachment.sizeBytes)].filter(Boolean).join(" \xB7 ");
|
|
1422
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1423
|
+
"a",
|
|
1424
|
+
{
|
|
1425
|
+
href: attachment.url,
|
|
1426
|
+
target: "_blank",
|
|
1427
|
+
rel: "noopener noreferrer",
|
|
1428
|
+
title: t("attachment_open"),
|
|
1429
|
+
style: tileLinkStyle(tileBg, tileBorder),
|
|
1430
|
+
children: [
|
|
1431
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FileIcon, { kind: attachment.kind }),
|
|
1432
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
|
|
1433
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: tileTitleStyle(), children: attachment.filename ?? t("attachment_open") }),
|
|
1434
|
+
sub && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: tileSubStyle(), children: sub })
|
|
1435
|
+
] })
|
|
1436
|
+
]
|
|
1437
|
+
}
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
function tileLinkStyle(bg, border) {
|
|
1441
|
+
return {
|
|
1442
|
+
display: "flex",
|
|
1443
|
+
alignItems: "center",
|
|
1444
|
+
gap: 10,
|
|
1445
|
+
padding: "8px 12px",
|
|
1446
|
+
background: bg,
|
|
1447
|
+
border: `1px solid ${border}`,
|
|
1448
|
+
borderRadius: 12,
|
|
1449
|
+
textDecoration: "none",
|
|
1450
|
+
color: "inherit",
|
|
1451
|
+
maxWidth: 260
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
function tileTitleStyle() {
|
|
1455
|
+
return {
|
|
1456
|
+
fontSize: 13,
|
|
1457
|
+
fontWeight: 500,
|
|
1458
|
+
color: "#1f2937",
|
|
1459
|
+
overflow: "hidden",
|
|
1460
|
+
textOverflow: "ellipsis",
|
|
1461
|
+
whiteSpace: "nowrap",
|
|
1462
|
+
maxWidth: 200
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
function tileSubStyle() {
|
|
1466
|
+
return {
|
|
1467
|
+
fontSize: 11,
|
|
1468
|
+
color: "#6b7280",
|
|
1469
|
+
overflow: "hidden",
|
|
1470
|
+
textOverflow: "ellipsis",
|
|
1471
|
+
whiteSpace: "nowrap",
|
|
1472
|
+
maxWidth: 200
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
function FileIcon({ kind }) {
|
|
1476
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1477
|
+
"svg",
|
|
1478
|
+
{
|
|
1479
|
+
width: "20",
|
|
1480
|
+
height: "20",
|
|
1481
|
+
viewBox: "0 0 24 24",
|
|
1482
|
+
fill: "none",
|
|
1483
|
+
stroke: "#4b5563",
|
|
1484
|
+
strokeWidth: "2",
|
|
1485
|
+
strokeLinecap: "round",
|
|
1486
|
+
strokeLinejoin: "round",
|
|
1487
|
+
"aria-hidden": "true",
|
|
1488
|
+
style: { flexShrink: 0 },
|
|
1489
|
+
children: kind === "audio" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1490
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M9 18V5l12-2v13" }),
|
|
1491
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "6", cy: "18", r: "3" }),
|
|
1492
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "18", cy: "16", r: "3" })
|
|
1493
|
+
] }) : kind === "video" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1494
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: "2", y: "6", width: "14", height: "12", rx: "2" }),
|
|
1495
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m22 8-6 4 6 4z" })
|
|
1496
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1497
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
1498
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "14 2 14 8 20 8" })
|
|
1499
|
+
] })
|
|
1500
|
+
}
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
function PinIcon() {
|
|
1504
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1505
|
+
"svg",
|
|
1506
|
+
{
|
|
1507
|
+
width: "20",
|
|
1508
|
+
height: "20",
|
|
1509
|
+
viewBox: "0 0 24 24",
|
|
1510
|
+
fill: "none",
|
|
1511
|
+
stroke: "#4b5563",
|
|
1512
|
+
strokeWidth: "2",
|
|
1513
|
+
strokeLinecap: "round",
|
|
1514
|
+
strokeLinejoin: "round",
|
|
1515
|
+
"aria-hidden": "true",
|
|
1516
|
+
style: { flexShrink: 0 },
|
|
1517
|
+
children: [
|
|
1518
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M20 10c0 7-8 13-8 13s-8-6-8-13a8 8 0 0 1 16 0Z" }),
|
|
1519
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "12", cy: "10", r: "3" })
|
|
1520
|
+
]
|
|
1521
|
+
}
|
|
1522
|
+
);
|
|
1523
|
+
}
|
|
1242
1524
|
function Message({
|
|
1243
1525
|
message,
|
|
1244
1526
|
config,
|
|
@@ -1264,14 +1546,16 @@ function Message({
|
|
|
1264
1546
|
color: "white",
|
|
1265
1547
|
borderBottomRightRadius: 4
|
|
1266
1548
|
} : {
|
|
1267
|
-
background:
|
|
1549
|
+
background: config.bubbleBackground,
|
|
1268
1550
|
color: config.textColor,
|
|
1269
1551
|
borderBottomLeftRadius: 4
|
|
1270
1552
|
}
|
|
1271
1553
|
};
|
|
1272
1554
|
const linkColor = isUser ? "#ffffff" : config.primaryColor;
|
|
1555
|
+
const hasContent = message.content.trim().length > 0;
|
|
1556
|
+
const hasAttachments = !!message.attachments?.length;
|
|
1273
1557
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(AnimatedMessage, { isUser, animate, reduced, children: [
|
|
1274
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1558
|
+
hasContent && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1275
1559
|
"div",
|
|
1276
1560
|
{
|
|
1277
1561
|
style: bubbleStyle,
|
|
@@ -1285,6 +1569,15 @@ function Message({
|
|
|
1285
1569
|
] })
|
|
1286
1570
|
}
|
|
1287
1571
|
),
|
|
1572
|
+
hasAttachments && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1573
|
+
AttachmentList,
|
|
1574
|
+
{
|
|
1575
|
+
attachments: message.attachments,
|
|
1576
|
+
isUser,
|
|
1577
|
+
primaryColor: config.primaryColor,
|
|
1578
|
+
t
|
|
1579
|
+
}
|
|
1580
|
+
),
|
|
1288
1581
|
isUser && message.status && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageStatusPill, { status: message.status, t }),
|
|
1289
1582
|
!isUser && message.blocks?.map((block, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1290
1583
|
BlockRenderer,
|
|
@@ -1360,7 +1653,6 @@ function ChatMessages() {
|
|
|
1360
1653
|
messages,
|
|
1361
1654
|
isLoading,
|
|
1362
1655
|
error,
|
|
1363
|
-
config,
|
|
1364
1656
|
conversationId,
|
|
1365
1657
|
rateMessage,
|
|
1366
1658
|
sendMessage,
|
|
@@ -1368,13 +1660,19 @@ function ChatMessages() {
|
|
|
1368
1660
|
cancelAction,
|
|
1369
1661
|
t
|
|
1370
1662
|
} = useChat();
|
|
1663
|
+
const theme = useEffectiveTheme();
|
|
1664
|
+
const themedConfig = {
|
|
1665
|
+
primaryColor: theme.primary,
|
|
1666
|
+
textColor: theme.text,
|
|
1667
|
+
bubbleBackground: theme.bubbleBackground
|
|
1668
|
+
};
|
|
1371
1669
|
const reduced = useReducedMotion();
|
|
1372
|
-
const containerRef = (0,
|
|
1373
|
-
const messagesEndRef = (0,
|
|
1374
|
-
const isFirstRender = (0,
|
|
1375
|
-
const prevMessageCount = (0,
|
|
1376
|
-
const stickRef = (0,
|
|
1377
|
-
const suppressScrollRef = (0,
|
|
1670
|
+
const containerRef = (0, import_react8.useRef)(null);
|
|
1671
|
+
const messagesEndRef = (0, import_react8.useRef)(null);
|
|
1672
|
+
const isFirstRender = (0, import_react8.useRef)(true);
|
|
1673
|
+
const prevMessageCount = (0, import_react8.useRef)(0);
|
|
1674
|
+
const stickRef = (0, import_react8.useRef)(true);
|
|
1675
|
+
const suppressScrollRef = (0, import_react8.useRef)(false);
|
|
1378
1676
|
const autoScrollTo = (top) => {
|
|
1379
1677
|
const el = containerRef.current;
|
|
1380
1678
|
if (!el) return;
|
|
@@ -1391,7 +1689,7 @@ function ChatMessages() {
|
|
|
1391
1689
|
const distFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
1392
1690
|
stickRef.current = distFromBottom < 60;
|
|
1393
1691
|
};
|
|
1394
|
-
(0,
|
|
1692
|
+
(0, import_react8.useEffect)(() => {
|
|
1395
1693
|
const el = containerRef.current;
|
|
1396
1694
|
if (!el) return;
|
|
1397
1695
|
const lastMsg2 = messages[messages.length - 1];
|
|
@@ -1416,7 +1714,7 @@ function ChatMessages() {
|
|
|
1416
1714
|
isFirstRender.current = false;
|
|
1417
1715
|
}, [messages, isLoading, reduced]);
|
|
1418
1716
|
const newStartIndex = isFirstRender.current ? messages.length : prevMessageCount.current;
|
|
1419
|
-
(0,
|
|
1717
|
+
(0, import_react8.useEffect)(() => {
|
|
1420
1718
|
prevMessageCount.current = messages.length;
|
|
1421
1719
|
}, [messages.length]);
|
|
1422
1720
|
let lastBotIndex = -1;
|
|
@@ -1441,7 +1739,7 @@ function ChatMessages() {
|
|
|
1441
1739
|
Message,
|
|
1442
1740
|
{
|
|
1443
1741
|
message: msg,
|
|
1444
|
-
config,
|
|
1742
|
+
config: themedConfig,
|
|
1445
1743
|
onRate: rateMessage,
|
|
1446
1744
|
onSend: sendMessage,
|
|
1447
1745
|
onApproveAction: approveAction,
|
|
@@ -1480,10 +1778,13 @@ var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
|
1480
1778
|
function ChatSuggestions() {
|
|
1481
1779
|
const { messages, isLoading, config, sendMessage } = useChat();
|
|
1482
1780
|
const reduced = useReducedMotion();
|
|
1781
|
+
const theme = useEffectiveTheme();
|
|
1483
1782
|
const hasUserMessage = messages.some((m) => m.role === "user");
|
|
1484
1783
|
if (config.suggestedMessages.length === 0 || hasUserMessage || isLoading) {
|
|
1485
1784
|
return null;
|
|
1486
1785
|
}
|
|
1786
|
+
const isDark = theme.scheme === "dark";
|
|
1787
|
+
const idleBorder = isDark ? "rgba(255,255,255,0.18)" : "#e0e0e0";
|
|
1487
1788
|
const containerStyle = {
|
|
1488
1789
|
padding: "8px 16px",
|
|
1489
1790
|
display: "flex",
|
|
@@ -1493,11 +1794,11 @@ function ChatSuggestions() {
|
|
|
1493
1794
|
};
|
|
1494
1795
|
const chipStyle = {
|
|
1495
1796
|
background: "none",
|
|
1496
|
-
border:
|
|
1797
|
+
border: `1px solid ${idleBorder}`,
|
|
1497
1798
|
borderRadius: 20,
|
|
1498
1799
|
padding: "7px 14px",
|
|
1499
1800
|
fontSize: 13,
|
|
1500
|
-
color:
|
|
1801
|
+
color: theme.text,
|
|
1501
1802
|
cursor: "pointer",
|
|
1502
1803
|
textAlign: "left",
|
|
1503
1804
|
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
@@ -1509,11 +1810,11 @@ function ChatSuggestions() {
|
|
|
1509
1810
|
style: chipStyle,
|
|
1510
1811
|
onClick: () => sendMessage(text),
|
|
1511
1812
|
onMouseEnter: (e) => {
|
|
1512
|
-
e.currentTarget.style.borderColor =
|
|
1513
|
-
e.currentTarget.style.background = `${
|
|
1813
|
+
e.currentTarget.style.borderColor = theme.primary;
|
|
1814
|
+
e.currentTarget.style.background = `${theme.primary}14`;
|
|
1514
1815
|
},
|
|
1515
1816
|
onMouseLeave: (e) => {
|
|
1516
|
-
e.currentTarget.style.borderColor =
|
|
1817
|
+
e.currentTarget.style.borderColor = idleBorder;
|
|
1517
1818
|
e.currentTarget.style.background = "none";
|
|
1518
1819
|
},
|
|
1519
1820
|
children: text
|
|
@@ -1523,8 +1824,8 @@ function ChatSuggestions() {
|
|
|
1523
1824
|
}
|
|
1524
1825
|
|
|
1525
1826
|
// src/components/chat-input.tsx
|
|
1526
|
-
var
|
|
1527
|
-
var
|
|
1827
|
+
var import_react9 = require("react");
|
|
1828
|
+
var import_js3 = require("@customerhero/js");
|
|
1528
1829
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1529
1830
|
var MAX_ATTACHMENTS = 3;
|
|
1530
1831
|
var ALLOWED_MIME_TYPES = [
|
|
@@ -1542,47 +1843,50 @@ function ChatInput() {
|
|
|
1542
1843
|
sendMessage,
|
|
1543
1844
|
uploadAttachment,
|
|
1544
1845
|
isLoading,
|
|
1846
|
+
readOnly,
|
|
1545
1847
|
config,
|
|
1546
1848
|
t,
|
|
1547
1849
|
consumePendingPrefill,
|
|
1548
1850
|
pendingPrefill
|
|
1549
1851
|
} = useChat();
|
|
1852
|
+
const theme = useEffectiveTheme();
|
|
1853
|
+
const isDark = theme.scheme === "dark";
|
|
1550
1854
|
const reduced = useReducedMotion();
|
|
1551
|
-
const [value, setValue] = (0,
|
|
1552
|
-
const [attachments, setAttachments] = (0,
|
|
1553
|
-
const [captureSupported, setCaptureSupported] = (0,
|
|
1554
|
-
const [menuOpen, setMenuOpen] = (0,
|
|
1555
|
-
const [dragActive, setDragActive] = (0,
|
|
1556
|
-
const [transientError, setTransientError] = (0,
|
|
1557
|
-
const fileInputRef = (0,
|
|
1558
|
-
const textInputRef = (0,
|
|
1559
|
-
const menuRef = (0,
|
|
1560
|
-
const menuButtonRef = (0,
|
|
1561
|
-
const dragCounterRef = (0,
|
|
1562
|
-
(0,
|
|
1563
|
-
setCaptureSupported((0,
|
|
1855
|
+
const [value, setValue] = (0, import_react9.useState)("");
|
|
1856
|
+
const [attachments, setAttachments] = (0, import_react9.useState)([]);
|
|
1857
|
+
const [captureSupported, setCaptureSupported] = (0, import_react9.useState)(false);
|
|
1858
|
+
const [menuOpen, setMenuOpen] = (0, import_react9.useState)(false);
|
|
1859
|
+
const [dragActive, setDragActive] = (0, import_react9.useState)(false);
|
|
1860
|
+
const [transientError, setTransientError] = (0, import_react9.useState)(null);
|
|
1861
|
+
const fileInputRef = (0, import_react9.useRef)(null);
|
|
1862
|
+
const textInputRef = (0, import_react9.useRef)(null);
|
|
1863
|
+
const menuRef = (0, import_react9.useRef)(null);
|
|
1864
|
+
const menuButtonRef = (0, import_react9.useRef)(null);
|
|
1865
|
+
const dragCounterRef = (0, import_react9.useRef)(0);
|
|
1866
|
+
(0, import_react9.useEffect)(() => {
|
|
1867
|
+
setCaptureSupported((0, import_js3.canCaptureScreenshot)());
|
|
1564
1868
|
}, []);
|
|
1565
|
-
(0,
|
|
1869
|
+
(0, import_react9.useEffect)(() => {
|
|
1566
1870
|
const id = requestAnimationFrame(() => textInputRef.current?.focus());
|
|
1567
1871
|
return () => cancelAnimationFrame(id);
|
|
1568
1872
|
}, []);
|
|
1569
|
-
(0,
|
|
1873
|
+
(0, import_react9.useLayoutEffect)(() => {
|
|
1570
1874
|
const el = textInputRef.current;
|
|
1571
1875
|
if (!el) return;
|
|
1572
1876
|
el.style.height = "auto";
|
|
1573
1877
|
el.style.height = `${el.scrollHeight}px`;
|
|
1574
1878
|
}, [value]);
|
|
1575
|
-
(0,
|
|
1879
|
+
(0, import_react9.useEffect)(() => {
|
|
1576
1880
|
if (pendingPrefill === null) return;
|
|
1577
1881
|
const text = consumePendingPrefill();
|
|
1578
1882
|
if (text !== null) setValue(text);
|
|
1579
1883
|
}, [pendingPrefill, consumePendingPrefill]);
|
|
1580
|
-
(0,
|
|
1884
|
+
(0, import_react9.useEffect)(() => {
|
|
1581
1885
|
return () => {
|
|
1582
1886
|
for (const a of attachments) URL.revokeObjectURL(a.previewUrl);
|
|
1583
1887
|
};
|
|
1584
1888
|
}, []);
|
|
1585
|
-
(0,
|
|
1889
|
+
(0, import_react9.useEffect)(() => {
|
|
1586
1890
|
if (!menuOpen) return;
|
|
1587
1891
|
const onClick = (e) => {
|
|
1588
1892
|
const target = e.target;
|
|
@@ -1601,7 +1905,7 @@ function ChatInput() {
|
|
|
1601
1905
|
document.removeEventListener("keydown", onKey);
|
|
1602
1906
|
};
|
|
1603
1907
|
}, [menuOpen]);
|
|
1604
|
-
(0,
|
|
1908
|
+
(0, import_react9.useEffect)(() => {
|
|
1605
1909
|
if (!transientError) return;
|
|
1606
1910
|
const id = window.setTimeout(() => setTransientError(null), 4e3);
|
|
1607
1911
|
return () => window.clearTimeout(id);
|
|
@@ -1657,10 +1961,10 @@ function ChatInput() {
|
|
|
1657
1961
|
const handleCapture = async () => {
|
|
1658
1962
|
setMenuOpen(false);
|
|
1659
1963
|
try {
|
|
1660
|
-
const blob = await (0,
|
|
1964
|
+
const blob = await (0, import_js3.captureScreenshot)();
|
|
1661
1965
|
await startUpload(blob);
|
|
1662
1966
|
} catch (e) {
|
|
1663
|
-
if (e instanceof
|
|
1967
|
+
if (e instanceof import_js3.ScreenshotCancelled) return;
|
|
1664
1968
|
}
|
|
1665
1969
|
};
|
|
1666
1970
|
const handlePickFile = () => {
|
|
@@ -1742,7 +2046,7 @@ function ChatInput() {
|
|
|
1742
2046
|
const containerStyle = {
|
|
1743
2047
|
position: "relative",
|
|
1744
2048
|
padding: "12px 16px",
|
|
1745
|
-
borderTop:
|
|
2049
|
+
borderTop: `1px solid ${theme.divider}`,
|
|
1746
2050
|
display: "flex",
|
|
1747
2051
|
flexDirection: "column",
|
|
1748
2052
|
gap: 8
|
|
@@ -1757,14 +2061,14 @@ function ChatInput() {
|
|
|
1757
2061
|
const TEXTAREA_MAX_HEIGHT = 140;
|
|
1758
2062
|
const inputStyle = {
|
|
1759
2063
|
flex: 1,
|
|
1760
|
-
border:
|
|
2064
|
+
border: `1px solid ${theme.divider}`,
|
|
1761
2065
|
borderRadius: 18,
|
|
1762
2066
|
padding: "10px 16px",
|
|
1763
2067
|
fontSize: 14,
|
|
1764
2068
|
lineHeight: 1.4,
|
|
1765
2069
|
outline: "none",
|
|
1766
|
-
background: "#fafafa",
|
|
1767
|
-
color:
|
|
2070
|
+
background: isDark ? "rgba(255,255,255,0.06)" : "#fafafa",
|
|
2071
|
+
color: theme.text,
|
|
1768
2072
|
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
1769
2073
|
resize: "none",
|
|
1770
2074
|
overflowY: "auto",
|
|
@@ -1775,7 +2079,7 @@ function ChatInput() {
|
|
|
1775
2079
|
width: 36,
|
|
1776
2080
|
height: 36,
|
|
1777
2081
|
borderRadius: "50%",
|
|
1778
|
-
background:
|
|
2082
|
+
background: theme.primary,
|
|
1779
2083
|
border: "none",
|
|
1780
2084
|
color: "white",
|
|
1781
2085
|
cursor: isLoading ? "not-allowed" : "pointer",
|
|
@@ -1805,10 +2109,10 @@ function ChatInput() {
|
|
|
1805
2109
|
position: "absolute",
|
|
1806
2110
|
bottom: "calc(100% + 4px)",
|
|
1807
2111
|
left: 0,
|
|
1808
|
-
background:
|
|
1809
|
-
border:
|
|
2112
|
+
background: theme.background,
|
|
2113
|
+
border: `1px solid ${theme.divider}`,
|
|
1810
2114
|
borderRadius: 8,
|
|
1811
|
-
boxShadow: "0 4px 16px rgba(0,0,0,0.12)",
|
|
2115
|
+
boxShadow: isDark ? "0 4px 16px rgba(0,0,0,0.5)" : "0 4px 16px rgba(0,0,0,0.12)",
|
|
1812
2116
|
padding: 4,
|
|
1813
2117
|
minWidth: 180,
|
|
1814
2118
|
zIndex: 10,
|
|
@@ -1825,7 +2129,7 @@ function ChatInput() {
|
|
|
1825
2129
|
borderRadius: 4,
|
|
1826
2130
|
cursor: "pointer",
|
|
1827
2131
|
fontSize: 14,
|
|
1828
|
-
color:
|
|
2132
|
+
color: theme.text,
|
|
1829
2133
|
textAlign: "left",
|
|
1830
2134
|
whiteSpace: "nowrap",
|
|
1831
2135
|
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
@@ -1833,13 +2137,13 @@ function ChatInput() {
|
|
|
1833
2137
|
const dropOverlayStyle = {
|
|
1834
2138
|
position: "absolute",
|
|
1835
2139
|
inset: 0,
|
|
1836
|
-
background: "rgba(255,255,255,0.92)",
|
|
1837
|
-
border: `2px dashed ${
|
|
2140
|
+
background: isDark ? "rgba(15,23,42,0.92)" : "rgba(255,255,255,0.92)",
|
|
2141
|
+
border: `2px dashed ${theme.primary}`,
|
|
1838
2142
|
borderRadius: 4,
|
|
1839
2143
|
display: "flex",
|
|
1840
2144
|
alignItems: "center",
|
|
1841
2145
|
justifyContent: "center",
|
|
1842
|
-
color:
|
|
2146
|
+
color: theme.primary,
|
|
1843
2147
|
fontSize: 14,
|
|
1844
2148
|
fontWeight: 500,
|
|
1845
2149
|
pointerEvents: "none",
|
|
@@ -1854,7 +2158,7 @@ function ChatInput() {
|
|
|
1854
2158
|
padding: "4px 10px",
|
|
1855
2159
|
fontSize: 12
|
|
1856
2160
|
};
|
|
1857
|
-
const attachDisabled = attachments.length >= MAX_ATTACHMENTS || isLoading;
|
|
2161
|
+
const attachDisabled = attachments.length >= MAX_ATTACHMENTS || isLoading || readOnly;
|
|
1858
2162
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1859
2163
|
"div",
|
|
1860
2164
|
{
|
|
@@ -1883,7 +2187,7 @@ function ChatInput() {
|
|
|
1883
2187
|
}
|
|
1884
2188
|
),
|
|
1885
2189
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: rowStyle, children: [
|
|
1886
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { position: "relative" }, children: [
|
|
2190
|
+
config.allowAttachments !== false && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { position: "relative" }, children: [
|
|
1887
2191
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1888
2192
|
"button",
|
|
1889
2193
|
{
|
|
@@ -1908,7 +2212,7 @@ function ChatInput() {
|
|
|
1908
2212
|
onClick: handlePickFile,
|
|
1909
2213
|
style: menuItemStyle,
|
|
1910
2214
|
onMouseEnter: (e) => {
|
|
1911
|
-
e.currentTarget.style.background =
|
|
2215
|
+
e.currentTarget.style.background = theme.bubbleBackground;
|
|
1912
2216
|
},
|
|
1913
2217
|
onMouseLeave: (e) => {
|
|
1914
2218
|
e.currentTarget.style.background = "transparent";
|
|
@@ -1927,7 +2231,7 @@ function ChatInput() {
|
|
|
1927
2231
|
onClick: handleCapture,
|
|
1928
2232
|
style: menuItemStyle,
|
|
1929
2233
|
onMouseEnter: (e) => {
|
|
1930
|
-
e.currentTarget.style.background =
|
|
2234
|
+
e.currentTarget.style.background = theme.bubbleBackground;
|
|
1931
2235
|
},
|
|
1932
2236
|
onMouseLeave: (e) => {
|
|
1933
2237
|
e.currentTarget.style.background = "transparent";
|
|
@@ -1964,14 +2268,14 @@ function ChatInput() {
|
|
|
1964
2268
|
onPaste: handlePaste,
|
|
1965
2269
|
placeholder: config.placeholderText,
|
|
1966
2270
|
style: inputStyle,
|
|
1967
|
-
disabled: isLoading
|
|
2271
|
+
disabled: isLoading || readOnly
|
|
1968
2272
|
}
|
|
1969
2273
|
),
|
|
1970
2274
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1971
2275
|
"button",
|
|
1972
2276
|
{
|
|
1973
2277
|
onClick: handleSend,
|
|
1974
|
-
disabled: isLoading || !value.trim(),
|
|
2278
|
+
disabled: isLoading || readOnly || !value.trim(),
|
|
1975
2279
|
style: sendButtonStyle,
|
|
1976
2280
|
"aria-label": t("send_message"),
|
|
1977
2281
|
onMouseEnter: (e) => {
|
|
@@ -2079,7 +2383,7 @@ function Thumbnail({
|
|
|
2079
2383
|
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
2080
2384
|
};
|
|
2081
2385
|
const displayName = filename ?? (isImage ? "" : "Document");
|
|
2082
|
-
const sizeLabel =
|
|
2386
|
+
const sizeLabel = formatBytes2(blob.size);
|
|
2083
2387
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: wrap, children: [
|
|
2084
2388
|
isImage ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: previewUrl, alt: "", style: img }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
2085
2389
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DocIcon, {}),
|
|
@@ -2183,7 +2487,7 @@ function DocIcon() {
|
|
|
2183
2487
|
}
|
|
2184
2488
|
);
|
|
2185
2489
|
}
|
|
2186
|
-
function
|
|
2490
|
+
function formatBytes2(bytes) {
|
|
2187
2491
|
if (!Number.isFinite(bytes) || bytes <= 0) return "";
|
|
2188
2492
|
if (bytes < 1024) return `${bytes} B`;
|
|
2189
2493
|
if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
|
|
@@ -2231,7 +2535,7 @@ function Spinner2() {
|
|
|
2231
2535
|
}
|
|
2232
2536
|
|
|
2233
2537
|
// src/components/incident-banner.tsx
|
|
2234
|
-
var
|
|
2538
|
+
var import_react10 = require("react");
|
|
2235
2539
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2236
2540
|
var PALETTE = {
|
|
2237
2541
|
info: {
|
|
@@ -2296,8 +2600,8 @@ function SeverityIcon({
|
|
|
2296
2600
|
}
|
|
2297
2601
|
function IncidentBanner() {
|
|
2298
2602
|
const { incidentBanner, incidentBannerDismissed, dismissIncidentBanner, t } = useChat();
|
|
2299
|
-
const [linkHover, setLinkHover] = (0,
|
|
2300
|
-
const [closeHover, setCloseHover] = (0,
|
|
2603
|
+
const [linkHover, setLinkHover] = (0, import_react10.useState)(false);
|
|
2604
|
+
const [closeHover, setCloseHover] = (0, import_react10.useState)(false);
|
|
2301
2605
|
if (!incidentBanner || incidentBannerDismissed) return null;
|
|
2302
2606
|
const palette = PALETTE[incidentBanner.severity];
|
|
2303
2607
|
const wrap = {
|
|
@@ -2513,9 +2817,9 @@ function validateField(field, value) {
|
|
|
2513
2817
|
}
|
|
2514
2818
|
function PreChatFormView() {
|
|
2515
2819
|
const { preChatForm, submitPreChatForm, cancelPreChatForm, config, t } = useChat();
|
|
2516
|
-
const [values, setValues] = (0,
|
|
2517
|
-
const [errors, setErrors] = (0,
|
|
2518
|
-
const [submitting, setSubmitting] = (0,
|
|
2820
|
+
const [values, setValues] = (0, import_react11.useState)({});
|
|
2821
|
+
const [errors, setErrors] = (0, import_react11.useState)({});
|
|
2822
|
+
const [submitting, setSubmitting] = (0, import_react11.useState)(false);
|
|
2519
2823
|
if (!preChatForm) return null;
|
|
2520
2824
|
function setValue(key, value) {
|
|
2521
2825
|
setValues((prev) => ({ ...prev, [key]: value }));
|
|
@@ -2744,12 +3048,13 @@ function PreChatFormView() {
|
|
|
2744
3048
|
}
|
|
2745
3049
|
);
|
|
2746
3050
|
}
|
|
2747
|
-
function ChatWindow() {
|
|
3051
|
+
function ChatWindow({ embedded } = {}) {
|
|
2748
3052
|
const { isOpen, config, configError, t, isRtl, preChatFormVisible } = useChat();
|
|
2749
3053
|
const reduced = useReducedMotion();
|
|
2750
|
-
const
|
|
2751
|
-
const [
|
|
2752
|
-
(0,
|
|
3054
|
+
const theme = useEffectiveTheme();
|
|
3055
|
+
const [visible, setVisible] = (0, import_react11.useState)(false);
|
|
3056
|
+
const [shouldRender, setShouldRender] = (0, import_react11.useState)(false);
|
|
3057
|
+
(0, import_react11.useEffect)(() => {
|
|
2753
3058
|
if (isOpen) {
|
|
2754
3059
|
setShouldRender(true);
|
|
2755
3060
|
requestAnimationFrame(() => {
|
|
@@ -2767,34 +3072,44 @@ function ChatWindow() {
|
|
|
2767
3072
|
}, [isOpen, reduced]);
|
|
2768
3073
|
if (!shouldRender) return null;
|
|
2769
3074
|
const effectivePosition = isRtl ? config.position === "bottom-right" ? "bottom-left" : "bottom-right" : config.position;
|
|
3075
|
+
const colors = theme;
|
|
3076
|
+
const preset = theme.size;
|
|
3077
|
+
const radius = theme.radius;
|
|
3078
|
+
const panelBottom = config.offset.bottom + preset.bubble + 14;
|
|
2770
3079
|
const style = {
|
|
2771
|
-
position: "fixed",
|
|
2772
|
-
bottom:
|
|
2773
|
-
[effectivePosition === "bottom-left" ? "left" : "right"]:
|
|
2774
|
-
width:
|
|
2775
|
-
maxWidth: "calc(100vw - 40px)",
|
|
2776
|
-
height:
|
|
2777
|
-
maxHeight:
|
|
2778
|
-
borderRadius:
|
|
3080
|
+
position: embedded ? "absolute" : "fixed",
|
|
3081
|
+
bottom: panelBottom,
|
|
3082
|
+
[effectivePosition === "bottom-left" ? "left" : "right"]: config.offset.side,
|
|
3083
|
+
width: preset.width,
|
|
3084
|
+
maxWidth: embedded ? "calc(100% - 16px)" : "calc(100vw - 40px)",
|
|
3085
|
+
height: preset.height,
|
|
3086
|
+
maxHeight: embedded ? `calc(100% - ${panelBottom + 16}px)` : `calc(100vh - ${panelBottom + 30}px)`,
|
|
3087
|
+
borderRadius: radius,
|
|
2779
3088
|
overflow: "hidden",
|
|
2780
3089
|
display: "flex",
|
|
2781
3090
|
flexDirection: "column",
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
3091
|
+
// In dark mode the default 0.15 opacity black shadow is invisible
|
|
3092
|
+
// against a dark page; switch to a stronger shadow plus a subtle 1px
|
|
3093
|
+
// light outline so the panel still reads as a separated surface.
|
|
3094
|
+
boxShadow: theme.scheme === "dark" ? "0 12px 40px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.06)" : "0 8px 40px rgba(0,0,0,0.15)",
|
|
3095
|
+
zIndex: config.zIndex,
|
|
3096
|
+
background: colors.background,
|
|
3097
|
+
color: colors.text,
|
|
3098
|
+
fontSize: preset.fontSize,
|
|
2785
3099
|
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
2786
3100
|
opacity: visible ? 1 : 0,
|
|
2787
3101
|
transform: visible ? "translateY(0) scale(1)" : "translateY(16px) scale(0.97)",
|
|
2788
3102
|
transition: reduced ? "none" : "opacity 0.25s ease, transform 0.25s ease"
|
|
2789
3103
|
};
|
|
3104
|
+
const isDark = theme.scheme === "dark";
|
|
2790
3105
|
const poweredStyle = {
|
|
2791
3106
|
textAlign: "center",
|
|
2792
3107
|
padding: 6,
|
|
2793
3108
|
fontSize: 10,
|
|
2794
|
-
color: "#aaa"
|
|
3109
|
+
color: isDark ? "rgba(255,255,255,0.45)" : "#aaa"
|
|
2795
3110
|
};
|
|
2796
3111
|
const linkStyle = {
|
|
2797
|
-
color: "#888",
|
|
3112
|
+
color: isDark ? "rgba(255,255,255,0.6)" : "#888",
|
|
2798
3113
|
textDecoration: "underline",
|
|
2799
3114
|
textUnderlineOffset: 2
|
|
2800
3115
|
};
|
|
@@ -2825,11 +3140,14 @@ function ChatWindow() {
|
|
|
2825
3140
|
|
|
2826
3141
|
// src/components/chat-widget.tsx
|
|
2827
3142
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2828
|
-
function ChatWidgetInner({
|
|
3143
|
+
function ChatWidgetInner({
|
|
3144
|
+
identity,
|
|
3145
|
+
embedded
|
|
3146
|
+
}) {
|
|
2829
3147
|
const client = useCustomerHeroClient();
|
|
2830
3148
|
const { configLoaded, configError } = useChat();
|
|
2831
|
-
const prevIdentityRef = (0,
|
|
2832
|
-
(0,
|
|
3149
|
+
const prevIdentityRef = (0, import_react12.useRef)(void 0);
|
|
3150
|
+
(0, import_react12.useEffect)(() => {
|
|
2833
3151
|
const key = identity ? JSON.stringify(identity) : void 0;
|
|
2834
3152
|
if (key !== prevIdentityRef.current) {
|
|
2835
3153
|
prevIdentityRef.current = key;
|
|
@@ -2840,12 +3158,12 @@ function ChatWidgetInner({ identity }) {
|
|
|
2840
3158
|
}, [identity, client]);
|
|
2841
3159
|
if (!configLoaded || configError) return null;
|
|
2842
3160
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
2843
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChatBubble, {}),
|
|
2844
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChatWindow, {})
|
|
3161
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChatBubble, { embedded }),
|
|
3162
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChatWindow, { embedded })
|
|
2845
3163
|
] });
|
|
2846
3164
|
}
|
|
2847
|
-
function ChatWidget({ identity, ...config }) {
|
|
2848
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CustomerHeroProvider, { ...config, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChatWidgetInner, { identity }) });
|
|
3165
|
+
function ChatWidget({ identity, embedded, ...config }) {
|
|
3166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CustomerHeroProvider, { ...config, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChatWidgetInner, { identity, embedded }) });
|
|
2849
3167
|
}
|
|
2850
3168
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2851
3169
|
0 && (module.exports = {
|