@customerhero/react 0.0.2 → 1.0.1
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/index.cjs +1119 -121
- package/dist/index.d.cts +36 -3
- package/dist/index.d.ts +36 -3
- package/dist/index.js +1116 -113
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/chat-widget.tsx
|
|
2
|
-
import { useEffect as
|
|
2
|
+
import { useEffect as useEffect8, useRef as useRef4 } from "react";
|
|
3
3
|
|
|
4
4
|
// src/context.tsx
|
|
5
5
|
import {
|
|
@@ -52,13 +52,26 @@ function useChat() {
|
|
|
52
52
|
...state,
|
|
53
53
|
t: client.t,
|
|
54
54
|
sendMessage: useCallback(
|
|
55
|
-
(message) => client.sendMessage(message),
|
|
55
|
+
(message, options) => client.sendMessage(message, options),
|
|
56
|
+
[client]
|
|
57
|
+
),
|
|
58
|
+
uploadAttachment: useCallback(
|
|
59
|
+
(blob, options) => client.uploadAttachment(blob, options),
|
|
56
60
|
[client]
|
|
57
61
|
),
|
|
58
62
|
rateMessage: useCallback(
|
|
59
63
|
(messageId, rating) => client.rateMessage(messageId, rating),
|
|
60
64
|
[client]
|
|
61
65
|
),
|
|
66
|
+
approveAction: useCallback(
|
|
67
|
+
(pendingId) => client.approveAction(pendingId),
|
|
68
|
+
[client]
|
|
69
|
+
),
|
|
70
|
+
cancelAction: useCallback(
|
|
71
|
+
(pendingId) => client.cancelAction(pendingId),
|
|
72
|
+
[client]
|
|
73
|
+
),
|
|
74
|
+
setLocale: useCallback((tag) => client.setLocale(tag), [client]),
|
|
62
75
|
toggle: useCallback(() => client.toggle(), [client]),
|
|
63
76
|
open: useCallback(() => client.open(), [client]),
|
|
64
77
|
close: useCallback(() => client.close(), [client]),
|
|
@@ -89,7 +102,7 @@ function useReducedMotion() {
|
|
|
89
102
|
// src/components/chat-bubble.tsx
|
|
90
103
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
91
104
|
function ChatBubble() {
|
|
92
|
-
const { toggle, config, t } = useChat();
|
|
105
|
+
const { toggle, config, t, isRtl } = useChat();
|
|
93
106
|
const reduced = useReducedMotion();
|
|
94
107
|
const [mounted, setMounted] = useState2(false);
|
|
95
108
|
useEffect3(() => {
|
|
@@ -97,10 +110,11 @@ function ChatBubble() {
|
|
|
97
110
|
return () => cancelAnimationFrame(id);
|
|
98
111
|
}, []);
|
|
99
112
|
const visible = mounted;
|
|
113
|
+
const effectivePosition = isRtl ? config.position === "bottom-right" ? "bottom-left" : "bottom-right" : config.position;
|
|
100
114
|
const style = {
|
|
101
115
|
position: "fixed",
|
|
102
116
|
bottom: 20,
|
|
103
|
-
[
|
|
117
|
+
[effectivePosition === "bottom-left" ? "left" : "right"]: 20,
|
|
104
118
|
width: 56,
|
|
105
119
|
height: 56,
|
|
106
120
|
borderRadius: "50%",
|
|
@@ -124,6 +138,7 @@ function ChatBubble() {
|
|
|
124
138
|
{
|
|
125
139
|
onClick: toggle,
|
|
126
140
|
style,
|
|
141
|
+
dir: isRtl ? "rtl" : "ltr",
|
|
127
142
|
"aria-label": t("open_chat"),
|
|
128
143
|
onMouseEnter: (e) => {
|
|
129
144
|
if (!reduced) e.currentTarget.style.transform = "scale(1.1)";
|
|
@@ -150,7 +165,7 @@ function ChatBubble() {
|
|
|
150
165
|
}
|
|
151
166
|
|
|
152
167
|
// src/components/chat-window.tsx
|
|
153
|
-
import { useEffect as
|
|
168
|
+
import { useEffect as useEffect7, useState as useState7 } from "react";
|
|
154
169
|
|
|
155
170
|
// src/components/chat-header.tsx
|
|
156
171
|
import { useState as useState3, useEffect as useEffect4, useRef as useRef2 } from "react";
|
|
@@ -369,8 +384,602 @@ function ChatHeader() {
|
|
|
369
384
|
}
|
|
370
385
|
|
|
371
386
|
// src/components/chat-messages.tsx
|
|
372
|
-
import { useEffect as useEffect5, useRef as useRef3, useState as
|
|
387
|
+
import { useEffect as useEffect5, useRef as useRef3, useState as useState5 } from "react";
|
|
388
|
+
|
|
389
|
+
// src/markdown/render.tsx
|
|
373
390
|
import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
391
|
+
function parseBlocks(source) {
|
|
392
|
+
const lines = source.replace(/\r\n?/g, "\n").split("\n");
|
|
393
|
+
const blocks = [];
|
|
394
|
+
let i = 0;
|
|
395
|
+
while (i < lines.length) {
|
|
396
|
+
const line = lines[i];
|
|
397
|
+
if (line.trim() === "") {
|
|
398
|
+
i++;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
const fenceMatch = line.match(/^```(.*)$/);
|
|
402
|
+
if (fenceMatch) {
|
|
403
|
+
const lang = fenceMatch[1].trim() || void 0;
|
|
404
|
+
const body2 = [];
|
|
405
|
+
i++;
|
|
406
|
+
while (i < lines.length && !/^```\s*$/.test(lines[i])) {
|
|
407
|
+
body2.push(lines[i]);
|
|
408
|
+
i++;
|
|
409
|
+
}
|
|
410
|
+
if (i < lines.length) i++;
|
|
411
|
+
blocks.push({ kind: "code", lines: body2, lang });
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.*?)\s*#*\s*$/);
|
|
415
|
+
if (headingMatch) {
|
|
416
|
+
blocks.push({
|
|
417
|
+
kind: "heading",
|
|
418
|
+
level: headingMatch[1].length,
|
|
419
|
+
lines: [headingMatch[2]]
|
|
420
|
+
});
|
|
421
|
+
i++;
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
if (/^(\s*)[-*+]\s+/.test(line)) {
|
|
425
|
+
const body2 = [];
|
|
426
|
+
while (i < lines.length && /^(\s*)[-*+]\s+/.test(lines[i])) {
|
|
427
|
+
body2.push(lines[i].replace(/^(\s*)[-*+]\s+/, ""));
|
|
428
|
+
i++;
|
|
429
|
+
}
|
|
430
|
+
blocks.push({ kind: "ul", lines: body2 });
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (/^(\s*)\d+\.\s+/.test(line)) {
|
|
434
|
+
const body2 = [];
|
|
435
|
+
while (i < lines.length && /^(\s*)\d+\.\s+/.test(lines[i])) {
|
|
436
|
+
body2.push(lines[i].replace(/^(\s*)\d+\.\s+/, ""));
|
|
437
|
+
i++;
|
|
438
|
+
}
|
|
439
|
+
blocks.push({ kind: "ol", lines: body2 });
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (/^>\s?/.test(line)) {
|
|
443
|
+
const body2 = [];
|
|
444
|
+
while (i < lines.length && /^>\s?/.test(lines[i])) {
|
|
445
|
+
body2.push(lines[i].replace(/^>\s?/, ""));
|
|
446
|
+
i++;
|
|
447
|
+
}
|
|
448
|
+
blocks.push({ kind: "blockquote", lines: body2 });
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
const body = [line];
|
|
452
|
+
i++;
|
|
453
|
+
while (i < lines.length) {
|
|
454
|
+
const next = lines[i];
|
|
455
|
+
if (next.trim() === "") break;
|
|
456
|
+
if (/^```/.test(next) || /^#{1,6}\s/.test(next) || /^(\s*)[-*+]\s+/.test(next) || /^(\s*)\d+\.\s+/.test(next) || /^>\s?/.test(next)) {
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
body.push(next);
|
|
460
|
+
i++;
|
|
461
|
+
}
|
|
462
|
+
blocks.push({ kind: "paragraph", lines: body });
|
|
463
|
+
}
|
|
464
|
+
return blocks;
|
|
465
|
+
}
|
|
466
|
+
function findClosing(text, from, end) {
|
|
467
|
+
let i = from;
|
|
468
|
+
while (i <= text.length - end.length) {
|
|
469
|
+
const c = text[i];
|
|
470
|
+
if (c === "\\") {
|
|
471
|
+
i += 2;
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
if (text.startsWith(end, i)) return i;
|
|
475
|
+
i++;
|
|
476
|
+
}
|
|
477
|
+
return -1;
|
|
478
|
+
}
|
|
479
|
+
function renderInlineText(text, ctx) {
|
|
480
|
+
const out = [];
|
|
481
|
+
let buffer = "";
|
|
482
|
+
let i = 0;
|
|
483
|
+
const flushBuffer = () => {
|
|
484
|
+
if (buffer.length > 0) {
|
|
485
|
+
out.push(buffer);
|
|
486
|
+
buffer = "";
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
while (i < text.length) {
|
|
490
|
+
const c = text[i];
|
|
491
|
+
if (c === "\\" && i + 1 < text.length) {
|
|
492
|
+
buffer += text[i + 1];
|
|
493
|
+
i += 2;
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
if (c === " " && text[i + 1] === " " && text[i + 2] === "\n") {
|
|
497
|
+
flushBuffer();
|
|
498
|
+
out.push(/* @__PURE__ */ jsx4("br", {}, ctx.nextKey()));
|
|
499
|
+
i += 3;
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
if (c === "\n") {
|
|
503
|
+
buffer += " ";
|
|
504
|
+
i++;
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
if (c === "`") {
|
|
508
|
+
const close = findClosing(text, i + 1, "`");
|
|
509
|
+
if (close !== -1) {
|
|
510
|
+
flushBuffer();
|
|
511
|
+
out.push(
|
|
512
|
+
/* @__PURE__ */ jsx4(
|
|
513
|
+
"code",
|
|
514
|
+
{
|
|
515
|
+
style: {
|
|
516
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
517
|
+
fontSize: "0.92em",
|
|
518
|
+
background: "rgba(0,0,0,0.06)",
|
|
519
|
+
padding: "1px 4px",
|
|
520
|
+
borderRadius: 3
|
|
521
|
+
},
|
|
522
|
+
children: text.slice(i + 1, close)
|
|
523
|
+
},
|
|
524
|
+
ctx.nextKey()
|
|
525
|
+
)
|
|
526
|
+
);
|
|
527
|
+
i = close + 1;
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if ((text.startsWith("**", i) || text.startsWith("__", i)) && i + 2 < text.length) {
|
|
532
|
+
const marker = text.slice(i, i + 2);
|
|
533
|
+
const close = findClosing(text, i + 2, marker);
|
|
534
|
+
if (close !== -1 && close > i + 2) {
|
|
535
|
+
flushBuffer();
|
|
536
|
+
const inner = renderInlineText(text.slice(i + 2, close), ctx);
|
|
537
|
+
out.push(/* @__PURE__ */ jsx4("strong", { children: inner }, ctx.nextKey()));
|
|
538
|
+
i = close + 2;
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
if (c === "*" || c === "_") {
|
|
543
|
+
const prev = i === 0 ? " " : text[i - 1];
|
|
544
|
+
const isWordBoundaryBefore = c === "*" || !/\w/.test(prev);
|
|
545
|
+
if (isWordBoundaryBefore) {
|
|
546
|
+
const close = findClosing(text, i + 1, c);
|
|
547
|
+
const prevOfClose = close === -1 ? " " : close + 1 < text.length ? text[close + 1] : " ";
|
|
548
|
+
const isWordBoundaryAfter = c === "*" || !/\w/.test(prevOfClose);
|
|
549
|
+
if (close !== -1 && close > i + 1 && text[close + 1] !== c && isWordBoundaryAfter) {
|
|
550
|
+
flushBuffer();
|
|
551
|
+
const inner = renderInlineText(text.slice(i + 1, close), ctx);
|
|
552
|
+
out.push(/* @__PURE__ */ jsx4("em", { children: inner }, ctx.nextKey()));
|
|
553
|
+
i = close + 1;
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (c === "[") {
|
|
559
|
+
const closeLabel = findClosing(text, i + 1, "]");
|
|
560
|
+
if (closeLabel !== -1 && text[closeLabel + 1] === "(") {
|
|
561
|
+
const closeUrl = findClosing(text, closeLabel + 2, ")");
|
|
562
|
+
if (closeUrl !== -1) {
|
|
563
|
+
const label = text.slice(i + 1, closeLabel);
|
|
564
|
+
const url = text.slice(closeLabel + 2, closeUrl).trim();
|
|
565
|
+
if (isSafeUrl(url)) {
|
|
566
|
+
flushBuffer();
|
|
567
|
+
out.push(
|
|
568
|
+
/* @__PURE__ */ jsx4(
|
|
569
|
+
"a",
|
|
570
|
+
{
|
|
571
|
+
href: url,
|
|
572
|
+
target: "_blank",
|
|
573
|
+
rel: "noopener noreferrer",
|
|
574
|
+
style: { color: ctx.linkColor, textDecoration: "underline" },
|
|
575
|
+
children: renderInlineText(label, ctx)
|
|
576
|
+
},
|
|
577
|
+
ctx.nextKey()
|
|
578
|
+
)
|
|
579
|
+
);
|
|
580
|
+
i = closeUrl + 1;
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (closeLabel !== -1 && ctx.sources && ctx.sources.length > 0) {
|
|
586
|
+
const body = text.slice(i + 1, closeLabel).trim();
|
|
587
|
+
const parts = body.split(/\s*,\s*/).filter((s) => s.length > 0);
|
|
588
|
+
const indices = parts.map((p) => Number(p));
|
|
589
|
+
const allValid = parts.length > 0 && indices.every(
|
|
590
|
+
(n) => Number.isInteger(n) && n >= 1 && n <= (ctx.sources?.length ?? 0)
|
|
591
|
+
);
|
|
592
|
+
if (allValid) {
|
|
593
|
+
flushBuffer();
|
|
594
|
+
out.push(
|
|
595
|
+
/* @__PURE__ */ jsx4("sup", { style: { whiteSpace: "nowrap" }, children: indices.map((n, idx) => {
|
|
596
|
+
const src = ctx.sources[n - 1];
|
|
597
|
+
const content = /* @__PURE__ */ jsxs2(
|
|
598
|
+
"span",
|
|
599
|
+
{
|
|
600
|
+
style: {
|
|
601
|
+
fontSize: "0.75em",
|
|
602
|
+
color: ctx.linkColor,
|
|
603
|
+
cursor: src.url ? "pointer" : "default"
|
|
604
|
+
},
|
|
605
|
+
title: src.heading ? `${src.title} \u2014 ${src.heading}` : src.title,
|
|
606
|
+
children: [
|
|
607
|
+
"[",
|
|
608
|
+
n,
|
|
609
|
+
"]"
|
|
610
|
+
]
|
|
611
|
+
}
|
|
612
|
+
);
|
|
613
|
+
const separator = idx > 0 ? " " : null;
|
|
614
|
+
return /* @__PURE__ */ jsxs2("span", { children: [
|
|
615
|
+
separator,
|
|
616
|
+
src.url ? /* @__PURE__ */ jsx4(
|
|
617
|
+
"a",
|
|
618
|
+
{
|
|
619
|
+
href: src.url,
|
|
620
|
+
target: "_blank",
|
|
621
|
+
rel: "noopener noreferrer",
|
|
622
|
+
style: {
|
|
623
|
+
color: ctx.linkColor,
|
|
624
|
+
textDecoration: "none"
|
|
625
|
+
},
|
|
626
|
+
children: content
|
|
627
|
+
}
|
|
628
|
+
) : content
|
|
629
|
+
] }, `${ctx.keyRoot}-cit-${idx}`);
|
|
630
|
+
}) }, ctx.nextKey())
|
|
631
|
+
);
|
|
632
|
+
i = closeLabel + 1;
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
buffer += c;
|
|
638
|
+
i++;
|
|
639
|
+
}
|
|
640
|
+
flushBuffer();
|
|
641
|
+
return out;
|
|
642
|
+
}
|
|
643
|
+
function isSafeUrl(url) {
|
|
644
|
+
if (/^https?:\/\//i.test(url)) return true;
|
|
645
|
+
if (/^mailto:/i.test(url)) return true;
|
|
646
|
+
if (/^tel:/i.test(url)) return true;
|
|
647
|
+
if (/^\/[^/]/.test(url) || /^#/.test(url)) return true;
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
function renderMarkdown(source, opts = {}) {
|
|
651
|
+
const blocks = parseBlocks(source);
|
|
652
|
+
const linkColor = opts.linkColor ?? "#6C3CE1";
|
|
653
|
+
let keyCounter = 0;
|
|
654
|
+
const ctx = {
|
|
655
|
+
sources: opts.sources,
|
|
656
|
+
linkColor,
|
|
657
|
+
keyRoot: "md",
|
|
658
|
+
nextKey: () => `md-${keyCounter++}`
|
|
659
|
+
};
|
|
660
|
+
const renderInline = (line) => renderInlineText(line, ctx);
|
|
661
|
+
return /* @__PURE__ */ jsx4(Fragment, { children: blocks.map((block, idx) => {
|
|
662
|
+
const key = `b-${idx}`;
|
|
663
|
+
switch (block.kind) {
|
|
664
|
+
case "heading": {
|
|
665
|
+
const level = block.level ?? 1;
|
|
666
|
+
const style = {
|
|
667
|
+
margin: "8px 0 4px",
|
|
668
|
+
fontWeight: 600,
|
|
669
|
+
fontSize: level <= 2 ? "1.15em" : level === 3 ? "1.05em" : "1em"
|
|
670
|
+
};
|
|
671
|
+
const children = renderInline(block.lines[0] ?? "");
|
|
672
|
+
if (level === 1)
|
|
673
|
+
return /* @__PURE__ */ jsx4("h1", { style, children }, key);
|
|
674
|
+
if (level === 2)
|
|
675
|
+
return /* @__PURE__ */ jsx4("h2", { style, children }, key);
|
|
676
|
+
if (level === 3)
|
|
677
|
+
return /* @__PURE__ */ jsx4("h3", { style, children }, key);
|
|
678
|
+
if (level === 4)
|
|
679
|
+
return /* @__PURE__ */ jsx4("h4", { style, children }, key);
|
|
680
|
+
if (level === 5)
|
|
681
|
+
return /* @__PURE__ */ jsx4("h5", { style, children }, key);
|
|
682
|
+
return /* @__PURE__ */ jsx4("h6", { style, children }, key);
|
|
683
|
+
}
|
|
684
|
+
case "paragraph":
|
|
685
|
+
return /* @__PURE__ */ jsx4("p", { style: { margin: "0 0 8px", lineHeight: 1.5 }, children: renderInline(block.lines.join("\n")) }, key);
|
|
686
|
+
case "ul":
|
|
687
|
+
return /* @__PURE__ */ jsx4(
|
|
688
|
+
"ul",
|
|
689
|
+
{
|
|
690
|
+
style: {
|
|
691
|
+
margin: "0 0 8px",
|
|
692
|
+
paddingLeft: 20,
|
|
693
|
+
lineHeight: 1.5
|
|
694
|
+
},
|
|
695
|
+
children: block.lines.map((l, i) => /* @__PURE__ */ jsx4("li", { children: renderInline(l) }, i))
|
|
696
|
+
},
|
|
697
|
+
key
|
|
698
|
+
);
|
|
699
|
+
case "ol":
|
|
700
|
+
return /* @__PURE__ */ jsx4(
|
|
701
|
+
"ol",
|
|
702
|
+
{
|
|
703
|
+
style: {
|
|
704
|
+
margin: "0 0 8px",
|
|
705
|
+
paddingLeft: 22,
|
|
706
|
+
lineHeight: 1.5
|
|
707
|
+
},
|
|
708
|
+
children: block.lines.map((l, i) => /* @__PURE__ */ jsx4("li", { children: renderInline(l) }, i))
|
|
709
|
+
},
|
|
710
|
+
key
|
|
711
|
+
);
|
|
712
|
+
case "code":
|
|
713
|
+
return /* @__PURE__ */ jsx4(
|
|
714
|
+
"pre",
|
|
715
|
+
{
|
|
716
|
+
style: {
|
|
717
|
+
margin: "0 0 8px",
|
|
718
|
+
padding: "10px 12px",
|
|
719
|
+
background: "rgba(0,0,0,0.06)",
|
|
720
|
+
borderRadius: 6,
|
|
721
|
+
overflowX: "auto",
|
|
722
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
723
|
+
fontSize: "0.88em",
|
|
724
|
+
lineHeight: 1.45
|
|
725
|
+
},
|
|
726
|
+
children: /* @__PURE__ */ jsx4("code", { children: block.lines.join("\n") })
|
|
727
|
+
},
|
|
728
|
+
key
|
|
729
|
+
);
|
|
730
|
+
case "blockquote":
|
|
731
|
+
return /* @__PURE__ */ jsx4(
|
|
732
|
+
"blockquote",
|
|
733
|
+
{
|
|
734
|
+
style: {
|
|
735
|
+
margin: "0 0 8px",
|
|
736
|
+
padding: "4px 0 4px 10px",
|
|
737
|
+
borderLeft: "3px solid rgba(0,0,0,0.15)",
|
|
738
|
+
color: "rgba(0,0,0,0.75)",
|
|
739
|
+
fontStyle: "italic"
|
|
740
|
+
},
|
|
741
|
+
children: renderInline(block.lines.join("\n"))
|
|
742
|
+
},
|
|
743
|
+
key
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
}) });
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// src/components/action-confirmation-card.tsx
|
|
750
|
+
import { useState as useState4 } from "react";
|
|
751
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
752
|
+
function ActionConfirmationCard({
|
|
753
|
+
block,
|
|
754
|
+
primaryColor,
|
|
755
|
+
t,
|
|
756
|
+
onApprove,
|
|
757
|
+
onCancel
|
|
758
|
+
}) {
|
|
759
|
+
const [chosen, setChosen] = useState4(null);
|
|
760
|
+
const [showSpinner, setShowSpinner] = useState4(false);
|
|
761
|
+
const [error, setError] = useState4(null);
|
|
762
|
+
const [showSummary, setShowSummary] = useState4(false);
|
|
763
|
+
const handle = async (choice) => {
|
|
764
|
+
if (chosen) return;
|
|
765
|
+
setChosen(choice);
|
|
766
|
+
setError(null);
|
|
767
|
+
const spinnerTimer = window.setTimeout(() => setShowSpinner(true), 800);
|
|
768
|
+
try {
|
|
769
|
+
const fn = choice === "approve" ? onApprove : onCancel;
|
|
770
|
+
await fn(block.pendingToolCallId);
|
|
771
|
+
} catch (e) {
|
|
772
|
+
const msg = e instanceof Error ? e.message : t("action_failed");
|
|
773
|
+
setError(msg);
|
|
774
|
+
setChosen(null);
|
|
775
|
+
} finally {
|
|
776
|
+
window.clearTimeout(spinnerTimer);
|
|
777
|
+
setShowSpinner(false);
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
const cardStyle = {
|
|
781
|
+
marginTop: 6,
|
|
782
|
+
padding: 12,
|
|
783
|
+
borderRadius: 12,
|
|
784
|
+
border: "1px solid #e0e0e0",
|
|
785
|
+
background: "#fff",
|
|
786
|
+
display: "flex",
|
|
787
|
+
flexDirection: "column",
|
|
788
|
+
gap: 8,
|
|
789
|
+
fontSize: 13,
|
|
790
|
+
lineHeight: 1.4
|
|
791
|
+
};
|
|
792
|
+
const titleStyle = {
|
|
793
|
+
fontWeight: 600,
|
|
794
|
+
color: "#222"
|
|
795
|
+
};
|
|
796
|
+
const disclosureBtn = {
|
|
797
|
+
background: "none",
|
|
798
|
+
border: "none",
|
|
799
|
+
padding: 0,
|
|
800
|
+
color: primaryColor,
|
|
801
|
+
fontSize: 12,
|
|
802
|
+
cursor: "pointer",
|
|
803
|
+
textAlign: "left",
|
|
804
|
+
fontFamily: "inherit"
|
|
805
|
+
};
|
|
806
|
+
const summaryStyle = {
|
|
807
|
+
color: "#555",
|
|
808
|
+
background: "#fafafa",
|
|
809
|
+
padding: 8,
|
|
810
|
+
borderRadius: 8,
|
|
811
|
+
whiteSpace: "pre-wrap"
|
|
812
|
+
};
|
|
813
|
+
const buttonRow = {
|
|
814
|
+
display: "flex",
|
|
815
|
+
gap: 8,
|
|
816
|
+
marginTop: 4
|
|
817
|
+
};
|
|
818
|
+
const baseBtn = (variant) => ({
|
|
819
|
+
flex: 1,
|
|
820
|
+
padding: "8px 12px",
|
|
821
|
+
borderRadius: 8,
|
|
822
|
+
fontSize: 13,
|
|
823
|
+
fontWeight: 500,
|
|
824
|
+
cursor: chosen ? "default" : "pointer",
|
|
825
|
+
border: variant === "primary" ? `1px solid ${primaryColor}` : "1px solid #ddd",
|
|
826
|
+
background: variant === "primary" ? primaryColor : "#fff",
|
|
827
|
+
color: variant === "primary" ? "#fff" : "#333",
|
|
828
|
+
opacity: chosen && chosen !== (variant === "primary" ? "approve" : "cancel") ? 0.5 : 1,
|
|
829
|
+
transition: "opacity 0.15s",
|
|
830
|
+
display: "flex",
|
|
831
|
+
alignItems: "center",
|
|
832
|
+
justifyContent: "center",
|
|
833
|
+
gap: 6,
|
|
834
|
+
fontFamily: "inherit"
|
|
835
|
+
});
|
|
836
|
+
return /* @__PURE__ */ jsxs3("div", { style: cardStyle, role: "group", "aria-label": block.title, children: [
|
|
837
|
+
/* @__PURE__ */ jsx5("div", { style: titleStyle, children: block.title }),
|
|
838
|
+
block.summary && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
839
|
+
/* @__PURE__ */ jsxs3(
|
|
840
|
+
"button",
|
|
841
|
+
{
|
|
842
|
+
type: "button",
|
|
843
|
+
style: disclosureBtn,
|
|
844
|
+
onClick: () => setShowSummary((s) => !s),
|
|
845
|
+
"aria-expanded": showSummary,
|
|
846
|
+
children: [
|
|
847
|
+
showSummary ? "\u25BE" : "\u25B8",
|
|
848
|
+
" ",
|
|
849
|
+
t("action_what_will_happen")
|
|
850
|
+
]
|
|
851
|
+
}
|
|
852
|
+
),
|
|
853
|
+
showSummary && /* @__PURE__ */ jsx5("div", { style: summaryStyle, children: block.summary })
|
|
854
|
+
] }),
|
|
855
|
+
/* @__PURE__ */ jsxs3("div", { style: buttonRow, children: [
|
|
856
|
+
/* @__PURE__ */ jsx5(
|
|
857
|
+
"button",
|
|
858
|
+
{
|
|
859
|
+
type: "button",
|
|
860
|
+
style: baseBtn("ghost"),
|
|
861
|
+
disabled: chosen !== null,
|
|
862
|
+
onClick: () => handle("cancel"),
|
|
863
|
+
children: chosen === "cancel" && showSpinner ? /* @__PURE__ */ jsx5(Spinner, { color: "#333" }) : t("action_cancel")
|
|
864
|
+
}
|
|
865
|
+
),
|
|
866
|
+
/* @__PURE__ */ jsx5(
|
|
867
|
+
"button",
|
|
868
|
+
{
|
|
869
|
+
type: "button",
|
|
870
|
+
style: baseBtn("primary"),
|
|
871
|
+
disabled: chosen !== null,
|
|
872
|
+
onClick: () => handle("approve"),
|
|
873
|
+
children: chosen === "approve" && showSpinner ? /* @__PURE__ */ jsx5(Spinner, { color: "#fff" }) : t("action_approve")
|
|
874
|
+
}
|
|
875
|
+
)
|
|
876
|
+
] }),
|
|
877
|
+
error && /* @__PURE__ */ jsx5("div", { role: "alert", style: { color: "#b91c1c", fontSize: 12 }, children: error })
|
|
878
|
+
] });
|
|
879
|
+
}
|
|
880
|
+
function Spinner({ color }) {
|
|
881
|
+
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
882
|
+
/* @__PURE__ */ jsx5("style", { children: `@keyframes ch-spin { to { transform: rotate(360deg); } }` }),
|
|
883
|
+
/* @__PURE__ */ jsx5(
|
|
884
|
+
"span",
|
|
885
|
+
{
|
|
886
|
+
"aria-hidden": "true",
|
|
887
|
+
style: {
|
|
888
|
+
width: 14,
|
|
889
|
+
height: 14,
|
|
890
|
+
borderRadius: "50%",
|
|
891
|
+
border: `2px solid ${color}`,
|
|
892
|
+
borderTopColor: "transparent",
|
|
893
|
+
animation: "ch-spin 0.8s linear infinite",
|
|
894
|
+
display: "inline-block"
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
)
|
|
898
|
+
] });
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// src/components/chat-messages.tsx
|
|
902
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
903
|
+
function MessageStatusPill({
|
|
904
|
+
status,
|
|
905
|
+
t
|
|
906
|
+
}) {
|
|
907
|
+
const failed = status === "failed";
|
|
908
|
+
const containerStyle = {
|
|
909
|
+
display: "flex",
|
|
910
|
+
alignItems: "center",
|
|
911
|
+
gap: 4,
|
|
912
|
+
marginTop: 2,
|
|
913
|
+
fontSize: 11,
|
|
914
|
+
color: failed ? "#b91c1c" : "#888",
|
|
915
|
+
alignSelf: "flex-end"
|
|
916
|
+
};
|
|
917
|
+
const labelKey = status === "sending" ? "status_sending" : status === "sent" ? "status_sent" : "status_failed";
|
|
918
|
+
return /* @__PURE__ */ jsxs4("div", { style: containerStyle, "aria-live": "polite", children: [
|
|
919
|
+
status === "sending" && /* @__PURE__ */ jsx6(ClockIcon, {}),
|
|
920
|
+
status === "sent" && /* @__PURE__ */ jsx6(CheckIcon, {}),
|
|
921
|
+
status === "failed" && /* @__PURE__ */ jsx6(ExclamationIcon, {}),
|
|
922
|
+
/* @__PURE__ */ jsx6("span", { children: t(labelKey) })
|
|
923
|
+
] });
|
|
924
|
+
}
|
|
925
|
+
function ClockIcon() {
|
|
926
|
+
return /* @__PURE__ */ jsxs4(
|
|
927
|
+
"svg",
|
|
928
|
+
{
|
|
929
|
+
width: "11",
|
|
930
|
+
height: "11",
|
|
931
|
+
viewBox: "0 0 24 24",
|
|
932
|
+
fill: "none",
|
|
933
|
+
stroke: "currentColor",
|
|
934
|
+
strokeWidth: "2",
|
|
935
|
+
strokeLinecap: "round",
|
|
936
|
+
strokeLinejoin: "round",
|
|
937
|
+
"aria-hidden": "true",
|
|
938
|
+
children: [
|
|
939
|
+
/* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "10" }),
|
|
940
|
+
/* @__PURE__ */ jsx6("polyline", { points: "12 6 12 12 16 14" })
|
|
941
|
+
]
|
|
942
|
+
}
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
function CheckIcon() {
|
|
946
|
+
return /* @__PURE__ */ jsx6(
|
|
947
|
+
"svg",
|
|
948
|
+
{
|
|
949
|
+
width: "11",
|
|
950
|
+
height: "11",
|
|
951
|
+
viewBox: "0 0 24 24",
|
|
952
|
+
fill: "none",
|
|
953
|
+
stroke: "currentColor",
|
|
954
|
+
strokeWidth: "2.5",
|
|
955
|
+
strokeLinecap: "round",
|
|
956
|
+
strokeLinejoin: "round",
|
|
957
|
+
"aria-hidden": "true",
|
|
958
|
+
children: /* @__PURE__ */ jsx6("polyline", { points: "20 6 9 17 4 12" })
|
|
959
|
+
}
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
function ExclamationIcon() {
|
|
963
|
+
return /* @__PURE__ */ jsxs4(
|
|
964
|
+
"svg",
|
|
965
|
+
{
|
|
966
|
+
width: "11",
|
|
967
|
+
height: "11",
|
|
968
|
+
viewBox: "0 0 24 24",
|
|
969
|
+
fill: "none",
|
|
970
|
+
stroke: "currentColor",
|
|
971
|
+
strokeWidth: "2",
|
|
972
|
+
strokeLinecap: "round",
|
|
973
|
+
strokeLinejoin: "round",
|
|
974
|
+
"aria-hidden": "true",
|
|
975
|
+
children: [
|
|
976
|
+
/* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "10" }),
|
|
977
|
+
/* @__PURE__ */ jsx6("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
|
|
978
|
+
/* @__PURE__ */ jsx6("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
|
|
979
|
+
]
|
|
980
|
+
}
|
|
981
|
+
);
|
|
982
|
+
}
|
|
374
983
|
function MessageRatingButtons({
|
|
375
984
|
messageId,
|
|
376
985
|
onRate,
|
|
@@ -378,7 +987,7 @@ function MessageRatingButtons({
|
|
|
378
987
|
t,
|
|
379
988
|
reduced
|
|
380
989
|
}) {
|
|
381
|
-
const [rated, setRated] =
|
|
990
|
+
const [rated, setRated] = useState5(null);
|
|
382
991
|
const handleRate = (rating) => {
|
|
383
992
|
setRated(rating);
|
|
384
993
|
onRate(messageId, rating);
|
|
@@ -396,15 +1005,16 @@ function MessageRatingButtons({
|
|
|
396
1005
|
transition: reduced ? "color 0.15s" : "all 0.15s",
|
|
397
1006
|
transform: isRated && !reduced ? "scale(1.15)" : "scale(1)"
|
|
398
1007
|
});
|
|
399
|
-
return /* @__PURE__ */
|
|
400
|
-
/* @__PURE__ */
|
|
1008
|
+
return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 4, marginTop: 4 }, children: [
|
|
1009
|
+
/* @__PURE__ */ jsx6(
|
|
401
1010
|
"button",
|
|
402
1011
|
{
|
|
403
1012
|
onClick: () => handleRate("positive"),
|
|
404
1013
|
disabled: rated !== null,
|
|
405
1014
|
style: buttonStyle(rated === "positive"),
|
|
406
1015
|
title: t("helpful"),
|
|
407
|
-
|
|
1016
|
+
"aria-label": t("helpful"),
|
|
1017
|
+
children: /* @__PURE__ */ jsxs4(
|
|
408
1018
|
"svg",
|
|
409
1019
|
{
|
|
410
1020
|
width: "14",
|
|
@@ -416,21 +1026,22 @@ function MessageRatingButtons({
|
|
|
416
1026
|
strokeLinecap: "round",
|
|
417
1027
|
strokeLinejoin: "round",
|
|
418
1028
|
children: [
|
|
419
|
-
/* @__PURE__ */
|
|
420
|
-
/* @__PURE__ */
|
|
1029
|
+
/* @__PURE__ */ jsx6("path", { d: "M7 10v12" }),
|
|
1030
|
+
/* @__PURE__ */ jsx6("path", { d: "M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z" })
|
|
421
1031
|
]
|
|
422
1032
|
}
|
|
423
1033
|
)
|
|
424
1034
|
}
|
|
425
1035
|
),
|
|
426
|
-
/* @__PURE__ */
|
|
1036
|
+
/* @__PURE__ */ jsx6(
|
|
427
1037
|
"button",
|
|
428
1038
|
{
|
|
429
1039
|
onClick: () => handleRate("negative"),
|
|
430
1040
|
disabled: rated !== null,
|
|
431
1041
|
style: buttonStyle(rated === "negative"),
|
|
432
1042
|
title: t("not_helpful"),
|
|
433
|
-
|
|
1043
|
+
"aria-label": t("not_helpful"),
|
|
1044
|
+
children: /* @__PURE__ */ jsxs4(
|
|
434
1045
|
"svg",
|
|
435
1046
|
{
|
|
436
1047
|
width: "14",
|
|
@@ -442,8 +1053,8 @@ function MessageRatingButtons({
|
|
|
442
1053
|
strokeLinecap: "round",
|
|
443
1054
|
strokeLinejoin: "round",
|
|
444
1055
|
children: [
|
|
445
|
-
/* @__PURE__ */
|
|
446
|
-
/* @__PURE__ */
|
|
1056
|
+
/* @__PURE__ */ jsx6("path", { d: "M17 14V2" }),
|
|
1057
|
+
/* @__PURE__ */ jsx6("path", { d: "M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22h0a3.13 3.13 0 0 1-3-3.88Z" })
|
|
447
1058
|
]
|
|
448
1059
|
}
|
|
449
1060
|
)
|
|
@@ -457,12 +1068,14 @@ function AnimatedMessage({
|
|
|
457
1068
|
animate,
|
|
458
1069
|
reduced
|
|
459
1070
|
}) {
|
|
460
|
-
const [visible, setVisible] =
|
|
1071
|
+
const [visible, setVisible] = useState5(!animate);
|
|
461
1072
|
useEffect5(() => {
|
|
462
|
-
if (animate
|
|
463
|
-
|
|
464
|
-
return
|
|
1073
|
+
if (!animate || reduced) {
|
|
1074
|
+
setVisible(true);
|
|
1075
|
+
return;
|
|
465
1076
|
}
|
|
1077
|
+
const id = requestAnimationFrame(() => setVisible(true));
|
|
1078
|
+
return () => cancelAnimationFrame(id);
|
|
466
1079
|
}, [animate, reduced]);
|
|
467
1080
|
const style = {
|
|
468
1081
|
alignSelf: isUser ? "flex-end" : "flex-start",
|
|
@@ -471,16 +1084,123 @@ function AnimatedMessage({
|
|
|
471
1084
|
transform: visible ? "translateX(0)" : `translateX(${isUser ? "12px" : "-12px"})`,
|
|
472
1085
|
transition: animate && !reduced ? "opacity 0.2s ease, transform 0.2s ease" : "none"
|
|
473
1086
|
};
|
|
474
|
-
return /* @__PURE__ */
|
|
1087
|
+
return /* @__PURE__ */ jsx6("div", { style, children });
|
|
1088
|
+
}
|
|
1089
|
+
function ChipRow({
|
|
1090
|
+
options,
|
|
1091
|
+
onSelect,
|
|
1092
|
+
primaryColor,
|
|
1093
|
+
reduced
|
|
1094
|
+
}) {
|
|
1095
|
+
const chip = {
|
|
1096
|
+
background: "none",
|
|
1097
|
+
border: "1px solid #e0e0e0",
|
|
1098
|
+
borderRadius: 20,
|
|
1099
|
+
padding: "6px 12px",
|
|
1100
|
+
fontSize: 13,
|
|
1101
|
+
color: "#333",
|
|
1102
|
+
cursor: "pointer",
|
|
1103
|
+
textAlign: "left",
|
|
1104
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
1105
|
+
transition: reduced ? "none" : "border-color 0.15s, background 0.15s"
|
|
1106
|
+
};
|
|
1107
|
+
return /* @__PURE__ */ jsx6(
|
|
1108
|
+
"div",
|
|
1109
|
+
{
|
|
1110
|
+
style: {
|
|
1111
|
+
display: "flex",
|
|
1112
|
+
flexWrap: "wrap",
|
|
1113
|
+
gap: 6,
|
|
1114
|
+
marginTop: 6
|
|
1115
|
+
},
|
|
1116
|
+
children: options.map((text) => /* @__PURE__ */ jsx6(
|
|
1117
|
+
"button",
|
|
1118
|
+
{
|
|
1119
|
+
style: chip,
|
|
1120
|
+
onClick: () => onSelect(text),
|
|
1121
|
+
onMouseEnter: (e) => {
|
|
1122
|
+
e.currentTarget.style.borderColor = primaryColor;
|
|
1123
|
+
e.currentTarget.style.background = `${primaryColor}08`;
|
|
1124
|
+
},
|
|
1125
|
+
onMouseLeave: (e) => {
|
|
1126
|
+
e.currentTarget.style.borderColor = "#e0e0e0";
|
|
1127
|
+
e.currentTarget.style.background = "none";
|
|
1128
|
+
},
|
|
1129
|
+
children: text
|
|
1130
|
+
},
|
|
1131
|
+
text
|
|
1132
|
+
))
|
|
1133
|
+
}
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
function BlockRenderer({
|
|
1137
|
+
block,
|
|
1138
|
+
onSend,
|
|
1139
|
+
onApproveAction,
|
|
1140
|
+
onCancelAction,
|
|
1141
|
+
primaryColor,
|
|
1142
|
+
reduced,
|
|
1143
|
+
t
|
|
1144
|
+
}) {
|
|
1145
|
+
switch (block.type) {
|
|
1146
|
+
case "quick_replies":
|
|
1147
|
+
return /* @__PURE__ */ jsx6(
|
|
1148
|
+
ChipRow,
|
|
1149
|
+
{
|
|
1150
|
+
options: block.options,
|
|
1151
|
+
onSelect: onSend,
|
|
1152
|
+
primaryColor,
|
|
1153
|
+
reduced
|
|
1154
|
+
}
|
|
1155
|
+
);
|
|
1156
|
+
case "action_confirmation":
|
|
1157
|
+
return /* @__PURE__ */ jsx6(
|
|
1158
|
+
ActionConfirmationCard,
|
|
1159
|
+
{
|
|
1160
|
+
block,
|
|
1161
|
+
primaryColor,
|
|
1162
|
+
t,
|
|
1163
|
+
onApprove: onApproveAction,
|
|
1164
|
+
onCancel: onCancelAction
|
|
1165
|
+
}
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
function StreamingCursor({ reduced }) {
|
|
1170
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1171
|
+
/* @__PURE__ */ jsx6("style", { children: `@keyframes ch-caret-blink {
|
|
1172
|
+
0%, 50% { opacity: 1; }
|
|
1173
|
+
50.01%, 100% { opacity: 0; }
|
|
1174
|
+
}` }),
|
|
1175
|
+
/* @__PURE__ */ jsx6(
|
|
1176
|
+
"span",
|
|
1177
|
+
{
|
|
1178
|
+
"aria-hidden": "true",
|
|
1179
|
+
style: {
|
|
1180
|
+
display: "inline-block",
|
|
1181
|
+
width: 2,
|
|
1182
|
+
height: "1em",
|
|
1183
|
+
marginLeft: 2,
|
|
1184
|
+
verticalAlign: "text-bottom",
|
|
1185
|
+
background: "#777",
|
|
1186
|
+
animation: reduced ? "none" : "ch-caret-blink 1s step-start infinite"
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
)
|
|
1190
|
+
] });
|
|
475
1191
|
}
|
|
476
1192
|
function Message({
|
|
477
1193
|
message,
|
|
478
1194
|
config,
|
|
479
1195
|
onRate,
|
|
1196
|
+
onSend,
|
|
1197
|
+
onApproveAction,
|
|
1198
|
+
onCancelAction,
|
|
480
1199
|
hasConversation,
|
|
481
1200
|
t,
|
|
482
1201
|
animate,
|
|
483
|
-
reduced
|
|
1202
|
+
reduced,
|
|
1203
|
+
showSuggestions
|
|
484
1204
|
}) {
|
|
485
1205
|
const isUser = message.role === "user";
|
|
486
1206
|
const bubbleStyle = {
|
|
@@ -499,9 +1219,39 @@ function Message({
|
|
|
499
1219
|
borderBottomLeftRadius: 4
|
|
500
1220
|
}
|
|
501
1221
|
};
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
1222
|
+
const linkColor = isUser ? "#ffffff" : config.primaryColor;
|
|
1223
|
+
return /* @__PURE__ */ jsxs4(AnimatedMessage, { isUser, animate, reduced, children: [
|
|
1224
|
+
/* @__PURE__ */ jsx6("div", { style: bubbleStyle, children: isUser ? message.content : /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1225
|
+
renderMarkdown(message.content, {
|
|
1226
|
+
sources: message.sources,
|
|
1227
|
+
linkColor
|
|
1228
|
+
}),
|
|
1229
|
+
message.streaming && /* @__PURE__ */ jsx6(StreamingCursor, { reduced })
|
|
1230
|
+
] }) }),
|
|
1231
|
+
isUser && message.status && /* @__PURE__ */ jsx6(MessageStatusPill, { status: message.status, t }),
|
|
1232
|
+
!isUser && message.blocks?.map((block, i) => /* @__PURE__ */ jsx6(
|
|
1233
|
+
BlockRenderer,
|
|
1234
|
+
{
|
|
1235
|
+
block,
|
|
1236
|
+
onSend,
|
|
1237
|
+
onApproveAction,
|
|
1238
|
+
onCancelAction,
|
|
1239
|
+
primaryColor: config.primaryColor,
|
|
1240
|
+
reduced,
|
|
1241
|
+
t
|
|
1242
|
+
},
|
|
1243
|
+
i
|
|
1244
|
+
)),
|
|
1245
|
+
!isUser && showSuggestions && message.suggestions?.length ? /* @__PURE__ */ jsx6(
|
|
1246
|
+
ChipRow,
|
|
1247
|
+
{
|
|
1248
|
+
options: message.suggestions,
|
|
1249
|
+
onSelect: onSend,
|
|
1250
|
+
primaryColor: config.primaryColor,
|
|
1251
|
+
reduced
|
|
1252
|
+
}
|
|
1253
|
+
) : null,
|
|
1254
|
+
message.role === "bot" && message.id && hasConversation && !message.streaming && !message.blocks?.some((b) => b.type === "action_confirmation") && /* @__PURE__ */ jsx6(
|
|
505
1255
|
MessageRatingButtons,
|
|
506
1256
|
{
|
|
507
1257
|
messageId: message.id,
|
|
@@ -521,12 +1271,12 @@ function TypingDots({ reduced }) {
|
|
|
521
1271
|
background: "#999",
|
|
522
1272
|
animation: reduced ? "none" : `ch-dot-pulse 1.2s ease-in-out ${delay}s infinite`
|
|
523
1273
|
});
|
|
524
|
-
return /* @__PURE__ */
|
|
525
|
-
/* @__PURE__ */
|
|
1274
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1275
|
+
/* @__PURE__ */ jsx6("style", { children: `@keyframes ch-dot-pulse {
|
|
526
1276
|
0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
|
|
527
1277
|
40% { opacity: 1; transform: scale(1); }
|
|
528
1278
|
}` }),
|
|
529
|
-
/* @__PURE__ */
|
|
1279
|
+
/* @__PURE__ */ jsxs4(
|
|
530
1280
|
"div",
|
|
531
1281
|
{
|
|
532
1282
|
style: {
|
|
@@ -540,16 +1290,27 @@ function TypingDots({ reduced }) {
|
|
|
540
1290
|
alignItems: "center"
|
|
541
1291
|
},
|
|
542
1292
|
children: [
|
|
543
|
-
/* @__PURE__ */
|
|
544
|
-
/* @__PURE__ */
|
|
545
|
-
/* @__PURE__ */
|
|
1293
|
+
/* @__PURE__ */ jsx6("div", { style: dotStyle(0) }),
|
|
1294
|
+
/* @__PURE__ */ jsx6("div", { style: dotStyle(0.2) }),
|
|
1295
|
+
/* @__PURE__ */ jsx6("div", { style: dotStyle(0.4) })
|
|
546
1296
|
]
|
|
547
1297
|
}
|
|
548
1298
|
)
|
|
549
1299
|
] });
|
|
550
1300
|
}
|
|
551
1301
|
function ChatMessages() {
|
|
552
|
-
const {
|
|
1302
|
+
const {
|
|
1303
|
+
messages,
|
|
1304
|
+
isLoading,
|
|
1305
|
+
error,
|
|
1306
|
+
config,
|
|
1307
|
+
conversationId,
|
|
1308
|
+
rateMessage,
|
|
1309
|
+
sendMessage,
|
|
1310
|
+
approveAction,
|
|
1311
|
+
cancelAction,
|
|
1312
|
+
t
|
|
1313
|
+
} = useChat();
|
|
553
1314
|
const reduced = useReducedMotion();
|
|
554
1315
|
const messagesEndRef = useRef3(null);
|
|
555
1316
|
const isFirstRender = useRef3(true);
|
|
@@ -566,6 +1327,13 @@ function ChatMessages() {
|
|
|
566
1327
|
useEffect5(() => {
|
|
567
1328
|
prevMessageCount.current = messages.length;
|
|
568
1329
|
}, [messages.length]);
|
|
1330
|
+
let lastBotIndex = -1;
|
|
1331
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1332
|
+
if (messages[i].role === "bot") {
|
|
1333
|
+
lastBotIndex = i;
|
|
1334
|
+
break;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
569
1337
|
const containerStyle = {
|
|
570
1338
|
flex: 1,
|
|
571
1339
|
overflowY: "auto",
|
|
@@ -574,24 +1342,31 @@ function ChatMessages() {
|
|
|
574
1342
|
flexDirection: "column",
|
|
575
1343
|
gap: 12
|
|
576
1344
|
};
|
|
577
|
-
|
|
578
|
-
|
|
1345
|
+
const lastMsg = messages[messages.length - 1];
|
|
1346
|
+
const waitingForFirstToken = isLoading && (lastMsg?.role !== "bot" || lastMsg.streaming !== true);
|
|
1347
|
+
return /* @__PURE__ */ jsxs4("div", { style: containerStyle, children: [
|
|
1348
|
+
messages.map((msg, i) => /* @__PURE__ */ jsx6(
|
|
579
1349
|
Message,
|
|
580
1350
|
{
|
|
581
1351
|
message: msg,
|
|
582
1352
|
config,
|
|
583
1353
|
onRate: rateMessage,
|
|
1354
|
+
onSend: sendMessage,
|
|
1355
|
+
onApproveAction: approveAction,
|
|
1356
|
+
onCancelAction: cancelAction,
|
|
584
1357
|
hasConversation: conversationId !== null,
|
|
585
1358
|
t,
|
|
586
1359
|
animate: i >= newStartIndex,
|
|
587
|
-
reduced
|
|
1360
|
+
reduced,
|
|
1361
|
+
showSuggestions: i === lastBotIndex && !isLoading && msg.streaming !== true
|
|
588
1362
|
},
|
|
589
1363
|
i
|
|
590
1364
|
)),
|
|
591
|
-
|
|
592
|
-
error && /* @__PURE__ */
|
|
1365
|
+
waitingForFirstToken && /* @__PURE__ */ jsx6(TypingDots, { reduced }),
|
|
1366
|
+
error && /* @__PURE__ */ jsx6(
|
|
593
1367
|
"div",
|
|
594
1368
|
{
|
|
1369
|
+
role: "alert",
|
|
595
1370
|
style: {
|
|
596
1371
|
alignSelf: "flex-start",
|
|
597
1372
|
padding: "10px 14px",
|
|
@@ -604,12 +1379,12 @@ function ChatMessages() {
|
|
|
604
1379
|
children: error
|
|
605
1380
|
}
|
|
606
1381
|
),
|
|
607
|
-
/* @__PURE__ */
|
|
1382
|
+
/* @__PURE__ */ jsx6("div", { ref: messagesEndRef })
|
|
608
1383
|
] });
|
|
609
1384
|
}
|
|
610
1385
|
|
|
611
1386
|
// src/components/chat-suggestions.tsx
|
|
612
|
-
import { jsx as
|
|
1387
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
613
1388
|
function ChatSuggestions() {
|
|
614
1389
|
const { messages, isLoading, config, sendMessage } = useChat();
|
|
615
1390
|
const reduced = useReducedMotion();
|
|
@@ -636,7 +1411,7 @@ function ChatSuggestions() {
|
|
|
636
1411
|
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
637
1412
|
transition: reduced ? "none" : "border-color 0.15s, background 0.15s"
|
|
638
1413
|
};
|
|
639
|
-
return /* @__PURE__ */
|
|
1414
|
+
return /* @__PURE__ */ jsx7("div", { style: containerStyle, children: config.suggestedMessages.map((text) => /* @__PURE__ */ jsx7(
|
|
640
1415
|
"button",
|
|
641
1416
|
{
|
|
642
1417
|
style: chipStyle,
|
|
@@ -656,15 +1431,82 @@ function ChatSuggestions() {
|
|
|
656
1431
|
}
|
|
657
1432
|
|
|
658
1433
|
// src/components/chat-input.tsx
|
|
659
|
-
import {
|
|
660
|
-
|
|
1434
|
+
import {
|
|
1435
|
+
useEffect as useEffect6,
|
|
1436
|
+
useState as useState6
|
|
1437
|
+
} from "react";
|
|
1438
|
+
import {
|
|
1439
|
+
ScreenshotCancelled,
|
|
1440
|
+
canCaptureScreenshot,
|
|
1441
|
+
captureScreenshot
|
|
1442
|
+
} from "@customerhero/js";
|
|
1443
|
+
import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1444
|
+
var MAX_ATTACHMENTS = 3;
|
|
661
1445
|
function ChatInput() {
|
|
662
|
-
const { sendMessage, isLoading, config, t } = useChat();
|
|
1446
|
+
const { sendMessage, uploadAttachment, isLoading, config, t } = useChat();
|
|
663
1447
|
const reduced = useReducedMotion();
|
|
664
|
-
const [value, setValue] =
|
|
1448
|
+
const [value, setValue] = useState6("");
|
|
1449
|
+
const [attachments, setAttachments] = useState6([]);
|
|
1450
|
+
const [captureSupported, setCaptureSupported] = useState6(false);
|
|
1451
|
+
useEffect6(() => {
|
|
1452
|
+
setCaptureSupported(canCaptureScreenshot());
|
|
1453
|
+
}, []);
|
|
1454
|
+
useEffect6(() => {
|
|
1455
|
+
return () => {
|
|
1456
|
+
for (const a of attachments) URL.revokeObjectURL(a.previewUrl);
|
|
1457
|
+
};
|
|
1458
|
+
}, []);
|
|
1459
|
+
const updateAttachment = (id, patch) => {
|
|
1460
|
+
setAttachments(
|
|
1461
|
+
(current) => current.map(
|
|
1462
|
+
(a) => a.id === id ? { ...a, ...patch } : a
|
|
1463
|
+
)
|
|
1464
|
+
);
|
|
1465
|
+
};
|
|
1466
|
+
const startUpload = async (blob) => {
|
|
1467
|
+
if (attachments.length >= MAX_ATTACHMENTS) return;
|
|
1468
|
+
const id = `att_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
1469
|
+
const previewUrl = URL.createObjectURL(blob);
|
|
1470
|
+
setAttachments((current) => [
|
|
1471
|
+
...current,
|
|
1472
|
+
{ id, status: "uploading", previewUrl, blob }
|
|
1473
|
+
]);
|
|
1474
|
+
try {
|
|
1475
|
+
const { attachmentToken } = await uploadAttachment(blob);
|
|
1476
|
+
updateAttachment(id, {
|
|
1477
|
+
status: "ready",
|
|
1478
|
+
token: attachmentToken
|
|
1479
|
+
});
|
|
1480
|
+
} catch {
|
|
1481
|
+
updateAttachment(id, { status: "error" });
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
const handleCapture = async () => {
|
|
1485
|
+
try {
|
|
1486
|
+
const blob = await captureScreenshot();
|
|
1487
|
+
await startUpload(blob);
|
|
1488
|
+
} catch (e) {
|
|
1489
|
+
if (e instanceof ScreenshotCancelled) return;
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
const handleRemove = (id) => {
|
|
1493
|
+
setAttachments((current) => {
|
|
1494
|
+
const target = current.find((a) => a.id === id);
|
|
1495
|
+
if (target) URL.revokeObjectURL(target.previewUrl);
|
|
1496
|
+
return current.filter((a) => a.id !== id);
|
|
1497
|
+
});
|
|
1498
|
+
};
|
|
1499
|
+
const readyTokens = attachments.filter(
|
|
1500
|
+
(a) => a.status === "ready"
|
|
1501
|
+
).map((a) => a.token);
|
|
665
1502
|
const handleSend = () => {
|
|
666
1503
|
if (!value.trim() || isLoading) return;
|
|
667
|
-
sendMessage(
|
|
1504
|
+
sendMessage(
|
|
1505
|
+
value,
|
|
1506
|
+
readyTokens.length > 0 ? { attachmentTokens: readyTokens } : void 0
|
|
1507
|
+
);
|
|
1508
|
+
for (const a of attachments) URL.revokeObjectURL(a.previewUrl);
|
|
1509
|
+
setAttachments([]);
|
|
668
1510
|
setValue("");
|
|
669
1511
|
};
|
|
670
1512
|
const handleKeyDown = (e) => {
|
|
@@ -676,6 +1518,11 @@ function ChatInput() {
|
|
|
676
1518
|
const containerStyle = {
|
|
677
1519
|
padding: "12px 16px",
|
|
678
1520
|
borderTop: "1px solid #eee",
|
|
1521
|
+
display: "flex",
|
|
1522
|
+
flexDirection: "column",
|
|
1523
|
+
gap: 8
|
|
1524
|
+
};
|
|
1525
|
+
const rowStyle = {
|
|
679
1526
|
display: "flex",
|
|
680
1527
|
alignItems: "center",
|
|
681
1528
|
gap: 8
|
|
@@ -691,7 +1538,7 @@ function ChatInput() {
|
|
|
691
1538
|
color: config.textColor,
|
|
692
1539
|
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
693
1540
|
};
|
|
694
|
-
const
|
|
1541
|
+
const sendButtonStyle = {
|
|
695
1542
|
width: 36,
|
|
696
1543
|
height: 36,
|
|
697
1544
|
borderRadius: "50%",
|
|
@@ -707,57 +1554,210 @@ function ChatInput() {
|
|
|
707
1554
|
flexShrink: 0,
|
|
708
1555
|
transform: "scale(1)"
|
|
709
1556
|
};
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
1557
|
+
const iconButtonStyle = (disabled) => ({
|
|
1558
|
+
width: 36,
|
|
1559
|
+
height: 36,
|
|
1560
|
+
borderRadius: "50%",
|
|
1561
|
+
background: "transparent",
|
|
1562
|
+
border: "none",
|
|
1563
|
+
color: disabled ? "#ccc" : "#666",
|
|
1564
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
1565
|
+
display: "flex",
|
|
1566
|
+
alignItems: "center",
|
|
1567
|
+
justifyContent: "center",
|
|
1568
|
+
flexShrink: 0,
|
|
1569
|
+
padding: 0
|
|
1570
|
+
});
|
|
1571
|
+
const captureDisabled = attachments.length >= MAX_ATTACHMENTS || isLoading;
|
|
1572
|
+
return /* @__PURE__ */ jsxs5("div", { style: containerStyle, children: [
|
|
1573
|
+
attachments.length > 0 && /* @__PURE__ */ jsx8(
|
|
1574
|
+
"div",
|
|
713
1575
|
{
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
1576
|
+
style: { display: "flex", gap: 8, flexWrap: "wrap" },
|
|
1577
|
+
"aria-label": "Attachments",
|
|
1578
|
+
children: attachments.map((a) => /* @__PURE__ */ jsx8(
|
|
1579
|
+
Thumbnail,
|
|
1580
|
+
{
|
|
1581
|
+
attachment: a,
|
|
1582
|
+
onRemove: () => handleRemove(a.id),
|
|
1583
|
+
t
|
|
1584
|
+
},
|
|
1585
|
+
a.id
|
|
1586
|
+
))
|
|
721
1587
|
}
|
|
722
1588
|
),
|
|
723
|
-
/* @__PURE__ */
|
|
1589
|
+
/* @__PURE__ */ jsxs5("div", { style: rowStyle, children: [
|
|
1590
|
+
captureSupported && /* @__PURE__ */ jsx8(
|
|
1591
|
+
"button",
|
|
1592
|
+
{
|
|
1593
|
+
type: "button",
|
|
1594
|
+
onClick: handleCapture,
|
|
1595
|
+
disabled: captureDisabled,
|
|
1596
|
+
style: iconButtonStyle(captureDisabled),
|
|
1597
|
+
"aria-label": t("screenshot_capture"),
|
|
1598
|
+
title: t("screenshot_capture"),
|
|
1599
|
+
children: /* @__PURE__ */ jsx8(CameraIcon, {})
|
|
1600
|
+
}
|
|
1601
|
+
),
|
|
1602
|
+
/* @__PURE__ */ jsx8(
|
|
1603
|
+
"input",
|
|
1604
|
+
{
|
|
1605
|
+
type: "text",
|
|
1606
|
+
value,
|
|
1607
|
+
onChange: (e) => setValue(e.target.value),
|
|
1608
|
+
onKeyDown: handleKeyDown,
|
|
1609
|
+
placeholder: config.placeholderText,
|
|
1610
|
+
style: inputStyle,
|
|
1611
|
+
disabled: isLoading
|
|
1612
|
+
}
|
|
1613
|
+
),
|
|
1614
|
+
/* @__PURE__ */ jsx8(
|
|
1615
|
+
"button",
|
|
1616
|
+
{
|
|
1617
|
+
onClick: handleSend,
|
|
1618
|
+
disabled: isLoading || !value.trim(),
|
|
1619
|
+
style: sendButtonStyle,
|
|
1620
|
+
"aria-label": t("send_message"),
|
|
1621
|
+
onMouseEnter: (e) => {
|
|
1622
|
+
if (!reduced && !isLoading)
|
|
1623
|
+
e.currentTarget.style.transform = "scale(1.1)";
|
|
1624
|
+
},
|
|
1625
|
+
onMouseLeave: (e) => {
|
|
1626
|
+
if (!reduced) e.currentTarget.style.transform = "scale(1)";
|
|
1627
|
+
},
|
|
1628
|
+
children: /* @__PURE__ */ jsxs5(
|
|
1629
|
+
"svg",
|
|
1630
|
+
{
|
|
1631
|
+
width: "16",
|
|
1632
|
+
height: "16",
|
|
1633
|
+
viewBox: "0 0 24 24",
|
|
1634
|
+
fill: "none",
|
|
1635
|
+
stroke: "currentColor",
|
|
1636
|
+
strokeWidth: "2",
|
|
1637
|
+
strokeLinecap: "round",
|
|
1638
|
+
strokeLinejoin: "round",
|
|
1639
|
+
children: [
|
|
1640
|
+
/* @__PURE__ */ jsx8("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
1641
|
+
/* @__PURE__ */ jsx8("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
1642
|
+
]
|
|
1643
|
+
}
|
|
1644
|
+
)
|
|
1645
|
+
}
|
|
1646
|
+
)
|
|
1647
|
+
] })
|
|
1648
|
+
] });
|
|
1649
|
+
}
|
|
1650
|
+
function Thumbnail({
|
|
1651
|
+
attachment,
|
|
1652
|
+
onRemove,
|
|
1653
|
+
t
|
|
1654
|
+
}) {
|
|
1655
|
+
const { status, previewUrl } = attachment;
|
|
1656
|
+
const wrap = {
|
|
1657
|
+
position: "relative",
|
|
1658
|
+
width: 56,
|
|
1659
|
+
height: 56,
|
|
1660
|
+
borderRadius: 8,
|
|
1661
|
+
overflow: "hidden",
|
|
1662
|
+
border: status === "error" ? "2px solid #b91c1c" : "1px solid #e0e0e0",
|
|
1663
|
+
background: "#f5f5f5"
|
|
1664
|
+
};
|
|
1665
|
+
const img = {
|
|
1666
|
+
width: "100%",
|
|
1667
|
+
height: "100%",
|
|
1668
|
+
objectFit: "cover"
|
|
1669
|
+
};
|
|
1670
|
+
const removeBtn = {
|
|
1671
|
+
position: "absolute",
|
|
1672
|
+
top: 2,
|
|
1673
|
+
right: 2,
|
|
1674
|
+
width: 18,
|
|
1675
|
+
height: 18,
|
|
1676
|
+
borderRadius: "50%",
|
|
1677
|
+
border: "none",
|
|
1678
|
+
background: "rgba(0,0,0,0.6)",
|
|
1679
|
+
color: "white",
|
|
1680
|
+
fontSize: 11,
|
|
1681
|
+
lineHeight: "18px",
|
|
1682
|
+
padding: 0,
|
|
1683
|
+
cursor: "pointer",
|
|
1684
|
+
textAlign: "center"
|
|
1685
|
+
};
|
|
1686
|
+
const overlay = {
|
|
1687
|
+
position: "absolute",
|
|
1688
|
+
inset: 0,
|
|
1689
|
+
background: "rgba(255,255,255,0.7)",
|
|
1690
|
+
display: "flex",
|
|
1691
|
+
alignItems: "center",
|
|
1692
|
+
justifyContent: "center"
|
|
1693
|
+
};
|
|
1694
|
+
return /* @__PURE__ */ jsxs5("div", { style: wrap, children: [
|
|
1695
|
+
/* @__PURE__ */ jsx8("img", { src: previewUrl, alt: "", style: img }),
|
|
1696
|
+
status === "uploading" && /* @__PURE__ */ jsx8("div", { style: overlay, "aria-label": "Uploading", children: /* @__PURE__ */ jsx8(Spinner2, {}) }),
|
|
1697
|
+
status === "error" && /* @__PURE__ */ jsx8(
|
|
1698
|
+
"div",
|
|
1699
|
+
{
|
|
1700
|
+
style: { ...overlay, background: "rgba(255,255,255,0.85)" },
|
|
1701
|
+
"aria-label": t("status_failed"),
|
|
1702
|
+
title: t("status_failed"),
|
|
1703
|
+
children: /* @__PURE__ */ jsx8("span", { style: { color: "#b91c1c", fontSize: 11, fontWeight: 600 }, children: "!" })
|
|
1704
|
+
}
|
|
1705
|
+
),
|
|
1706
|
+
/* @__PURE__ */ jsx8(
|
|
724
1707
|
"button",
|
|
725
1708
|
{
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
"aria-label": t("
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
1709
|
+
type: "button",
|
|
1710
|
+
style: removeBtn,
|
|
1711
|
+
onClick: onRemove,
|
|
1712
|
+
"aria-label": t("attachment_remove"),
|
|
1713
|
+
children: "\xD7"
|
|
1714
|
+
}
|
|
1715
|
+
)
|
|
1716
|
+
] });
|
|
1717
|
+
}
|
|
1718
|
+
function CameraIcon() {
|
|
1719
|
+
return /* @__PURE__ */ jsxs5(
|
|
1720
|
+
"svg",
|
|
1721
|
+
{
|
|
1722
|
+
width: "20",
|
|
1723
|
+
height: "20",
|
|
1724
|
+
viewBox: "0 0 24 24",
|
|
1725
|
+
fill: "none",
|
|
1726
|
+
stroke: "currentColor",
|
|
1727
|
+
strokeWidth: "2",
|
|
1728
|
+
strokeLinecap: "round",
|
|
1729
|
+
strokeLinejoin: "round",
|
|
1730
|
+
"aria-hidden": "true",
|
|
1731
|
+
children: [
|
|
1732
|
+
/* @__PURE__ */ jsx8("path", { d: "M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" }),
|
|
1733
|
+
/* @__PURE__ */ jsx8("circle", { cx: "12", cy: "13", r: "4" })
|
|
1734
|
+
]
|
|
1735
|
+
}
|
|
1736
|
+
);
|
|
1737
|
+
}
|
|
1738
|
+
function Spinner2() {
|
|
1739
|
+
return /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
1740
|
+
/* @__PURE__ */ jsx8("style", { children: `@keyframes ch-att-spin { to { transform: rotate(360deg); } }` }),
|
|
1741
|
+
/* @__PURE__ */ jsx8(
|
|
1742
|
+
"span",
|
|
1743
|
+
{
|
|
1744
|
+
"aria-hidden": "true",
|
|
1745
|
+
style: {
|
|
1746
|
+
width: 16,
|
|
1747
|
+
height: 16,
|
|
1748
|
+
borderRadius: "50%",
|
|
1749
|
+
border: "2px solid #888",
|
|
1750
|
+
borderTopColor: "transparent",
|
|
1751
|
+
animation: "ch-att-spin 0.8s linear infinite",
|
|
1752
|
+
display: "inline-block"
|
|
1753
|
+
}
|
|
754
1754
|
}
|
|
755
1755
|
)
|
|
756
1756
|
] });
|
|
757
1757
|
}
|
|
758
1758
|
|
|
759
1759
|
// src/components/chat-window.tsx
|
|
760
|
-
import { Fragment as
|
|
1760
|
+
import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
761
1761
|
function ConfigError({ message, title }) {
|
|
762
1762
|
const errorStyle = {
|
|
763
1763
|
flex: 1,
|
|
@@ -769,8 +1769,8 @@ function ConfigError({ message, title }) {
|
|
|
769
1769
|
textAlign: "center",
|
|
770
1770
|
gap: 8
|
|
771
1771
|
};
|
|
772
|
-
return /* @__PURE__ */
|
|
773
|
-
/* @__PURE__ */
|
|
1772
|
+
return /* @__PURE__ */ jsxs6("div", { style: errorStyle, children: [
|
|
1773
|
+
/* @__PURE__ */ jsxs6(
|
|
774
1774
|
"svg",
|
|
775
1775
|
{
|
|
776
1776
|
width: "32",
|
|
@@ -782,22 +1782,22 @@ function ConfigError({ message, title }) {
|
|
|
782
1782
|
strokeLinecap: "round",
|
|
783
1783
|
strokeLinejoin: "round",
|
|
784
1784
|
children: [
|
|
785
|
-
/* @__PURE__ */
|
|
786
|
-
/* @__PURE__ */
|
|
787
|
-
/* @__PURE__ */
|
|
1785
|
+
/* @__PURE__ */ jsx9("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1786
|
+
/* @__PURE__ */ jsx9("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
|
|
1787
|
+
/* @__PURE__ */ jsx9("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
|
|
788
1788
|
]
|
|
789
1789
|
}
|
|
790
1790
|
),
|
|
791
|
-
/* @__PURE__ */
|
|
792
|
-
/* @__PURE__ */
|
|
1791
|
+
/* @__PURE__ */ jsx9("p", { style: { fontSize: 14, fontWeight: 500, color: "#333", margin: 0 }, children: title }),
|
|
1792
|
+
/* @__PURE__ */ jsx9("p", { style: { fontSize: 12, color: "#999", margin: 0 }, children: message })
|
|
793
1793
|
] });
|
|
794
1794
|
}
|
|
795
1795
|
function ChatWindow() {
|
|
796
|
-
const { isOpen, config, configError, t } = useChat();
|
|
1796
|
+
const { isOpen, config, configError, t, isRtl } = useChat();
|
|
797
1797
|
const reduced = useReducedMotion();
|
|
798
|
-
const [visible, setVisible] =
|
|
799
|
-
const [shouldRender, setShouldRender] =
|
|
800
|
-
|
|
1798
|
+
const [visible, setVisible] = useState7(false);
|
|
1799
|
+
const [shouldRender, setShouldRender] = useState7(false);
|
|
1800
|
+
useEffect7(() => {
|
|
801
1801
|
if (isOpen) {
|
|
802
1802
|
setShouldRender(true);
|
|
803
1803
|
requestAnimationFrame(() => {
|
|
@@ -814,10 +1814,11 @@ function ChatWindow() {
|
|
|
814
1814
|
}
|
|
815
1815
|
}, [isOpen, reduced]);
|
|
816
1816
|
if (!shouldRender) return null;
|
|
1817
|
+
const effectivePosition = isRtl ? config.position === "bottom-right" ? "bottom-left" : "bottom-right" : config.position;
|
|
817
1818
|
const style = {
|
|
818
1819
|
position: "fixed",
|
|
819
1820
|
bottom: 90,
|
|
820
|
-
[
|
|
1821
|
+
[effectivePosition === "bottom-left" ? "left" : "right"]: 20,
|
|
821
1822
|
width: 380,
|
|
822
1823
|
maxWidth: "calc(100vw - 40px)",
|
|
823
1824
|
height: 520,
|
|
@@ -845,17 +1846,17 @@ function ChatWindow() {
|
|
|
845
1846
|
textDecoration: "underline",
|
|
846
1847
|
textUnderlineOffset: 2
|
|
847
1848
|
};
|
|
848
|
-
return /* @__PURE__ */
|
|
849
|
-
/* @__PURE__ */
|
|
850
|
-
configError ? /* @__PURE__ */
|
|
851
|
-
/* @__PURE__ */
|
|
852
|
-
/* @__PURE__ */
|
|
853
|
-
/* @__PURE__ */
|
|
1849
|
+
return /* @__PURE__ */ jsxs6("div", { style, dir: isRtl ? "rtl" : "ltr", children: [
|
|
1850
|
+
/* @__PURE__ */ jsx9(ChatHeader, {}),
|
|
1851
|
+
configError ? /* @__PURE__ */ jsx9(ConfigError, { title: t("unable_to_load"), message: configError }) : /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1852
|
+
/* @__PURE__ */ jsx9(ChatMessages, {}),
|
|
1853
|
+
/* @__PURE__ */ jsx9(ChatSuggestions, {}),
|
|
1854
|
+
/* @__PURE__ */ jsx9(ChatInput, {})
|
|
854
1855
|
] }),
|
|
855
|
-
/* @__PURE__ */
|
|
1856
|
+
/* @__PURE__ */ jsxs6("div", { style: poweredStyle, children: [
|
|
856
1857
|
t("powered_by"),
|
|
857
1858
|
" ",
|
|
858
|
-
/* @__PURE__ */
|
|
1859
|
+
/* @__PURE__ */ jsx9(
|
|
859
1860
|
"a",
|
|
860
1861
|
{
|
|
861
1862
|
href: "https://customerhero.app",
|
|
@@ -870,11 +1871,11 @@ function ChatWindow() {
|
|
|
870
1871
|
}
|
|
871
1872
|
|
|
872
1873
|
// src/components/chat-widget.tsx
|
|
873
|
-
import { Fragment as
|
|
1874
|
+
import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
874
1875
|
function ChatWidgetInner({ identity }) {
|
|
875
1876
|
const client = useCustomerHeroClient();
|
|
876
1877
|
const prevIdentityRef = useRef4(void 0);
|
|
877
|
-
|
|
1878
|
+
useEffect8(() => {
|
|
878
1879
|
const key = identity ? JSON.stringify(identity) : void 0;
|
|
879
1880
|
if (key !== prevIdentityRef.current) {
|
|
880
1881
|
prevIdentityRef.current = key;
|
|
@@ -883,14 +1884,16 @@ function ChatWidgetInner({ identity }) {
|
|
|
883
1884
|
}
|
|
884
1885
|
}
|
|
885
1886
|
}, [identity, client]);
|
|
886
|
-
return /* @__PURE__ */
|
|
887
|
-
/* @__PURE__ */
|
|
888
|
-
/* @__PURE__ */
|
|
1887
|
+
return /* @__PURE__ */ jsxs7(Fragment6, { children: [
|
|
1888
|
+
/* @__PURE__ */ jsx10(ChatBubble, {}),
|
|
1889
|
+
/* @__PURE__ */ jsx10(ChatWindow, {})
|
|
889
1890
|
] });
|
|
890
1891
|
}
|
|
891
1892
|
function ChatWidget({ identity, ...config }) {
|
|
892
|
-
return /* @__PURE__ */
|
|
1893
|
+
return /* @__PURE__ */ jsx10(CustomerHeroProvider, { ...config, children: /* @__PURE__ */ jsx10(ChatWidgetInner, { identity }) });
|
|
893
1894
|
}
|
|
894
1895
|
export {
|
|
895
|
-
|
|
1896
|
+
ActionConfirmationCard,
|
|
1897
|
+
ChatWidget,
|
|
1898
|
+
useChat
|
|
896
1899
|
};
|