@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 CHANGED
@@ -20,12 +20,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- ChatWidget: () => ChatWidget
23
+ ActionConfirmationCard: () => ActionConfirmationCard,
24
+ ChatWidget: () => ChatWidget,
25
+ useChat: () => useChat
24
26
  });
25
27
  module.exports = __toCommonJS(index_exports);
26
28
 
27
29
  // src/components/chat-widget.tsx
28
- var import_react9 = require("react");
30
+ var import_react10 = require("react");
29
31
 
30
32
  // src/context.tsx
31
33
  var import_react = require("react");
@@ -71,13 +73,26 @@ function useChat() {
71
73
  ...state,
72
74
  t: client.t,
73
75
  sendMessage: (0, import_react2.useCallback)(
74
- (message) => client.sendMessage(message),
76
+ (message, options) => client.sendMessage(message, options),
77
+ [client]
78
+ ),
79
+ uploadAttachment: (0, import_react2.useCallback)(
80
+ (blob, options) => client.uploadAttachment(blob, options),
75
81
  [client]
76
82
  ),
77
83
  rateMessage: (0, import_react2.useCallback)(
78
84
  (messageId, rating) => client.rateMessage(messageId, rating),
79
85
  [client]
80
86
  ),
87
+ approveAction: (0, import_react2.useCallback)(
88
+ (pendingId) => client.approveAction(pendingId),
89
+ [client]
90
+ ),
91
+ cancelAction: (0, import_react2.useCallback)(
92
+ (pendingId) => client.cancelAction(pendingId),
93
+ [client]
94
+ ),
95
+ setLocale: (0, import_react2.useCallback)((tag) => client.setLocale(tag), [client]),
81
96
  toggle: (0, import_react2.useCallback)(() => client.toggle(), [client]),
82
97
  open: (0, import_react2.useCallback)(() => client.open(), [client]),
83
98
  close: (0, import_react2.useCallback)(() => client.close(), [client]),
@@ -108,7 +123,7 @@ function useReducedMotion() {
108
123
  // src/components/chat-bubble.tsx
109
124
  var import_jsx_runtime2 = require("react/jsx-runtime");
110
125
  function ChatBubble() {
111
- const { toggle, config, t } = useChat();
126
+ const { toggle, config, t, isRtl } = useChat();
112
127
  const reduced = useReducedMotion();
113
128
  const [mounted, setMounted] = (0, import_react4.useState)(false);
114
129
  (0, import_react4.useEffect)(() => {
@@ -116,10 +131,11 @@ function ChatBubble() {
116
131
  return () => cancelAnimationFrame(id);
117
132
  }, []);
118
133
  const visible = mounted;
134
+ const effectivePosition = isRtl ? config.position === "bottom-right" ? "bottom-left" : "bottom-right" : config.position;
119
135
  const style = {
120
136
  position: "fixed",
121
137
  bottom: 20,
122
- [config.position === "bottom-left" ? "left" : "right"]: 20,
138
+ [effectivePosition === "bottom-left" ? "left" : "right"]: 20,
123
139
  width: 56,
124
140
  height: 56,
125
141
  borderRadius: "50%",
@@ -143,6 +159,7 @@ function ChatBubble() {
143
159
  {
144
160
  onClick: toggle,
145
161
  style,
162
+ dir: isRtl ? "rtl" : "ltr",
146
163
  "aria-label": t("open_chat"),
147
164
  onMouseEnter: (e) => {
148
165
  if (!reduced) e.currentTarget.style.transform = "scale(1.1)";
@@ -169,7 +186,7 @@ function ChatBubble() {
169
186
  }
170
187
 
171
188
  // src/components/chat-window.tsx
172
- var import_react8 = require("react");
189
+ var import_react9 = require("react");
173
190
 
174
191
  // src/components/chat-header.tsx
175
192
  var import_react5 = require("react");
@@ -388,8 +405,602 @@ function ChatHeader() {
388
405
  }
389
406
 
390
407
  // src/components/chat-messages.tsx
391
- var import_react6 = require("react");
408
+ var import_react7 = require("react");
409
+
410
+ // src/markdown/render.tsx
392
411
  var import_jsx_runtime4 = require("react/jsx-runtime");
412
+ function parseBlocks(source) {
413
+ const lines = source.replace(/\r\n?/g, "\n").split("\n");
414
+ const blocks = [];
415
+ let i = 0;
416
+ while (i < lines.length) {
417
+ const line = lines[i];
418
+ if (line.trim() === "") {
419
+ i++;
420
+ continue;
421
+ }
422
+ const fenceMatch = line.match(/^```(.*)$/);
423
+ if (fenceMatch) {
424
+ const lang = fenceMatch[1].trim() || void 0;
425
+ const body2 = [];
426
+ i++;
427
+ while (i < lines.length && !/^```\s*$/.test(lines[i])) {
428
+ body2.push(lines[i]);
429
+ i++;
430
+ }
431
+ if (i < lines.length) i++;
432
+ blocks.push({ kind: "code", lines: body2, lang });
433
+ continue;
434
+ }
435
+ const headingMatch = line.match(/^(#{1,6})\s+(.*?)\s*#*\s*$/);
436
+ if (headingMatch) {
437
+ blocks.push({
438
+ kind: "heading",
439
+ level: headingMatch[1].length,
440
+ lines: [headingMatch[2]]
441
+ });
442
+ i++;
443
+ continue;
444
+ }
445
+ if (/^(\s*)[-*+]\s+/.test(line)) {
446
+ const body2 = [];
447
+ while (i < lines.length && /^(\s*)[-*+]\s+/.test(lines[i])) {
448
+ body2.push(lines[i].replace(/^(\s*)[-*+]\s+/, ""));
449
+ i++;
450
+ }
451
+ blocks.push({ kind: "ul", lines: body2 });
452
+ continue;
453
+ }
454
+ if (/^(\s*)\d+\.\s+/.test(line)) {
455
+ const body2 = [];
456
+ while (i < lines.length && /^(\s*)\d+\.\s+/.test(lines[i])) {
457
+ body2.push(lines[i].replace(/^(\s*)\d+\.\s+/, ""));
458
+ i++;
459
+ }
460
+ blocks.push({ kind: "ol", lines: body2 });
461
+ continue;
462
+ }
463
+ if (/^>\s?/.test(line)) {
464
+ const body2 = [];
465
+ while (i < lines.length && /^>\s?/.test(lines[i])) {
466
+ body2.push(lines[i].replace(/^>\s?/, ""));
467
+ i++;
468
+ }
469
+ blocks.push({ kind: "blockquote", lines: body2 });
470
+ continue;
471
+ }
472
+ const body = [line];
473
+ i++;
474
+ while (i < lines.length) {
475
+ const next = lines[i];
476
+ if (next.trim() === "") break;
477
+ if (/^```/.test(next) || /^#{1,6}\s/.test(next) || /^(\s*)[-*+]\s+/.test(next) || /^(\s*)\d+\.\s+/.test(next) || /^>\s?/.test(next)) {
478
+ break;
479
+ }
480
+ body.push(next);
481
+ i++;
482
+ }
483
+ blocks.push({ kind: "paragraph", lines: body });
484
+ }
485
+ return blocks;
486
+ }
487
+ function findClosing(text, from, end) {
488
+ let i = from;
489
+ while (i <= text.length - end.length) {
490
+ const c = text[i];
491
+ if (c === "\\") {
492
+ i += 2;
493
+ continue;
494
+ }
495
+ if (text.startsWith(end, i)) return i;
496
+ i++;
497
+ }
498
+ return -1;
499
+ }
500
+ function renderInlineText(text, ctx) {
501
+ const out = [];
502
+ let buffer = "";
503
+ let i = 0;
504
+ const flushBuffer = () => {
505
+ if (buffer.length > 0) {
506
+ out.push(buffer);
507
+ buffer = "";
508
+ }
509
+ };
510
+ while (i < text.length) {
511
+ const c = text[i];
512
+ if (c === "\\" && i + 1 < text.length) {
513
+ buffer += text[i + 1];
514
+ i += 2;
515
+ continue;
516
+ }
517
+ if (c === " " && text[i + 1] === " " && text[i + 2] === "\n") {
518
+ flushBuffer();
519
+ out.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("br", {}, ctx.nextKey()));
520
+ i += 3;
521
+ continue;
522
+ }
523
+ if (c === "\n") {
524
+ buffer += " ";
525
+ i++;
526
+ continue;
527
+ }
528
+ if (c === "`") {
529
+ const close = findClosing(text, i + 1, "`");
530
+ if (close !== -1) {
531
+ flushBuffer();
532
+ out.push(
533
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
534
+ "code",
535
+ {
536
+ style: {
537
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
538
+ fontSize: "0.92em",
539
+ background: "rgba(0,0,0,0.06)",
540
+ padding: "1px 4px",
541
+ borderRadius: 3
542
+ },
543
+ children: text.slice(i + 1, close)
544
+ },
545
+ ctx.nextKey()
546
+ )
547
+ );
548
+ i = close + 1;
549
+ continue;
550
+ }
551
+ }
552
+ if ((text.startsWith("**", i) || text.startsWith("__", i)) && i + 2 < text.length) {
553
+ const marker = text.slice(i, i + 2);
554
+ const close = findClosing(text, i + 2, marker);
555
+ if (close !== -1 && close > i + 2) {
556
+ flushBuffer();
557
+ const inner = renderInlineText(text.slice(i + 2, close), ctx);
558
+ out.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: inner }, ctx.nextKey()));
559
+ i = close + 2;
560
+ continue;
561
+ }
562
+ }
563
+ if (c === "*" || c === "_") {
564
+ const prev = i === 0 ? " " : text[i - 1];
565
+ const isWordBoundaryBefore = c === "*" || !/\w/.test(prev);
566
+ if (isWordBoundaryBefore) {
567
+ const close = findClosing(text, i + 1, c);
568
+ const prevOfClose = close === -1 ? " " : close + 1 < text.length ? text[close + 1] : " ";
569
+ const isWordBoundaryAfter = c === "*" || !/\w/.test(prevOfClose);
570
+ if (close !== -1 && close > i + 1 && text[close + 1] !== c && isWordBoundaryAfter) {
571
+ flushBuffer();
572
+ const inner = renderInlineText(text.slice(i + 1, close), ctx);
573
+ out.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("em", { children: inner }, ctx.nextKey()));
574
+ i = close + 1;
575
+ continue;
576
+ }
577
+ }
578
+ }
579
+ if (c === "[") {
580
+ const closeLabel = findClosing(text, i + 1, "]");
581
+ if (closeLabel !== -1 && text[closeLabel + 1] === "(") {
582
+ const closeUrl = findClosing(text, closeLabel + 2, ")");
583
+ if (closeUrl !== -1) {
584
+ const label = text.slice(i + 1, closeLabel);
585
+ const url = text.slice(closeLabel + 2, closeUrl).trim();
586
+ if (isSafeUrl(url)) {
587
+ flushBuffer();
588
+ out.push(
589
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
590
+ "a",
591
+ {
592
+ href: url,
593
+ target: "_blank",
594
+ rel: "noopener noreferrer",
595
+ style: { color: ctx.linkColor, textDecoration: "underline" },
596
+ children: renderInlineText(label, ctx)
597
+ },
598
+ ctx.nextKey()
599
+ )
600
+ );
601
+ i = closeUrl + 1;
602
+ continue;
603
+ }
604
+ }
605
+ }
606
+ if (closeLabel !== -1 && ctx.sources && ctx.sources.length > 0) {
607
+ const body = text.slice(i + 1, closeLabel).trim();
608
+ const parts = body.split(/\s*,\s*/).filter((s) => s.length > 0);
609
+ const indices = parts.map((p) => Number(p));
610
+ const allValid = parts.length > 0 && indices.every(
611
+ (n) => Number.isInteger(n) && n >= 1 && n <= (ctx.sources?.length ?? 0)
612
+ );
613
+ if (allValid) {
614
+ flushBuffer();
615
+ out.push(
616
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("sup", { style: { whiteSpace: "nowrap" }, children: indices.map((n, idx) => {
617
+ const src = ctx.sources[n - 1];
618
+ const content = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
619
+ "span",
620
+ {
621
+ style: {
622
+ fontSize: "0.75em",
623
+ color: ctx.linkColor,
624
+ cursor: src.url ? "pointer" : "default"
625
+ },
626
+ title: src.heading ? `${src.title} \u2014 ${src.heading}` : src.title,
627
+ children: [
628
+ "[",
629
+ n,
630
+ "]"
631
+ ]
632
+ }
633
+ );
634
+ const separator = idx > 0 ? " " : null;
635
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
636
+ separator,
637
+ src.url ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
638
+ "a",
639
+ {
640
+ href: src.url,
641
+ target: "_blank",
642
+ rel: "noopener noreferrer",
643
+ style: {
644
+ color: ctx.linkColor,
645
+ textDecoration: "none"
646
+ },
647
+ children: content
648
+ }
649
+ ) : content
650
+ ] }, `${ctx.keyRoot}-cit-${idx}`);
651
+ }) }, ctx.nextKey())
652
+ );
653
+ i = closeLabel + 1;
654
+ continue;
655
+ }
656
+ }
657
+ }
658
+ buffer += c;
659
+ i++;
660
+ }
661
+ flushBuffer();
662
+ return out;
663
+ }
664
+ function isSafeUrl(url) {
665
+ if (/^https?:\/\//i.test(url)) return true;
666
+ if (/^mailto:/i.test(url)) return true;
667
+ if (/^tel:/i.test(url)) return true;
668
+ if (/^\/[^/]/.test(url) || /^#/.test(url)) return true;
669
+ return false;
670
+ }
671
+ function renderMarkdown(source, opts = {}) {
672
+ const blocks = parseBlocks(source);
673
+ const linkColor = opts.linkColor ?? "#6C3CE1";
674
+ let keyCounter = 0;
675
+ const ctx = {
676
+ sources: opts.sources,
677
+ linkColor,
678
+ keyRoot: "md",
679
+ nextKey: () => `md-${keyCounter++}`
680
+ };
681
+ const renderInline = (line) => renderInlineText(line, ctx);
682
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: blocks.map((block, idx) => {
683
+ const key = `b-${idx}`;
684
+ switch (block.kind) {
685
+ case "heading": {
686
+ const level = block.level ?? 1;
687
+ const style = {
688
+ margin: "8px 0 4px",
689
+ fontWeight: 600,
690
+ fontSize: level <= 2 ? "1.15em" : level === 3 ? "1.05em" : "1em"
691
+ };
692
+ const children = renderInline(block.lines[0] ?? "");
693
+ if (level === 1)
694
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h1", { style, children }, key);
695
+ if (level === 2)
696
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { style, children }, key);
697
+ if (level === 3)
698
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { style, children }, key);
699
+ if (level === 4)
700
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h4", { style, children }, key);
701
+ if (level === 5)
702
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h5", { style, children }, key);
703
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h6", { style, children }, key);
704
+ }
705
+ case "paragraph":
706
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { margin: "0 0 8px", lineHeight: 1.5 }, children: renderInline(block.lines.join("\n")) }, key);
707
+ case "ul":
708
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
709
+ "ul",
710
+ {
711
+ style: {
712
+ margin: "0 0 8px",
713
+ paddingLeft: 20,
714
+ lineHeight: 1.5
715
+ },
716
+ children: block.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: renderInline(l) }, i))
717
+ },
718
+ key
719
+ );
720
+ case "ol":
721
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
722
+ "ol",
723
+ {
724
+ style: {
725
+ margin: "0 0 8px",
726
+ paddingLeft: 22,
727
+ lineHeight: 1.5
728
+ },
729
+ children: block.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: renderInline(l) }, i))
730
+ },
731
+ key
732
+ );
733
+ case "code":
734
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
735
+ "pre",
736
+ {
737
+ style: {
738
+ margin: "0 0 8px",
739
+ padding: "10px 12px",
740
+ background: "rgba(0,0,0,0.06)",
741
+ borderRadius: 6,
742
+ overflowX: "auto",
743
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
744
+ fontSize: "0.88em",
745
+ lineHeight: 1.45
746
+ },
747
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { children: block.lines.join("\n") })
748
+ },
749
+ key
750
+ );
751
+ case "blockquote":
752
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
753
+ "blockquote",
754
+ {
755
+ style: {
756
+ margin: "0 0 8px",
757
+ padding: "4px 0 4px 10px",
758
+ borderLeft: "3px solid rgba(0,0,0,0.15)",
759
+ color: "rgba(0,0,0,0.75)",
760
+ fontStyle: "italic"
761
+ },
762
+ children: renderInline(block.lines.join("\n"))
763
+ },
764
+ key
765
+ );
766
+ }
767
+ }) });
768
+ }
769
+
770
+ // src/components/action-confirmation-card.tsx
771
+ var import_react6 = require("react");
772
+ var import_jsx_runtime5 = require("react/jsx-runtime");
773
+ function ActionConfirmationCard({
774
+ block,
775
+ primaryColor,
776
+ t,
777
+ onApprove,
778
+ onCancel
779
+ }) {
780
+ const [chosen, setChosen] = (0, import_react6.useState)(null);
781
+ const [showSpinner, setShowSpinner] = (0, import_react6.useState)(false);
782
+ const [error, setError] = (0, import_react6.useState)(null);
783
+ const [showSummary, setShowSummary] = (0, import_react6.useState)(false);
784
+ const handle = async (choice) => {
785
+ if (chosen) return;
786
+ setChosen(choice);
787
+ setError(null);
788
+ const spinnerTimer = window.setTimeout(() => setShowSpinner(true), 800);
789
+ try {
790
+ const fn = choice === "approve" ? onApprove : onCancel;
791
+ await fn(block.pendingToolCallId);
792
+ } catch (e) {
793
+ const msg = e instanceof Error ? e.message : t("action_failed");
794
+ setError(msg);
795
+ setChosen(null);
796
+ } finally {
797
+ window.clearTimeout(spinnerTimer);
798
+ setShowSpinner(false);
799
+ }
800
+ };
801
+ const cardStyle = {
802
+ marginTop: 6,
803
+ padding: 12,
804
+ borderRadius: 12,
805
+ border: "1px solid #e0e0e0",
806
+ background: "#fff",
807
+ display: "flex",
808
+ flexDirection: "column",
809
+ gap: 8,
810
+ fontSize: 13,
811
+ lineHeight: 1.4
812
+ };
813
+ const titleStyle = {
814
+ fontWeight: 600,
815
+ color: "#222"
816
+ };
817
+ const disclosureBtn = {
818
+ background: "none",
819
+ border: "none",
820
+ padding: 0,
821
+ color: primaryColor,
822
+ fontSize: 12,
823
+ cursor: "pointer",
824
+ textAlign: "left",
825
+ fontFamily: "inherit"
826
+ };
827
+ const summaryStyle = {
828
+ color: "#555",
829
+ background: "#fafafa",
830
+ padding: 8,
831
+ borderRadius: 8,
832
+ whiteSpace: "pre-wrap"
833
+ };
834
+ const buttonRow = {
835
+ display: "flex",
836
+ gap: 8,
837
+ marginTop: 4
838
+ };
839
+ const baseBtn = (variant) => ({
840
+ flex: 1,
841
+ padding: "8px 12px",
842
+ borderRadius: 8,
843
+ fontSize: 13,
844
+ fontWeight: 500,
845
+ cursor: chosen ? "default" : "pointer",
846
+ border: variant === "primary" ? `1px solid ${primaryColor}` : "1px solid #ddd",
847
+ background: variant === "primary" ? primaryColor : "#fff",
848
+ color: variant === "primary" ? "#fff" : "#333",
849
+ opacity: chosen && chosen !== (variant === "primary" ? "approve" : "cancel") ? 0.5 : 1,
850
+ transition: "opacity 0.15s",
851
+ display: "flex",
852
+ alignItems: "center",
853
+ justifyContent: "center",
854
+ gap: 6,
855
+ fontFamily: "inherit"
856
+ });
857
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: cardStyle, role: "group", "aria-label": block.title, children: [
858
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: titleStyle, children: block.title }),
859
+ block.summary && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
860
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
861
+ "button",
862
+ {
863
+ type: "button",
864
+ style: disclosureBtn,
865
+ onClick: () => setShowSummary((s) => !s),
866
+ "aria-expanded": showSummary,
867
+ children: [
868
+ showSummary ? "\u25BE" : "\u25B8",
869
+ " ",
870
+ t("action_what_will_happen")
871
+ ]
872
+ }
873
+ ),
874
+ showSummary && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: summaryStyle, children: block.summary })
875
+ ] }),
876
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: buttonRow, children: [
877
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
878
+ "button",
879
+ {
880
+ type: "button",
881
+ style: baseBtn("ghost"),
882
+ disabled: chosen !== null,
883
+ onClick: () => handle("cancel"),
884
+ children: chosen === "cancel" && showSpinner ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Spinner, { color: "#333" }) : t("action_cancel")
885
+ }
886
+ ),
887
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
888
+ "button",
889
+ {
890
+ type: "button",
891
+ style: baseBtn("primary"),
892
+ disabled: chosen !== null,
893
+ onClick: () => handle("approve"),
894
+ children: chosen === "approve" && showSpinner ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Spinner, { color: "#fff" }) : t("action_approve")
895
+ }
896
+ )
897
+ ] }),
898
+ error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { role: "alert", style: { color: "#b91c1c", fontSize: 12 }, children: error })
899
+ ] });
900
+ }
901
+ function Spinner({ color }) {
902
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
903
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: `@keyframes ch-spin { to { transform: rotate(360deg); } }` }),
904
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
905
+ "span",
906
+ {
907
+ "aria-hidden": "true",
908
+ style: {
909
+ width: 14,
910
+ height: 14,
911
+ borderRadius: "50%",
912
+ border: `2px solid ${color}`,
913
+ borderTopColor: "transparent",
914
+ animation: "ch-spin 0.8s linear infinite",
915
+ display: "inline-block"
916
+ }
917
+ }
918
+ )
919
+ ] });
920
+ }
921
+
922
+ // src/components/chat-messages.tsx
923
+ var import_jsx_runtime6 = require("react/jsx-runtime");
924
+ function MessageStatusPill({
925
+ status,
926
+ t
927
+ }) {
928
+ const failed = status === "failed";
929
+ const containerStyle = {
930
+ display: "flex",
931
+ alignItems: "center",
932
+ gap: 4,
933
+ marginTop: 2,
934
+ fontSize: 11,
935
+ color: failed ? "#b91c1c" : "#888",
936
+ alignSelf: "flex-end"
937
+ };
938
+ const labelKey = status === "sending" ? "status_sending" : status === "sent" ? "status_sent" : "status_failed";
939
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: containerStyle, "aria-live": "polite", children: [
940
+ status === "sending" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ClockIcon, {}),
941
+ status === "sent" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CheckIcon, {}),
942
+ status === "failed" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExclamationIcon, {}),
943
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: t(labelKey) })
944
+ ] });
945
+ }
946
+ function ClockIcon() {
947
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
948
+ "svg",
949
+ {
950
+ width: "11",
951
+ height: "11",
952
+ viewBox: "0 0 24 24",
953
+ fill: "none",
954
+ stroke: "currentColor",
955
+ strokeWidth: "2",
956
+ strokeLinecap: "round",
957
+ strokeLinejoin: "round",
958
+ "aria-hidden": "true",
959
+ children: [
960
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
961
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "12 6 12 12 16 14" })
962
+ ]
963
+ }
964
+ );
965
+ }
966
+ function CheckIcon() {
967
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
968
+ "svg",
969
+ {
970
+ width: "11",
971
+ height: "11",
972
+ viewBox: "0 0 24 24",
973
+ fill: "none",
974
+ stroke: "currentColor",
975
+ strokeWidth: "2.5",
976
+ strokeLinecap: "round",
977
+ strokeLinejoin: "round",
978
+ "aria-hidden": "true",
979
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "20 6 9 17 4 12" })
980
+ }
981
+ );
982
+ }
983
+ function ExclamationIcon() {
984
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
985
+ "svg",
986
+ {
987
+ width: "11",
988
+ height: "11",
989
+ viewBox: "0 0 24 24",
990
+ fill: "none",
991
+ stroke: "currentColor",
992
+ strokeWidth: "2",
993
+ strokeLinecap: "round",
994
+ strokeLinejoin: "round",
995
+ "aria-hidden": "true",
996
+ children: [
997
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
998
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
999
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
1000
+ ]
1001
+ }
1002
+ );
1003
+ }
393
1004
  function MessageRatingButtons({
394
1005
  messageId,
395
1006
  onRate,
@@ -397,7 +1008,7 @@ function MessageRatingButtons({
397
1008
  t,
398
1009
  reduced
399
1010
  }) {
400
- const [rated, setRated] = (0, import_react6.useState)(null);
1011
+ const [rated, setRated] = (0, import_react7.useState)(null);
401
1012
  const handleRate = (rating) => {
402
1013
  setRated(rating);
403
1014
  onRate(messageId, rating);
@@ -415,15 +1026,16 @@ function MessageRatingButtons({
415
1026
  transition: reduced ? "color 0.15s" : "all 0.15s",
416
1027
  transform: isRated && !reduced ? "scale(1.15)" : "scale(1)"
417
1028
  });
418
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: 4, marginTop: 4 }, children: [
419
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1029
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 4, marginTop: 4 }, children: [
1030
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
420
1031
  "button",
421
1032
  {
422
1033
  onClick: () => handleRate("positive"),
423
1034
  disabled: rated !== null,
424
1035
  style: buttonStyle(rated === "positive"),
425
1036
  title: t("helpful"),
426
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1037
+ "aria-label": t("helpful"),
1038
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
427
1039
  "svg",
428
1040
  {
429
1041
  width: "14",
@@ -435,21 +1047,22 @@ function MessageRatingButtons({
435
1047
  strokeLinecap: "round",
436
1048
  strokeLinejoin: "round",
437
1049
  children: [
438
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M7 10v12" }),
439
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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" })
1050
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M7 10v12" }),
1051
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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" })
440
1052
  ]
441
1053
  }
442
1054
  )
443
1055
  }
444
1056
  ),
445
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1057
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
446
1058
  "button",
447
1059
  {
448
1060
  onClick: () => handleRate("negative"),
449
1061
  disabled: rated !== null,
450
1062
  style: buttonStyle(rated === "negative"),
451
1063
  title: t("not_helpful"),
452
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1064
+ "aria-label": t("not_helpful"),
1065
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
453
1066
  "svg",
454
1067
  {
455
1068
  width: "14",
@@ -461,8 +1074,8 @@ function MessageRatingButtons({
461
1074
  strokeLinecap: "round",
462
1075
  strokeLinejoin: "round",
463
1076
  children: [
464
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M17 14V2" }),
465
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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" })
1077
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M17 14V2" }),
1078
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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" })
466
1079
  ]
467
1080
  }
468
1081
  )
@@ -476,12 +1089,14 @@ function AnimatedMessage({
476
1089
  animate,
477
1090
  reduced
478
1091
  }) {
479
- const [visible, setVisible] = (0, import_react6.useState)(!animate);
480
- (0, import_react6.useEffect)(() => {
481
- if (animate && !reduced) {
482
- const id = requestAnimationFrame(() => setVisible(true));
483
- return () => cancelAnimationFrame(id);
1092
+ const [visible, setVisible] = (0, import_react7.useState)(!animate);
1093
+ (0, import_react7.useEffect)(() => {
1094
+ if (!animate || reduced) {
1095
+ setVisible(true);
1096
+ return;
484
1097
  }
1098
+ const id = requestAnimationFrame(() => setVisible(true));
1099
+ return () => cancelAnimationFrame(id);
485
1100
  }, [animate, reduced]);
486
1101
  const style = {
487
1102
  alignSelf: isUser ? "flex-end" : "flex-start",
@@ -490,16 +1105,123 @@ function AnimatedMessage({
490
1105
  transform: visible ? "translateX(0)" : `translateX(${isUser ? "12px" : "-12px"})`,
491
1106
  transition: animate && !reduced ? "opacity 0.2s ease, transform 0.2s ease" : "none"
492
1107
  };
493
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style, children });
1108
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style, children });
1109
+ }
1110
+ function ChipRow({
1111
+ options,
1112
+ onSelect,
1113
+ primaryColor,
1114
+ reduced
1115
+ }) {
1116
+ const chip = {
1117
+ background: "none",
1118
+ border: "1px solid #e0e0e0",
1119
+ borderRadius: 20,
1120
+ padding: "6px 12px",
1121
+ fontSize: 13,
1122
+ color: "#333",
1123
+ cursor: "pointer",
1124
+ textAlign: "left",
1125
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
1126
+ transition: reduced ? "none" : "border-color 0.15s, background 0.15s"
1127
+ };
1128
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1129
+ "div",
1130
+ {
1131
+ style: {
1132
+ display: "flex",
1133
+ flexWrap: "wrap",
1134
+ gap: 6,
1135
+ marginTop: 6
1136
+ },
1137
+ children: options.map((text) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1138
+ "button",
1139
+ {
1140
+ style: chip,
1141
+ onClick: () => onSelect(text),
1142
+ onMouseEnter: (e) => {
1143
+ e.currentTarget.style.borderColor = primaryColor;
1144
+ e.currentTarget.style.background = `${primaryColor}08`;
1145
+ },
1146
+ onMouseLeave: (e) => {
1147
+ e.currentTarget.style.borderColor = "#e0e0e0";
1148
+ e.currentTarget.style.background = "none";
1149
+ },
1150
+ children: text
1151
+ },
1152
+ text
1153
+ ))
1154
+ }
1155
+ );
1156
+ }
1157
+ function BlockRenderer({
1158
+ block,
1159
+ onSend,
1160
+ onApproveAction,
1161
+ onCancelAction,
1162
+ primaryColor,
1163
+ reduced,
1164
+ t
1165
+ }) {
1166
+ switch (block.type) {
1167
+ case "quick_replies":
1168
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1169
+ ChipRow,
1170
+ {
1171
+ options: block.options,
1172
+ onSelect: onSend,
1173
+ primaryColor,
1174
+ reduced
1175
+ }
1176
+ );
1177
+ case "action_confirmation":
1178
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1179
+ ActionConfirmationCard,
1180
+ {
1181
+ block,
1182
+ primaryColor,
1183
+ t,
1184
+ onApprove: onApproveAction,
1185
+ onCancel: onCancelAction
1186
+ }
1187
+ );
1188
+ }
1189
+ }
1190
+ function StreamingCursor({ reduced }) {
1191
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1192
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: `@keyframes ch-caret-blink {
1193
+ 0%, 50% { opacity: 1; }
1194
+ 50.01%, 100% { opacity: 0; }
1195
+ }` }),
1196
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1197
+ "span",
1198
+ {
1199
+ "aria-hidden": "true",
1200
+ style: {
1201
+ display: "inline-block",
1202
+ width: 2,
1203
+ height: "1em",
1204
+ marginLeft: 2,
1205
+ verticalAlign: "text-bottom",
1206
+ background: "#777",
1207
+ animation: reduced ? "none" : "ch-caret-blink 1s step-start infinite"
1208
+ }
1209
+ }
1210
+ )
1211
+ ] });
494
1212
  }
495
1213
  function Message({
496
1214
  message,
497
1215
  config,
498
1216
  onRate,
1217
+ onSend,
1218
+ onApproveAction,
1219
+ onCancelAction,
499
1220
  hasConversation,
500
1221
  t,
501
1222
  animate,
502
- reduced
1223
+ reduced,
1224
+ showSuggestions
503
1225
  }) {
504
1226
  const isUser = message.role === "user";
505
1227
  const bubbleStyle = {
@@ -518,9 +1240,39 @@ function Message({
518
1240
  borderBottomLeftRadius: 4
519
1241
  }
520
1242
  };
521
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(AnimatedMessage, { isUser, animate, reduced, children: [
522
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: bubbleStyle, children: message.content }),
523
- message.role === "bot" && message.id && hasConversation && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1243
+ const linkColor = isUser ? "#ffffff" : config.primaryColor;
1244
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(AnimatedMessage, { isUser, animate, reduced, children: [
1245
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: bubbleStyle, children: isUser ? message.content : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1246
+ renderMarkdown(message.content, {
1247
+ sources: message.sources,
1248
+ linkColor
1249
+ }),
1250
+ message.streaming && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StreamingCursor, { reduced })
1251
+ ] }) }),
1252
+ isUser && message.status && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageStatusPill, { status: message.status, t }),
1253
+ !isUser && message.blocks?.map((block, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1254
+ BlockRenderer,
1255
+ {
1256
+ block,
1257
+ onSend,
1258
+ onApproveAction,
1259
+ onCancelAction,
1260
+ primaryColor: config.primaryColor,
1261
+ reduced,
1262
+ t
1263
+ },
1264
+ i
1265
+ )),
1266
+ !isUser && showSuggestions && message.suggestions?.length ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1267
+ ChipRow,
1268
+ {
1269
+ options: message.suggestions,
1270
+ onSelect: onSend,
1271
+ primaryColor: config.primaryColor,
1272
+ reduced
1273
+ }
1274
+ ) : null,
1275
+ message.role === "bot" && message.id && hasConversation && !message.streaming && !message.blocks?.some((b) => b.type === "action_confirmation") && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
524
1276
  MessageRatingButtons,
525
1277
  {
526
1278
  messageId: message.id,
@@ -540,12 +1292,12 @@ function TypingDots({ reduced }) {
540
1292
  background: "#999",
541
1293
  animation: reduced ? "none" : `ch-dot-pulse 1.2s ease-in-out ${delay}s infinite`
542
1294
  });
543
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
544
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `@keyframes ch-dot-pulse {
1295
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1296
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: `@keyframes ch-dot-pulse {
545
1297
  0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
546
1298
  40% { opacity: 1; transform: scale(1); }
547
1299
  }` }),
548
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1300
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
549
1301
  "div",
550
1302
  {
551
1303
  style: {
@@ -559,21 +1311,32 @@ function TypingDots({ reduced }) {
559
1311
  alignItems: "center"
560
1312
  },
561
1313
  children: [
562
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: dotStyle(0) }),
563
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: dotStyle(0.2) }),
564
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: dotStyle(0.4) })
1314
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: dotStyle(0) }),
1315
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: dotStyle(0.2) }),
1316
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: dotStyle(0.4) })
565
1317
  ]
566
1318
  }
567
1319
  )
568
1320
  ] });
569
1321
  }
570
1322
  function ChatMessages() {
571
- const { messages, isLoading, error, config, conversationId, rateMessage, t } = useChat();
1323
+ const {
1324
+ messages,
1325
+ isLoading,
1326
+ error,
1327
+ config,
1328
+ conversationId,
1329
+ rateMessage,
1330
+ sendMessage,
1331
+ approveAction,
1332
+ cancelAction,
1333
+ t
1334
+ } = useChat();
572
1335
  const reduced = useReducedMotion();
573
- const messagesEndRef = (0, import_react6.useRef)(null);
574
- const isFirstRender = (0, import_react6.useRef)(true);
575
- const prevMessageCount = (0, import_react6.useRef)(0);
576
- (0, import_react6.useEffect)(() => {
1336
+ const messagesEndRef = (0, import_react7.useRef)(null);
1337
+ const isFirstRender = (0, import_react7.useRef)(true);
1338
+ const prevMessageCount = (0, import_react7.useRef)(0);
1339
+ (0, import_react7.useEffect)(() => {
577
1340
  if (messagesEndRef.current) {
578
1341
  messagesEndRef.current.scrollIntoView({
579
1342
  behavior: isFirstRender.current || reduced ? "auto" : "smooth"
@@ -582,9 +1345,16 @@ function ChatMessages() {
582
1345
  }
583
1346
  }, [messages, isLoading, reduced]);
584
1347
  const newStartIndex = isFirstRender.current ? messages.length : prevMessageCount.current;
585
- (0, import_react6.useEffect)(() => {
1348
+ (0, import_react7.useEffect)(() => {
586
1349
  prevMessageCount.current = messages.length;
587
1350
  }, [messages.length]);
1351
+ let lastBotIndex = -1;
1352
+ for (let i = messages.length - 1; i >= 0; i--) {
1353
+ if (messages[i].role === "bot") {
1354
+ lastBotIndex = i;
1355
+ break;
1356
+ }
1357
+ }
588
1358
  const containerStyle = {
589
1359
  flex: 1,
590
1360
  overflowY: "auto",
@@ -593,24 +1363,31 @@ function ChatMessages() {
593
1363
  flexDirection: "column",
594
1364
  gap: 12
595
1365
  };
596
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: containerStyle, children: [
597
- messages.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1366
+ const lastMsg = messages[messages.length - 1];
1367
+ const waitingForFirstToken = isLoading && (lastMsg?.role !== "bot" || lastMsg.streaming !== true);
1368
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: containerStyle, children: [
1369
+ messages.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
598
1370
  Message,
599
1371
  {
600
1372
  message: msg,
601
1373
  config,
602
1374
  onRate: rateMessage,
1375
+ onSend: sendMessage,
1376
+ onApproveAction: approveAction,
1377
+ onCancelAction: cancelAction,
603
1378
  hasConversation: conversationId !== null,
604
1379
  t,
605
1380
  animate: i >= newStartIndex,
606
- reduced
1381
+ reduced,
1382
+ showSuggestions: i === lastBotIndex && !isLoading && msg.streaming !== true
607
1383
  },
608
1384
  i
609
1385
  )),
610
- isLoading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TypingDots, { reduced }),
611
- error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1386
+ waitingForFirstToken && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TypingDots, { reduced }),
1387
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
612
1388
  "div",
613
1389
  {
1390
+ role: "alert",
614
1391
  style: {
615
1392
  alignSelf: "flex-start",
616
1393
  padding: "10px 14px",
@@ -623,12 +1400,12 @@ function ChatMessages() {
623
1400
  children: error
624
1401
  }
625
1402
  ),
626
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: messagesEndRef })
1403
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: messagesEndRef })
627
1404
  ] });
628
1405
  }
629
1406
 
630
1407
  // src/components/chat-suggestions.tsx
631
- var import_jsx_runtime5 = require("react/jsx-runtime");
1408
+ var import_jsx_runtime7 = require("react/jsx-runtime");
632
1409
  function ChatSuggestions() {
633
1410
  const { messages, isLoading, config, sendMessage } = useChat();
634
1411
  const reduced = useReducedMotion();
@@ -655,7 +1432,7 @@ function ChatSuggestions() {
655
1432
  fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
656
1433
  transition: reduced ? "none" : "border-color 0.15s, background 0.15s"
657
1434
  };
658
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: containerStyle, children: config.suggestedMessages.map((text) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1435
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: containerStyle, children: config.suggestedMessages.map((text) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
659
1436
  "button",
660
1437
  {
661
1438
  style: chipStyle,
@@ -675,15 +1452,75 @@ function ChatSuggestions() {
675
1452
  }
676
1453
 
677
1454
  // src/components/chat-input.tsx
678
- var import_react7 = require("react");
679
- var import_jsx_runtime6 = require("react/jsx-runtime");
1455
+ var import_react8 = require("react");
1456
+ var import_js2 = require("@customerhero/js");
1457
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1458
+ var MAX_ATTACHMENTS = 3;
680
1459
  function ChatInput() {
681
- const { sendMessage, isLoading, config, t } = useChat();
1460
+ const { sendMessage, uploadAttachment, isLoading, config, t } = useChat();
682
1461
  const reduced = useReducedMotion();
683
- const [value, setValue] = (0, import_react7.useState)("");
1462
+ const [value, setValue] = (0, import_react8.useState)("");
1463
+ const [attachments, setAttachments] = (0, import_react8.useState)([]);
1464
+ const [captureSupported, setCaptureSupported] = (0, import_react8.useState)(false);
1465
+ (0, import_react8.useEffect)(() => {
1466
+ setCaptureSupported((0, import_js2.canCaptureScreenshot)());
1467
+ }, []);
1468
+ (0, import_react8.useEffect)(() => {
1469
+ return () => {
1470
+ for (const a of attachments) URL.revokeObjectURL(a.previewUrl);
1471
+ };
1472
+ }, []);
1473
+ const updateAttachment = (id, patch) => {
1474
+ setAttachments(
1475
+ (current) => current.map(
1476
+ (a) => a.id === id ? { ...a, ...patch } : a
1477
+ )
1478
+ );
1479
+ };
1480
+ const startUpload = async (blob) => {
1481
+ if (attachments.length >= MAX_ATTACHMENTS) return;
1482
+ const id = `att_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1483
+ const previewUrl = URL.createObjectURL(blob);
1484
+ setAttachments((current) => [
1485
+ ...current,
1486
+ { id, status: "uploading", previewUrl, blob }
1487
+ ]);
1488
+ try {
1489
+ const { attachmentToken } = await uploadAttachment(blob);
1490
+ updateAttachment(id, {
1491
+ status: "ready",
1492
+ token: attachmentToken
1493
+ });
1494
+ } catch {
1495
+ updateAttachment(id, { status: "error" });
1496
+ }
1497
+ };
1498
+ const handleCapture = async () => {
1499
+ try {
1500
+ const blob = await (0, import_js2.captureScreenshot)();
1501
+ await startUpload(blob);
1502
+ } catch (e) {
1503
+ if (e instanceof import_js2.ScreenshotCancelled) return;
1504
+ }
1505
+ };
1506
+ const handleRemove = (id) => {
1507
+ setAttachments((current) => {
1508
+ const target = current.find((a) => a.id === id);
1509
+ if (target) URL.revokeObjectURL(target.previewUrl);
1510
+ return current.filter((a) => a.id !== id);
1511
+ });
1512
+ };
1513
+ const readyTokens = attachments.filter(
1514
+ (a) => a.status === "ready"
1515
+ ).map((a) => a.token);
684
1516
  const handleSend = () => {
685
1517
  if (!value.trim() || isLoading) return;
686
- sendMessage(value);
1518
+ sendMessage(
1519
+ value,
1520
+ readyTokens.length > 0 ? { attachmentTokens: readyTokens } : void 0
1521
+ );
1522
+ for (const a of attachments) URL.revokeObjectURL(a.previewUrl);
1523
+ setAttachments([]);
687
1524
  setValue("");
688
1525
  };
689
1526
  const handleKeyDown = (e) => {
@@ -695,6 +1532,11 @@ function ChatInput() {
695
1532
  const containerStyle = {
696
1533
  padding: "12px 16px",
697
1534
  borderTop: "1px solid #eee",
1535
+ display: "flex",
1536
+ flexDirection: "column",
1537
+ gap: 8
1538
+ };
1539
+ const rowStyle = {
698
1540
  display: "flex",
699
1541
  alignItems: "center",
700
1542
  gap: 8
@@ -710,7 +1552,7 @@ function ChatInput() {
710
1552
  color: config.textColor,
711
1553
  fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
712
1554
  };
713
- const buttonStyle = {
1555
+ const sendButtonStyle = {
714
1556
  width: 36,
715
1557
  height: 36,
716
1558
  borderRadius: "50%",
@@ -726,57 +1568,210 @@ function ChatInput() {
726
1568
  flexShrink: 0,
727
1569
  transform: "scale(1)"
728
1570
  };
729
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: containerStyle, children: [
730
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
731
- "input",
1571
+ const iconButtonStyle = (disabled) => ({
1572
+ width: 36,
1573
+ height: 36,
1574
+ borderRadius: "50%",
1575
+ background: "transparent",
1576
+ border: "none",
1577
+ color: disabled ? "#ccc" : "#666",
1578
+ cursor: disabled ? "not-allowed" : "pointer",
1579
+ display: "flex",
1580
+ alignItems: "center",
1581
+ justifyContent: "center",
1582
+ flexShrink: 0,
1583
+ padding: 0
1584
+ });
1585
+ const captureDisabled = attachments.length >= MAX_ATTACHMENTS || isLoading;
1586
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: containerStyle, children: [
1587
+ attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1588
+ "div",
732
1589
  {
733
- type: "text",
734
- value,
735
- onChange: (e) => setValue(e.target.value),
736
- onKeyDown: handleKeyDown,
737
- placeholder: config.placeholderText,
738
- style: inputStyle,
739
- disabled: isLoading
1590
+ style: { display: "flex", gap: 8, flexWrap: "wrap" },
1591
+ "aria-label": "Attachments",
1592
+ children: attachments.map((a) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1593
+ Thumbnail,
1594
+ {
1595
+ attachment: a,
1596
+ onRemove: () => handleRemove(a.id),
1597
+ t
1598
+ },
1599
+ a.id
1600
+ ))
740
1601
  }
741
1602
  ),
742
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1603
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: rowStyle, children: [
1604
+ captureSupported && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1605
+ "button",
1606
+ {
1607
+ type: "button",
1608
+ onClick: handleCapture,
1609
+ disabled: captureDisabled,
1610
+ style: iconButtonStyle(captureDisabled),
1611
+ "aria-label": t("screenshot_capture"),
1612
+ title: t("screenshot_capture"),
1613
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CameraIcon, {})
1614
+ }
1615
+ ),
1616
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1617
+ "input",
1618
+ {
1619
+ type: "text",
1620
+ value,
1621
+ onChange: (e) => setValue(e.target.value),
1622
+ onKeyDown: handleKeyDown,
1623
+ placeholder: config.placeholderText,
1624
+ style: inputStyle,
1625
+ disabled: isLoading
1626
+ }
1627
+ ),
1628
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1629
+ "button",
1630
+ {
1631
+ onClick: handleSend,
1632
+ disabled: isLoading || !value.trim(),
1633
+ style: sendButtonStyle,
1634
+ "aria-label": t("send_message"),
1635
+ onMouseEnter: (e) => {
1636
+ if (!reduced && !isLoading)
1637
+ e.currentTarget.style.transform = "scale(1.1)";
1638
+ },
1639
+ onMouseLeave: (e) => {
1640
+ if (!reduced) e.currentTarget.style.transform = "scale(1)";
1641
+ },
1642
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1643
+ "svg",
1644
+ {
1645
+ width: "16",
1646
+ height: "16",
1647
+ viewBox: "0 0 24 24",
1648
+ fill: "none",
1649
+ stroke: "currentColor",
1650
+ strokeWidth: "2",
1651
+ strokeLinecap: "round",
1652
+ strokeLinejoin: "round",
1653
+ children: [
1654
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
1655
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
1656
+ ]
1657
+ }
1658
+ )
1659
+ }
1660
+ )
1661
+ ] })
1662
+ ] });
1663
+ }
1664
+ function Thumbnail({
1665
+ attachment,
1666
+ onRemove,
1667
+ t
1668
+ }) {
1669
+ const { status, previewUrl } = attachment;
1670
+ const wrap = {
1671
+ position: "relative",
1672
+ width: 56,
1673
+ height: 56,
1674
+ borderRadius: 8,
1675
+ overflow: "hidden",
1676
+ border: status === "error" ? "2px solid #b91c1c" : "1px solid #e0e0e0",
1677
+ background: "#f5f5f5"
1678
+ };
1679
+ const img = {
1680
+ width: "100%",
1681
+ height: "100%",
1682
+ objectFit: "cover"
1683
+ };
1684
+ const removeBtn = {
1685
+ position: "absolute",
1686
+ top: 2,
1687
+ right: 2,
1688
+ width: 18,
1689
+ height: 18,
1690
+ borderRadius: "50%",
1691
+ border: "none",
1692
+ background: "rgba(0,0,0,0.6)",
1693
+ color: "white",
1694
+ fontSize: 11,
1695
+ lineHeight: "18px",
1696
+ padding: 0,
1697
+ cursor: "pointer",
1698
+ textAlign: "center"
1699
+ };
1700
+ const overlay = {
1701
+ position: "absolute",
1702
+ inset: 0,
1703
+ background: "rgba(255,255,255,0.7)",
1704
+ display: "flex",
1705
+ alignItems: "center",
1706
+ justifyContent: "center"
1707
+ };
1708
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: wrap, children: [
1709
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: previewUrl, alt: "", style: img }),
1710
+ status === "uploading" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: overlay, "aria-label": "Uploading", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Spinner2, {}) }),
1711
+ status === "error" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1712
+ "div",
1713
+ {
1714
+ style: { ...overlay, background: "rgba(255,255,255,0.85)" },
1715
+ "aria-label": t("status_failed"),
1716
+ title: t("status_failed"),
1717
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { color: "#b91c1c", fontSize: 11, fontWeight: 600 }, children: "!" })
1718
+ }
1719
+ ),
1720
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
743
1721
  "button",
744
1722
  {
745
- onClick: handleSend,
746
- disabled: isLoading || !value.trim(),
747
- style: buttonStyle,
748
- "aria-label": t("send_message"),
749
- onMouseEnter: (e) => {
750
- if (!reduced && !isLoading)
751
- e.currentTarget.style.transform = "scale(1.1)";
752
- },
753
- onMouseLeave: (e) => {
754
- if (!reduced) e.currentTarget.style.transform = "scale(1)";
755
- },
756
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
757
- "svg",
758
- {
759
- width: "16",
760
- height: "16",
761
- viewBox: "0 0 24 24",
762
- fill: "none",
763
- stroke: "currentColor",
764
- strokeWidth: "2",
765
- strokeLinecap: "round",
766
- strokeLinejoin: "round",
767
- children: [
768
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
769
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
770
- ]
771
- }
772
- )
1723
+ type: "button",
1724
+ style: removeBtn,
1725
+ onClick: onRemove,
1726
+ "aria-label": t("attachment_remove"),
1727
+ children: "\xD7"
1728
+ }
1729
+ )
1730
+ ] });
1731
+ }
1732
+ function CameraIcon() {
1733
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1734
+ "svg",
1735
+ {
1736
+ width: "20",
1737
+ height: "20",
1738
+ viewBox: "0 0 24 24",
1739
+ fill: "none",
1740
+ stroke: "currentColor",
1741
+ strokeWidth: "2",
1742
+ strokeLinecap: "round",
1743
+ strokeLinejoin: "round",
1744
+ "aria-hidden": "true",
1745
+ children: [
1746
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("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" }),
1747
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "12", cy: "13", r: "4" })
1748
+ ]
1749
+ }
1750
+ );
1751
+ }
1752
+ function Spinner2() {
1753
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1754
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("style", { children: `@keyframes ch-att-spin { to { transform: rotate(360deg); } }` }),
1755
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1756
+ "span",
1757
+ {
1758
+ "aria-hidden": "true",
1759
+ style: {
1760
+ width: 16,
1761
+ height: 16,
1762
+ borderRadius: "50%",
1763
+ border: "2px solid #888",
1764
+ borderTopColor: "transparent",
1765
+ animation: "ch-att-spin 0.8s linear infinite",
1766
+ display: "inline-block"
1767
+ }
773
1768
  }
774
1769
  )
775
1770
  ] });
776
1771
  }
777
1772
 
778
1773
  // src/components/chat-window.tsx
779
- var import_jsx_runtime7 = require("react/jsx-runtime");
1774
+ var import_jsx_runtime9 = require("react/jsx-runtime");
780
1775
  function ConfigError({ message, title }) {
781
1776
  const errorStyle = {
782
1777
  flex: 1,
@@ -788,8 +1783,8 @@ function ConfigError({ message, title }) {
788
1783
  textAlign: "center",
789
1784
  gap: 8
790
1785
  };
791
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: errorStyle, children: [
792
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1786
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: errorStyle, children: [
1787
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
793
1788
  "svg",
794
1789
  {
795
1790
  width: "32",
@@ -801,22 +1796,22 @@ function ConfigError({ message, title }) {
801
1796
  strokeLinecap: "round",
802
1797
  strokeLinejoin: "round",
803
1798
  children: [
804
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
805
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
806
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
1799
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1800
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
1801
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
807
1802
  ]
808
1803
  }
809
1804
  ),
810
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: { fontSize: 14, fontWeight: 500, color: "#333", margin: 0 }, children: title }),
811
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: { fontSize: 12, color: "#999", margin: 0 }, children: message })
1805
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { fontSize: 14, fontWeight: 500, color: "#333", margin: 0 }, children: title }),
1806
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { fontSize: 12, color: "#999", margin: 0 }, children: message })
812
1807
  ] });
813
1808
  }
814
1809
  function ChatWindow() {
815
- const { isOpen, config, configError, t } = useChat();
1810
+ const { isOpen, config, configError, t, isRtl } = useChat();
816
1811
  const reduced = useReducedMotion();
817
- const [visible, setVisible] = (0, import_react8.useState)(false);
818
- const [shouldRender, setShouldRender] = (0, import_react8.useState)(false);
819
- (0, import_react8.useEffect)(() => {
1812
+ const [visible, setVisible] = (0, import_react9.useState)(false);
1813
+ const [shouldRender, setShouldRender] = (0, import_react9.useState)(false);
1814
+ (0, import_react9.useEffect)(() => {
820
1815
  if (isOpen) {
821
1816
  setShouldRender(true);
822
1817
  requestAnimationFrame(() => {
@@ -833,10 +1828,11 @@ function ChatWindow() {
833
1828
  }
834
1829
  }, [isOpen, reduced]);
835
1830
  if (!shouldRender) return null;
1831
+ const effectivePosition = isRtl ? config.position === "bottom-right" ? "bottom-left" : "bottom-right" : config.position;
836
1832
  const style = {
837
1833
  position: "fixed",
838
1834
  bottom: 90,
839
- [config.position === "bottom-left" ? "left" : "right"]: 20,
1835
+ [effectivePosition === "bottom-left" ? "left" : "right"]: 20,
840
1836
  width: 380,
841
1837
  maxWidth: "calc(100vw - 40px)",
842
1838
  height: 520,
@@ -864,17 +1860,17 @@ function ChatWindow() {
864
1860
  textDecoration: "underline",
865
1861
  textUnderlineOffset: 2
866
1862
  };
867
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style, children: [
868
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChatHeader, {}),
869
- configError ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ConfigError, { title: t("unable_to_load"), message: configError }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
870
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChatMessages, {}),
871
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChatSuggestions, {}),
872
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChatInput, {})
1863
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style, dir: isRtl ? "rtl" : "ltr", children: [
1864
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatHeader, {}),
1865
+ configError ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ConfigError, { title: t("unable_to_load"), message: configError }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1866
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatMessages, {}),
1867
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatSuggestions, {}),
1868
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatInput, {})
873
1869
  ] }),
874
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: poweredStyle, children: [
1870
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: poweredStyle, children: [
875
1871
  t("powered_by"),
876
1872
  " ",
877
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1873
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
878
1874
  "a",
879
1875
  {
880
1876
  href: "https://customerhero.app",
@@ -889,11 +1885,11 @@ function ChatWindow() {
889
1885
  }
890
1886
 
891
1887
  // src/components/chat-widget.tsx
892
- var import_jsx_runtime8 = require("react/jsx-runtime");
1888
+ var import_jsx_runtime10 = require("react/jsx-runtime");
893
1889
  function ChatWidgetInner({ identity }) {
894
1890
  const client = useCustomerHeroClient();
895
- const prevIdentityRef = (0, import_react9.useRef)(void 0);
896
- (0, import_react9.useEffect)(() => {
1891
+ const prevIdentityRef = (0, import_react10.useRef)(void 0);
1892
+ (0, import_react10.useEffect)(() => {
897
1893
  const key = identity ? JSON.stringify(identity) : void 0;
898
1894
  if (key !== prevIdentityRef.current) {
899
1895
  prevIdentityRef.current = key;
@@ -902,15 +1898,17 @@ function ChatWidgetInner({ identity }) {
902
1898
  }
903
1899
  }
904
1900
  }, [identity, client]);
905
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
906
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChatBubble, {}),
907
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChatWindow, {})
1901
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1902
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatBubble, {}),
1903
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatWindow, {})
908
1904
  ] });
909
1905
  }
910
1906
  function ChatWidget({ identity, ...config }) {
911
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CustomerHeroProvider, { ...config, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChatWidgetInner, { identity }) });
1907
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CustomerHeroProvider, { ...config, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatWidgetInner, { identity }) });
912
1908
  }
913
1909
  // Annotate the CommonJS export names for ESM import in node:
914
1910
  0 && (module.exports = {
915
- ChatWidget
1911
+ ActionConfirmationCard,
1912
+ ChatWidget,
1913
+ useChat
916
1914
  });