@customerhero/react 0.0.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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,8 +1089,8 @@ function AnimatedMessage({
476
1089
  animate,
477
1090
  reduced
478
1091
  }) {
479
- const [visible, setVisible] = (0, import_react6.useState)(!animate);
480
- (0, import_react6.useEffect)(() => {
1092
+ const [visible, setVisible] = (0, import_react7.useState)(!animate);
1093
+ (0, import_react7.useEffect)(() => {
481
1094
  if (animate && !reduced) {
482
1095
  const id = requestAnimationFrame(() => setVisible(true));
483
1096
  return () => cancelAnimationFrame(id);
@@ -490,16 +1103,123 @@ function AnimatedMessage({
490
1103
  transform: visible ? "translateX(0)" : `translateX(${isUser ? "12px" : "-12px"})`,
491
1104
  transition: animate && !reduced ? "opacity 0.2s ease, transform 0.2s ease" : "none"
492
1105
  };
493
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style, children });
1106
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style, children });
1107
+ }
1108
+ function ChipRow({
1109
+ options,
1110
+ onSelect,
1111
+ primaryColor,
1112
+ reduced
1113
+ }) {
1114
+ const chip = {
1115
+ background: "none",
1116
+ border: "1px solid #e0e0e0",
1117
+ borderRadius: 20,
1118
+ padding: "6px 12px",
1119
+ fontSize: 13,
1120
+ color: "#333",
1121
+ cursor: "pointer",
1122
+ textAlign: "left",
1123
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
1124
+ transition: reduced ? "none" : "border-color 0.15s, background 0.15s"
1125
+ };
1126
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1127
+ "div",
1128
+ {
1129
+ style: {
1130
+ display: "flex",
1131
+ flexWrap: "wrap",
1132
+ gap: 6,
1133
+ marginTop: 6
1134
+ },
1135
+ children: options.map((text) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1136
+ "button",
1137
+ {
1138
+ style: chip,
1139
+ onClick: () => onSelect(text),
1140
+ onMouseEnter: (e) => {
1141
+ e.currentTarget.style.borderColor = primaryColor;
1142
+ e.currentTarget.style.background = `${primaryColor}08`;
1143
+ },
1144
+ onMouseLeave: (e) => {
1145
+ e.currentTarget.style.borderColor = "#e0e0e0";
1146
+ e.currentTarget.style.background = "none";
1147
+ },
1148
+ children: text
1149
+ },
1150
+ text
1151
+ ))
1152
+ }
1153
+ );
1154
+ }
1155
+ function BlockRenderer({
1156
+ block,
1157
+ onSend,
1158
+ onApproveAction,
1159
+ onCancelAction,
1160
+ primaryColor,
1161
+ reduced,
1162
+ t
1163
+ }) {
1164
+ switch (block.type) {
1165
+ case "quick_replies":
1166
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1167
+ ChipRow,
1168
+ {
1169
+ options: block.options,
1170
+ onSelect: onSend,
1171
+ primaryColor,
1172
+ reduced
1173
+ }
1174
+ );
1175
+ case "action_confirmation":
1176
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1177
+ ActionConfirmationCard,
1178
+ {
1179
+ block,
1180
+ primaryColor,
1181
+ t,
1182
+ onApprove: onApproveAction,
1183
+ onCancel: onCancelAction
1184
+ }
1185
+ );
1186
+ }
1187
+ }
1188
+ function StreamingCursor({ reduced }) {
1189
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1190
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: `@keyframes ch-caret-blink {
1191
+ 0%, 50% { opacity: 1; }
1192
+ 50.01%, 100% { opacity: 0; }
1193
+ }` }),
1194
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1195
+ "span",
1196
+ {
1197
+ "aria-hidden": "true",
1198
+ style: {
1199
+ display: "inline-block",
1200
+ width: 2,
1201
+ height: "1em",
1202
+ marginLeft: 2,
1203
+ verticalAlign: "text-bottom",
1204
+ background: "#777",
1205
+ animation: reduced ? "none" : "ch-caret-blink 1s step-start infinite"
1206
+ }
1207
+ }
1208
+ )
1209
+ ] });
494
1210
  }
495
1211
  function Message({
496
1212
  message,
497
1213
  config,
498
1214
  onRate,
1215
+ onSend,
1216
+ onApproveAction,
1217
+ onCancelAction,
499
1218
  hasConversation,
500
1219
  t,
501
1220
  animate,
502
- reduced
1221
+ reduced,
1222
+ showSuggestions
503
1223
  }) {
504
1224
  const isUser = message.role === "user";
505
1225
  const bubbleStyle = {
@@ -518,9 +1238,39 @@ function Message({
518
1238
  borderBottomLeftRadius: 4
519
1239
  }
520
1240
  };
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)(
1241
+ const linkColor = isUser ? "#ffffff" : config.primaryColor;
1242
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(AnimatedMessage, { isUser, animate, reduced, children: [
1243
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: bubbleStyle, children: isUser ? message.content : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1244
+ renderMarkdown(message.content, {
1245
+ sources: message.sources,
1246
+ linkColor
1247
+ }),
1248
+ message.streaming && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StreamingCursor, { reduced })
1249
+ ] }) }),
1250
+ isUser && message.status && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageStatusPill, { status: message.status, t }),
1251
+ !isUser && message.blocks?.map((block, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1252
+ BlockRenderer,
1253
+ {
1254
+ block,
1255
+ onSend,
1256
+ onApproveAction,
1257
+ onCancelAction,
1258
+ primaryColor: config.primaryColor,
1259
+ reduced,
1260
+ t
1261
+ },
1262
+ i
1263
+ )),
1264
+ !isUser && showSuggestions && message.suggestions?.length ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1265
+ ChipRow,
1266
+ {
1267
+ options: message.suggestions,
1268
+ onSelect: onSend,
1269
+ primaryColor: config.primaryColor,
1270
+ reduced
1271
+ }
1272
+ ) : null,
1273
+ message.role === "bot" && message.id && hasConversation && !message.streaming && !message.blocks?.some((b) => b.type === "action_confirmation") && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
524
1274
  MessageRatingButtons,
525
1275
  {
526
1276
  messageId: message.id,
@@ -540,12 +1290,12 @@ function TypingDots({ reduced }) {
540
1290
  background: "#999",
541
1291
  animation: reduced ? "none" : `ch-dot-pulse 1.2s ease-in-out ${delay}s infinite`
542
1292
  });
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 {
1293
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1294
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: `@keyframes ch-dot-pulse {
545
1295
  0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
546
1296
  40% { opacity: 1; transform: scale(1); }
547
1297
  }` }),
548
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1298
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
549
1299
  "div",
550
1300
  {
551
1301
  style: {
@@ -559,21 +1309,32 @@ function TypingDots({ reduced }) {
559
1309
  alignItems: "center"
560
1310
  },
561
1311
  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) })
1312
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: dotStyle(0) }),
1313
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: dotStyle(0.2) }),
1314
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: dotStyle(0.4) })
565
1315
  ]
566
1316
  }
567
1317
  )
568
1318
  ] });
569
1319
  }
570
1320
  function ChatMessages() {
571
- const { messages, isLoading, error, config, conversationId, rateMessage, t } = useChat();
1321
+ const {
1322
+ messages,
1323
+ isLoading,
1324
+ error,
1325
+ config,
1326
+ conversationId,
1327
+ rateMessage,
1328
+ sendMessage,
1329
+ approveAction,
1330
+ cancelAction,
1331
+ t
1332
+ } = useChat();
572
1333
  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)(() => {
1334
+ const messagesEndRef = (0, import_react7.useRef)(null);
1335
+ const isFirstRender = (0, import_react7.useRef)(true);
1336
+ const prevMessageCount = (0, import_react7.useRef)(0);
1337
+ (0, import_react7.useEffect)(() => {
577
1338
  if (messagesEndRef.current) {
578
1339
  messagesEndRef.current.scrollIntoView({
579
1340
  behavior: isFirstRender.current || reduced ? "auto" : "smooth"
@@ -582,9 +1343,16 @@ function ChatMessages() {
582
1343
  }
583
1344
  }, [messages, isLoading, reduced]);
584
1345
  const newStartIndex = isFirstRender.current ? messages.length : prevMessageCount.current;
585
- (0, import_react6.useEffect)(() => {
1346
+ (0, import_react7.useEffect)(() => {
586
1347
  prevMessageCount.current = messages.length;
587
1348
  }, [messages.length]);
1349
+ let lastBotIndex = -1;
1350
+ for (let i = messages.length - 1; i >= 0; i--) {
1351
+ if (messages[i].role === "bot") {
1352
+ lastBotIndex = i;
1353
+ break;
1354
+ }
1355
+ }
588
1356
  const containerStyle = {
589
1357
  flex: 1,
590
1358
  overflowY: "auto",
@@ -593,24 +1361,31 @@ function ChatMessages() {
593
1361
  flexDirection: "column",
594
1362
  gap: 12
595
1363
  };
596
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: containerStyle, children: [
597
- messages.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1364
+ const lastMsg = messages[messages.length - 1];
1365
+ const waitingForFirstToken = isLoading && (lastMsg?.role !== "bot" || lastMsg.streaming !== true);
1366
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: containerStyle, children: [
1367
+ messages.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
598
1368
  Message,
599
1369
  {
600
1370
  message: msg,
601
1371
  config,
602
1372
  onRate: rateMessage,
1373
+ onSend: sendMessage,
1374
+ onApproveAction: approveAction,
1375
+ onCancelAction: cancelAction,
603
1376
  hasConversation: conversationId !== null,
604
1377
  t,
605
1378
  animate: i >= newStartIndex,
606
- reduced
1379
+ reduced,
1380
+ showSuggestions: i === lastBotIndex && !isLoading && msg.streaming !== true
607
1381
  },
608
1382
  i
609
1383
  )),
610
- isLoading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TypingDots, { reduced }),
611
- error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1384
+ waitingForFirstToken && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TypingDots, { reduced }),
1385
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
612
1386
  "div",
613
1387
  {
1388
+ role: "alert",
614
1389
  style: {
615
1390
  alignSelf: "flex-start",
616
1391
  padding: "10px 14px",
@@ -623,12 +1398,12 @@ function ChatMessages() {
623
1398
  children: error
624
1399
  }
625
1400
  ),
626
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: messagesEndRef })
1401
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: messagesEndRef })
627
1402
  ] });
628
1403
  }
629
1404
 
630
1405
  // src/components/chat-suggestions.tsx
631
- var import_jsx_runtime5 = require("react/jsx-runtime");
1406
+ var import_jsx_runtime7 = require("react/jsx-runtime");
632
1407
  function ChatSuggestions() {
633
1408
  const { messages, isLoading, config, sendMessage } = useChat();
634
1409
  const reduced = useReducedMotion();
@@ -655,7 +1430,7 @@ function ChatSuggestions() {
655
1430
  fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
656
1431
  transition: reduced ? "none" : "border-color 0.15s, background 0.15s"
657
1432
  };
658
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: containerStyle, children: config.suggestedMessages.map((text) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1433
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: containerStyle, children: config.suggestedMessages.map((text) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
659
1434
  "button",
660
1435
  {
661
1436
  style: chipStyle,
@@ -675,15 +1450,68 @@ function ChatSuggestions() {
675
1450
  }
676
1451
 
677
1452
  // src/components/chat-input.tsx
678
- var import_react7 = require("react");
679
- var import_jsx_runtime6 = require("react/jsx-runtime");
1453
+ var import_react8 = require("react");
1454
+ var import_js2 = require("@customerhero/js");
1455
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1456
+ var MAX_ATTACHMENTS = 3;
680
1457
  function ChatInput() {
681
- const { sendMessage, isLoading, config, t } = useChat();
1458
+ const { sendMessage, uploadAttachment, isLoading, config, t } = useChat();
682
1459
  const reduced = useReducedMotion();
683
- const [value, setValue] = (0, import_react7.useState)("");
1460
+ const [value, setValue] = (0, import_react8.useState)("");
1461
+ const [attachments, setAttachments] = (0, import_react8.useState)([]);
1462
+ const [captureSupported, setCaptureSupported] = (0, import_react8.useState)(false);
1463
+ (0, import_react8.useEffect)(() => {
1464
+ setCaptureSupported((0, import_js2.canCaptureScreenshot)());
1465
+ }, []);
1466
+ (0, import_react8.useEffect)(() => {
1467
+ return () => {
1468
+ for (const a of attachments) URL.revokeObjectURL(a.previewUrl);
1469
+ };
1470
+ }, []);
1471
+ const updateAttachment = (id, patch) => {
1472
+ setAttachments(
1473
+ (current) => current.map((a) => a.id === id ? { ...a, ...patch } : a)
1474
+ );
1475
+ };
1476
+ const startUpload = async (blob) => {
1477
+ if (attachments.length >= MAX_ATTACHMENTS) return;
1478
+ const id = `att_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1479
+ const previewUrl = URL.createObjectURL(blob);
1480
+ setAttachments((current) => [
1481
+ ...current,
1482
+ { id, status: "uploading", previewUrl, blob }
1483
+ ]);
1484
+ try {
1485
+ const { attachmentToken } = await uploadAttachment(blob);
1486
+ updateAttachment(id, {
1487
+ status: "ready",
1488
+ token: attachmentToken
1489
+ });
1490
+ } catch {
1491
+ updateAttachment(id, { status: "error" });
1492
+ }
1493
+ };
1494
+ const handleCapture = async () => {
1495
+ try {
1496
+ const blob = await (0, import_js2.captureScreenshot)();
1497
+ await startUpload(blob);
1498
+ } catch (e) {
1499
+ if (e instanceof import_js2.ScreenshotCancelled) return;
1500
+ }
1501
+ };
1502
+ const handleRemove = (id) => {
1503
+ setAttachments((current) => {
1504
+ const target = current.find((a) => a.id === id);
1505
+ if (target) URL.revokeObjectURL(target.previewUrl);
1506
+ return current.filter((a) => a.id !== id);
1507
+ });
1508
+ };
1509
+ const readyTokens = attachments.filter((a) => a.status === "ready").map((a) => a.token);
684
1510
  const handleSend = () => {
685
1511
  if (!value.trim() || isLoading) return;
686
- sendMessage(value);
1512
+ sendMessage(value, readyTokens.length > 0 ? { attachmentTokens: readyTokens } : void 0);
1513
+ for (const a of attachments) URL.revokeObjectURL(a.previewUrl);
1514
+ setAttachments([]);
687
1515
  setValue("");
688
1516
  };
689
1517
  const handleKeyDown = (e) => {
@@ -695,6 +1523,11 @@ function ChatInput() {
695
1523
  const containerStyle = {
696
1524
  padding: "12px 16px",
697
1525
  borderTop: "1px solid #eee",
1526
+ display: "flex",
1527
+ flexDirection: "column",
1528
+ gap: 8
1529
+ };
1530
+ const rowStyle = {
698
1531
  display: "flex",
699
1532
  alignItems: "center",
700
1533
  gap: 8
@@ -710,7 +1543,7 @@ function ChatInput() {
710
1543
  color: config.textColor,
711
1544
  fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
712
1545
  };
713
- const buttonStyle = {
1546
+ const sendButtonStyle = {
714
1547
  width: 36,
715
1548
  height: 36,
716
1549
  borderRadius: "50%",
@@ -726,57 +1559,210 @@ function ChatInput() {
726
1559
  flexShrink: 0,
727
1560
  transform: "scale(1)"
728
1561
  };
729
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: containerStyle, children: [
730
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
731
- "input",
1562
+ const iconButtonStyle = (disabled) => ({
1563
+ width: 36,
1564
+ height: 36,
1565
+ borderRadius: "50%",
1566
+ background: "transparent",
1567
+ border: "none",
1568
+ color: disabled ? "#ccc" : "#666",
1569
+ cursor: disabled ? "not-allowed" : "pointer",
1570
+ display: "flex",
1571
+ alignItems: "center",
1572
+ justifyContent: "center",
1573
+ flexShrink: 0,
1574
+ padding: 0
1575
+ });
1576
+ const captureDisabled = attachments.length >= MAX_ATTACHMENTS || isLoading;
1577
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: containerStyle, children: [
1578
+ attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1579
+ "div",
732
1580
  {
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
1581
+ style: { display: "flex", gap: 8, flexWrap: "wrap" },
1582
+ "aria-label": "Attachments",
1583
+ children: attachments.map((a) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1584
+ Thumbnail,
1585
+ {
1586
+ attachment: a,
1587
+ onRemove: () => handleRemove(a.id),
1588
+ t
1589
+ },
1590
+ a.id
1591
+ ))
740
1592
  }
741
1593
  ),
742
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1594
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: rowStyle, children: [
1595
+ captureSupported && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1596
+ "button",
1597
+ {
1598
+ type: "button",
1599
+ onClick: handleCapture,
1600
+ disabled: captureDisabled,
1601
+ style: iconButtonStyle(captureDisabled),
1602
+ "aria-label": t("screenshot_capture"),
1603
+ title: t("screenshot_capture"),
1604
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CameraIcon, {})
1605
+ }
1606
+ ),
1607
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1608
+ "input",
1609
+ {
1610
+ type: "text",
1611
+ value,
1612
+ onChange: (e) => setValue(e.target.value),
1613
+ onKeyDown: handleKeyDown,
1614
+ placeholder: config.placeholderText,
1615
+ style: inputStyle,
1616
+ disabled: isLoading
1617
+ }
1618
+ ),
1619
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1620
+ "button",
1621
+ {
1622
+ onClick: handleSend,
1623
+ disabled: isLoading || !value.trim(),
1624
+ style: sendButtonStyle,
1625
+ "aria-label": t("send_message"),
1626
+ onMouseEnter: (e) => {
1627
+ if (!reduced && !isLoading)
1628
+ e.currentTarget.style.transform = "scale(1.1)";
1629
+ },
1630
+ onMouseLeave: (e) => {
1631
+ if (!reduced) e.currentTarget.style.transform = "scale(1)";
1632
+ },
1633
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1634
+ "svg",
1635
+ {
1636
+ width: "16",
1637
+ height: "16",
1638
+ viewBox: "0 0 24 24",
1639
+ fill: "none",
1640
+ stroke: "currentColor",
1641
+ strokeWidth: "2",
1642
+ strokeLinecap: "round",
1643
+ strokeLinejoin: "round",
1644
+ children: [
1645
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
1646
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
1647
+ ]
1648
+ }
1649
+ )
1650
+ }
1651
+ )
1652
+ ] })
1653
+ ] });
1654
+ }
1655
+ function Thumbnail({
1656
+ attachment,
1657
+ onRemove,
1658
+ t
1659
+ }) {
1660
+ const { status, previewUrl } = attachment;
1661
+ const wrap = {
1662
+ position: "relative",
1663
+ width: 56,
1664
+ height: 56,
1665
+ borderRadius: 8,
1666
+ overflow: "hidden",
1667
+ border: status === "error" ? "2px solid #b91c1c" : "1px solid #e0e0e0",
1668
+ background: "#f5f5f5"
1669
+ };
1670
+ const img = {
1671
+ width: "100%",
1672
+ height: "100%",
1673
+ objectFit: "cover"
1674
+ };
1675
+ const removeBtn = {
1676
+ position: "absolute",
1677
+ top: 2,
1678
+ right: 2,
1679
+ width: 18,
1680
+ height: 18,
1681
+ borderRadius: "50%",
1682
+ border: "none",
1683
+ background: "rgba(0,0,0,0.6)",
1684
+ color: "white",
1685
+ fontSize: 11,
1686
+ lineHeight: "18px",
1687
+ padding: 0,
1688
+ cursor: "pointer",
1689
+ textAlign: "center"
1690
+ };
1691
+ const overlay = {
1692
+ position: "absolute",
1693
+ inset: 0,
1694
+ background: "rgba(255,255,255,0.7)",
1695
+ display: "flex",
1696
+ alignItems: "center",
1697
+ justifyContent: "center"
1698
+ };
1699
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: wrap, children: [
1700
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: previewUrl, alt: "", style: img }),
1701
+ status === "uploading" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: overlay, "aria-label": "Uploading", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Spinner2, {}) }),
1702
+ status === "error" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1703
+ "div",
1704
+ {
1705
+ style: { ...overlay, background: "rgba(255,255,255,0.85)" },
1706
+ "aria-label": t("status_failed"),
1707
+ title: t("status_failed"),
1708
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { color: "#b91c1c", fontSize: 11, fontWeight: 600 }, children: "!" })
1709
+ }
1710
+ ),
1711
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
743
1712
  "button",
744
1713
  {
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
- )
1714
+ type: "button",
1715
+ style: removeBtn,
1716
+ onClick: onRemove,
1717
+ "aria-label": t("attachment_remove"),
1718
+ children: "\xD7"
1719
+ }
1720
+ )
1721
+ ] });
1722
+ }
1723
+ function CameraIcon() {
1724
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1725
+ "svg",
1726
+ {
1727
+ width: "20",
1728
+ height: "20",
1729
+ viewBox: "0 0 24 24",
1730
+ fill: "none",
1731
+ stroke: "currentColor",
1732
+ strokeWidth: "2",
1733
+ strokeLinecap: "round",
1734
+ strokeLinejoin: "round",
1735
+ "aria-hidden": "true",
1736
+ children: [
1737
+ /* @__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" }),
1738
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "12", cy: "13", r: "4" })
1739
+ ]
1740
+ }
1741
+ );
1742
+ }
1743
+ function Spinner2() {
1744
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1745
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("style", { children: `@keyframes ch-att-spin { to { transform: rotate(360deg); } }` }),
1746
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1747
+ "span",
1748
+ {
1749
+ "aria-hidden": "true",
1750
+ style: {
1751
+ width: 16,
1752
+ height: 16,
1753
+ borderRadius: "50%",
1754
+ border: "2px solid #888",
1755
+ borderTopColor: "transparent",
1756
+ animation: "ch-att-spin 0.8s linear infinite",
1757
+ display: "inline-block"
1758
+ }
773
1759
  }
774
1760
  )
775
1761
  ] });
776
1762
  }
777
1763
 
778
1764
  // src/components/chat-window.tsx
779
- var import_jsx_runtime7 = require("react/jsx-runtime");
1765
+ var import_jsx_runtime9 = require("react/jsx-runtime");
780
1766
  function ConfigError({ message, title }) {
781
1767
  const errorStyle = {
782
1768
  flex: 1,
@@ -788,8 +1774,8 @@ function ConfigError({ message, title }) {
788
1774
  textAlign: "center",
789
1775
  gap: 8
790
1776
  };
791
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: errorStyle, children: [
792
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1777
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: errorStyle, children: [
1778
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
793
1779
  "svg",
794
1780
  {
795
1781
  width: "32",
@@ -801,22 +1787,22 @@ function ConfigError({ message, title }) {
801
1787
  strokeLinecap: "round",
802
1788
  strokeLinejoin: "round",
803
1789
  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" })
1790
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1791
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
1792
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
807
1793
  ]
808
1794
  }
809
1795
  ),
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 })
1796
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { fontSize: 14, fontWeight: 500, color: "#333", margin: 0 }, children: title }),
1797
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { fontSize: 12, color: "#999", margin: 0 }, children: message })
812
1798
  ] });
813
1799
  }
814
1800
  function ChatWindow() {
815
- const { isOpen, config, configError, t } = useChat();
1801
+ const { isOpen, config, configError, t, isRtl } = useChat();
816
1802
  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)(() => {
1803
+ const [visible, setVisible] = (0, import_react9.useState)(false);
1804
+ const [shouldRender, setShouldRender] = (0, import_react9.useState)(false);
1805
+ (0, import_react9.useEffect)(() => {
820
1806
  if (isOpen) {
821
1807
  setShouldRender(true);
822
1808
  requestAnimationFrame(() => {
@@ -833,10 +1819,11 @@ function ChatWindow() {
833
1819
  }
834
1820
  }, [isOpen, reduced]);
835
1821
  if (!shouldRender) return null;
1822
+ const effectivePosition = isRtl ? config.position === "bottom-right" ? "bottom-left" : "bottom-right" : config.position;
836
1823
  const style = {
837
1824
  position: "fixed",
838
1825
  bottom: 90,
839
- [config.position === "bottom-left" ? "left" : "right"]: 20,
1826
+ [effectivePosition === "bottom-left" ? "left" : "right"]: 20,
840
1827
  width: 380,
841
1828
  maxWidth: "calc(100vw - 40px)",
842
1829
  height: 520,
@@ -864,17 +1851,17 @@ function ChatWindow() {
864
1851
  textDecoration: "underline",
865
1852
  textUnderlineOffset: 2
866
1853
  };
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, {})
1854
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style, dir: isRtl ? "rtl" : "ltr", children: [
1855
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatHeader, {}),
1856
+ 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: [
1857
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatMessages, {}),
1858
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatSuggestions, {}),
1859
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatInput, {})
873
1860
  ] }),
874
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: poweredStyle, children: [
1861
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: poweredStyle, children: [
875
1862
  t("powered_by"),
876
1863
  " ",
877
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1864
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
878
1865
  "a",
879
1866
  {
880
1867
  href: "https://customerhero.app",
@@ -889,11 +1876,11 @@ function ChatWindow() {
889
1876
  }
890
1877
 
891
1878
  // src/components/chat-widget.tsx
892
- var import_jsx_runtime8 = require("react/jsx-runtime");
1879
+ var import_jsx_runtime10 = require("react/jsx-runtime");
893
1880
  function ChatWidgetInner({ identity }) {
894
1881
  const client = useCustomerHeroClient();
895
- const prevIdentityRef = (0, import_react9.useRef)(void 0);
896
- (0, import_react9.useEffect)(() => {
1882
+ const prevIdentityRef = (0, import_react10.useRef)(void 0);
1883
+ (0, import_react10.useEffect)(() => {
897
1884
  const key = identity ? JSON.stringify(identity) : void 0;
898
1885
  if (key !== prevIdentityRef.current) {
899
1886
  prevIdentityRef.current = key;
@@ -902,15 +1889,17 @@ function ChatWidgetInner({ identity }) {
902
1889
  }
903
1890
  }
904
1891
  }, [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, {})
1892
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1893
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatBubble, {}),
1894
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatWindow, {})
908
1895
  ] });
909
1896
  }
910
1897
  function ChatWidget({ identity, ...config }) {
911
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CustomerHeroProvider, { ...config, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChatWidgetInner, { identity }) });
1898
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CustomerHeroProvider, { ...config, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatWidgetInner, { identity }) });
912
1899
  }
913
1900
  // Annotate the CommonJS export names for ESM import in node:
914
1901
  0 && (module.exports = {
915
- ChatWidget
1902
+ ActionConfirmationCard,
1903
+ ChatWidget,
1904
+ useChat
916
1905
  });