@copilotz/chat-ui 0.1.0 → 0.1.4

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
@@ -64,6 +64,11 @@ var defaultChatConfig = {
64
64
  title: "Chat Assistant",
65
65
  subtitle: "How can I help you today?"
66
66
  },
67
+ agentSelector: {
68
+ enabled: false,
69
+ label: "Select agent",
70
+ hideIfSingle: true
71
+ },
67
72
  labels: {
68
73
  inputPlaceholder: "Type your message...",
69
74
  sendButton: "Send",
@@ -156,6 +161,10 @@ function mergeConfig(_baseConfig, userConfig) {
156
161
  ...defaultChatConfig.ui,
157
162
  ...userConfig.ui
158
163
  },
164
+ agentSelector: {
165
+ ...defaultChatConfig.agentSelector,
166
+ ...userConfig.agentSelector
167
+ },
159
168
  customComponent: userConfig.customComponent || defaultChatConfig.customComponent,
160
169
  headerActions: userConfig.headerActions || defaultChatConfig.headerActions
161
170
  };
@@ -286,6 +295,7 @@ var configUtils = {
286
295
  var import_react = require("react");
287
296
  var import_react_markdown = __toESM(require("react-markdown"), 1);
288
297
  var import_remark_gfm = __toESM(require("remark-gfm"), 1);
298
+ var import_remark_breaks = __toESM(require("remark-breaks"), 1);
289
299
  var import_rehype_highlight = __toESM(require("rehype-highlight"), 1);
290
300
 
291
301
  // src/components/ui/button.tsx
@@ -566,7 +576,25 @@ function TooltipContent({
566
576
  // src/components/chat/Message.tsx
567
577
  var import_lucide_react = require("lucide-react");
568
578
  var import_jsx_runtime7 = require("react/jsx-runtime");
569
- var ThinkingIndicator = ({ label = "Thinking..." }) => {
579
+ var MarkdownErrorBoundary = class extends import_react.Component {
580
+ constructor(props) {
581
+ super(props);
582
+ this.state = { hasError: false };
583
+ }
584
+ static getDerivedStateFromError(_error) {
585
+ return { hasError: true };
586
+ }
587
+ componentDidCatch(error, errorInfo) {
588
+ console.warn("[Markdown] Falling back to simple rendering due to:", error.message);
589
+ }
590
+ render() {
591
+ if (this.state.hasError) {
592
+ return this.props.fallback;
593
+ }
594
+ return this.props.children;
595
+ }
596
+ };
597
+ var ThinkingIndicator = (0, import_react.memo)(function ThinkingIndicator2({ label = "Thinking..." }) {
570
598
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2 py-2", children: [
571
599
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex gap-1", children: [
572
600
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
@@ -593,36 +621,61 @@ var ThinkingIndicator = ({ label = "Thinking..." }) => {
593
621
  ] }),
594
622
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-sm text-muted-foreground animate-pulse", children: label })
595
623
  ] });
624
+ });
625
+ var markdownComponents = {
626
+ code: ({ node, className, children, ...props }) => {
627
+ const inline = props.inline;
628
+ const match = /language-(\w+)/.exec(className || "");
629
+ return !inline && match ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("pre", { className: "relative", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { className, ...props, children }) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { className: "bg-muted px-1 py-0.5 rounded text-sm", ...props, children });
630
+ }
596
631
  };
597
- var StreamingText = ({
632
+ var remarkPluginsWithGfm = [import_remark_gfm.default, import_remark_breaks.default];
633
+ var remarkPluginsSimple = [import_remark_breaks.default];
634
+ var rehypePluginsDefault = [import_rehype_highlight.default];
635
+ var rehypePluginsEmpty = [];
636
+ var SimpleMarkdown = (0, import_react.memo)(function SimpleMarkdown2({
637
+ content,
638
+ isStreaming = false
639
+ }) {
640
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
641
+ import_react_markdown.default,
642
+ {
643
+ remarkPlugins: remarkPluginsSimple,
644
+ rehypePlugins: isStreaming ? rehypePluginsEmpty : rehypePluginsDefault,
645
+ components: markdownComponents,
646
+ children: content
647
+ }
648
+ );
649
+ });
650
+ var FullMarkdown = (0, import_react.memo)(function FullMarkdown2({
651
+ content,
652
+ isStreaming = false
653
+ }) {
654
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
655
+ import_react_markdown.default,
656
+ {
657
+ remarkPlugins: remarkPluginsWithGfm,
658
+ rehypePlugins: isStreaming ? rehypePluginsEmpty : rehypePluginsDefault,
659
+ components: markdownComponents,
660
+ children: content
661
+ }
662
+ );
663
+ });
664
+ var StreamingText = (0, import_react.memo)(function StreamingText2({
598
665
  content,
599
666
  isStreaming = false,
600
667
  thinkingLabel = "Thinking..."
601
- }) => {
668
+ }) {
602
669
  const hasContent = content.trim().length > 0;
603
670
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "prose prose-sm max-w-none dark:prose-invert", children: [
604
- hasContent ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
605
- import_react_markdown.default,
606
- {
607
- remarkPlugins: [import_remark_gfm.default],
608
- rehypePlugins: isStreaming ? [] : [import_rehype_highlight.default],
609
- components: {
610
- code: ({ node, className, children, ...props }) => {
611
- const inline = props.inline;
612
- const match = /language-(\w+)/.exec(className || "");
613
- return !inline && match ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("pre", { className: "relative", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { className, ...props, children }) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { className: "bg-muted px-1 py-0.5 rounded text-sm", ...props, children });
614
- }
615
- },
616
- children: content
617
- }
618
- ) : isStreaming ? (
671
+ hasContent ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MarkdownErrorBoundary, { fallback: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SimpleMarkdown, { content, isStreaming }), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FullMarkdown, { content, isStreaming }) }) : isStreaming ? (
619
672
  // Show thinking indicator while waiting for first token
620
673
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ThinkingIndicator, { label: thinkingLabel })
621
674
  ) : null,
622
675
  isStreaming && hasContent && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "inline-block w-2 h-4 bg-primary animate-pulse ml-1" })
623
676
  ] });
624
- };
625
- var MediaRenderer = ({ attachment }) => {
677
+ });
678
+ var MediaRenderer = (0, import_react.memo)(function MediaRenderer2({ attachment }) {
626
679
  const [isPlaying, setIsPlaying] = (0, import_react.useState)(false);
627
680
  const audioRef = (0, import_react.useRef)(null);
628
681
  const videoRef = (0, import_react.useRef)(null);
@@ -696,8 +749,8 @@ var MediaRenderer = ({ attachment }) => {
696
749
  default:
697
750
  return null;
698
751
  }
699
- };
700
- var ToolCallsDisplay = ({ toolCalls, label }) => {
752
+ });
753
+ var ToolCallsDisplay = (0, import_react.memo)(function ToolCallsDisplay2({ toolCalls, label }) {
701
754
  const [expandedCall, setExpandedCall] = (0, import_react.useState)(null);
702
755
  const getStatusIcon = (status) => {
703
756
  switch (status) {
@@ -766,8 +819,48 @@ var ToolCallsDisplay = ({ toolCalls, label }) => {
766
819
  ] }, call.id);
767
820
  })
768
821
  ] });
822
+ });
823
+ var arePropsEqual = (prevProps, nextProps) => {
824
+ if (prevProps.message.id !== nextProps.message.id) return false;
825
+ if (prevProps.message.content !== nextProps.message.content) return false;
826
+ if (prevProps.message.isStreaming !== nextProps.message.isStreaming) return false;
827
+ if (prevProps.message.isComplete !== nextProps.message.isComplete) return false;
828
+ if (prevProps.message.isEdited !== nextProps.message.isEdited) return false;
829
+ if (prevProps.message.timestamp !== nextProps.message.timestamp) return false;
830
+ if (prevProps.message.toolCalls !== nextProps.message.toolCalls) {
831
+ const prevCalls = prevProps.message.toolCalls;
832
+ const nextCalls = nextProps.message.toolCalls;
833
+ if (!prevCalls || !nextCalls || prevCalls.length !== nextCalls.length) return false;
834
+ for (let i = 0; i < prevCalls.length; i++) {
835
+ if (prevCalls[i].id !== nextCalls[i].id || prevCalls[i].status !== nextCalls[i].status || prevCalls[i].result !== nextCalls[i].result) {
836
+ return false;
837
+ }
838
+ }
839
+ }
840
+ if (prevProps.message.attachments !== nextProps.message.attachments) {
841
+ const prevAtt = prevProps.message.attachments;
842
+ const nextAtt = nextProps.message.attachments;
843
+ if (!prevAtt || !nextAtt || prevAtt.length !== nextAtt.length) return false;
844
+ }
845
+ if (prevProps.isUser !== nextProps.isUser) return false;
846
+ if (prevProps.userAvatar !== nextProps.userAvatar) return false;
847
+ if (prevProps.userName !== nextProps.userName) return false;
848
+ if (prevProps.assistantName !== nextProps.assistantName) return false;
849
+ if (prevProps.showTimestamp !== nextProps.showTimestamp) return false;
850
+ if (prevProps.showAvatar !== nextProps.showAvatar) return false;
851
+ if (prevProps.enableCopy !== nextProps.enableCopy) return false;
852
+ if (prevProps.enableEdit !== nextProps.enableEdit) return false;
853
+ if (prevProps.enableRegenerate !== nextProps.enableRegenerate) return false;
854
+ if (prevProps.enableToolCallsDisplay !== nextProps.enableToolCallsDisplay) return false;
855
+ if (prevProps.compactMode !== nextProps.compactMode) return false;
856
+ if (prevProps.className !== nextProps.className) return false;
857
+ if (prevProps.toolUsedLabel !== nextProps.toolUsedLabel) return false;
858
+ if (prevProps.thinkingLabel !== nextProps.thinkingLabel) return false;
859
+ if (prevProps.isGrouped !== nextProps.isGrouped) return false;
860
+ if (prevProps.assistantAvatar !== nextProps.assistantAvatar) return false;
861
+ return true;
769
862
  };
770
- var Message = ({
863
+ var Message = (0, import_react.memo)(({
771
864
  message,
772
865
  isUser,
773
866
  userAvatar,
@@ -784,7 +877,8 @@ var Message = ({
784
877
  onAction,
785
878
  className = "",
786
879
  toolUsedLabel,
787
- thinkingLabel = "Thinking..."
880
+ thinkingLabel = "Thinking...",
881
+ isGrouped = false
788
882
  }) => {
789
883
  const [isEditing, setIsEditing] = (0, import_react.useState)(false);
790
884
  const [editContent, setEditContent] = (0, import_react.useState)(message.content);
@@ -834,7 +928,7 @@ var Message = ({
834
928
  onMouseEnter: () => setShowActions(true),
835
929
  onMouseLeave: () => setShowActions(false),
836
930
  children: [
837
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `flex gap-3 ${messageIsUser ? "flex-row-reverse" : "flex-row"} w-full mb-1`, children: [
931
+ !isGrouped && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `flex gap-3 ${messageIsUser ? "flex-row-reverse" : "flex-row"} w-full mb-1`, children: [
838
932
  showAvatar && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `flex-shrink-0 ${compactMode ? "mt-1" : "mt-0"}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Avatar, { className: compactMode ? "h-6 w-6" : "h-8 w-8", children: messageIsUser ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
839
933
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AvatarImage, { src: userAvatar, alt: userName }),
840
934
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AvatarFallback, { className: "bg-primary text-primary-foreground", children: userName.charAt(0).toUpperCase() })
@@ -845,7 +939,7 @@ var Message = ({
845
939
  message.isEdited && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Badge, { variant: "outline", className: "text-xs", children: "editado" })
846
940
  ] })
847
941
  ] }),
848
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `flex-1 min-w-0 ${messageIsUser ? "text-right" : "text-left"}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `relative inline-flex flex-col ${messageIsUser ? "rounded-lg p-3 bg-primary text-primary-foreground ml-auto max-w-[85%]" : "max-w-[85%]"}`, children: [
942
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `flex-1 min-w-0 ${messageIsUser ? "text-right" : "text-left"} ${isGrouped && showAvatar && !messageIsUser ? compactMode ? "ml-9" : "ml-11" : ""} ${isGrouped && showAvatar && messageIsUser ? compactMode ? "mr-9" : "mr-11" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `relative inline-flex flex-col ${messageIsUser ? "rounded-lg p-3 bg-primary text-primary-foreground ml-auto max-w-[85%]" : "max-w-[85%]"}`, children: [
849
943
  isEditing ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-2", children: [
850
944
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
851
945
  Textarea,
@@ -923,7 +1017,7 @@ var Message = ({
923
1017
  ]
924
1018
  }
925
1019
  ) });
926
- };
1020
+ }, arePropsEqual);
927
1021
 
928
1022
  // src/components/chat/Sidebar.tsx
929
1023
  var import_react2 = require("react");
@@ -948,7 +1042,7 @@ function Input({ className, type, ...props }) {
948
1042
  }
949
1043
 
950
1044
  // src/components/ui/sidebar.tsx
951
- var React3 = __toESM(require("react"), 1);
1045
+ var React4 = __toESM(require("react"), 1);
952
1046
  var import_react_slot3 = require("@radix-ui/react-slot");
953
1047
  var import_class_variance_authority3 = require("class-variance-authority");
954
1048
  var import_lucide_react3 = require("lucide-react");
@@ -956,18 +1050,26 @@ var import_lucide_react3 = require("lucide-react");
956
1050
  // src/hooks/use-mobile.ts
957
1051
  var React2 = __toESM(require("react"), 1);
958
1052
  var MOBILE_BREAKPOINT = 768;
1053
+ function getInitialIsMobile() {
1054
+ if (typeof window === "undefined") return false;
1055
+ return window.innerWidth < MOBILE_BREAKPOINT;
1056
+ }
959
1057
  function useIsMobile() {
960
- const [isMobile, setIsMobile] = React2.useState(void 0);
1058
+ const [isMobile, setIsMobile] = React2.useState(getInitialIsMobile);
961
1059
  React2.useEffect(() => {
962
1060
  const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
963
1061
  const onChange = () => {
964
1062
  setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
965
1063
  };
966
1064
  mql.addEventListener("change", onChange);
1065
+ window.addEventListener("resize", onChange);
967
1066
  setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
968
- return () => mql.removeEventListener("change", onChange);
1067
+ return () => {
1068
+ mql.removeEventListener("change", onChange);
1069
+ window.removeEventListener("resize", onChange);
1070
+ };
969
1071
  }, []);
970
- return !!isMobile;
1072
+ return isMobile;
971
1073
  }
972
1074
 
973
1075
  // src/components/ui/separator.tsx
@@ -995,11 +1097,30 @@ function Separator({
995
1097
  }
996
1098
 
997
1099
  // src/components/ui/sheet.tsx
1100
+ var React3 = __toESM(require("react"), 1);
998
1101
  var SheetPrimitive = __toESM(require("@radix-ui/react-dialog"), 1);
999
1102
  var import_lucide_react2 = require("lucide-react");
1000
1103
  var import_jsx_runtime10 = require("react/jsx-runtime");
1001
- function Sheet({ ...props }) {
1002
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SheetPrimitive.Root, { "data-slot": "sheet", ...props });
1104
+ function cleanupBodyStyles() {
1105
+ if (typeof document !== "undefined" && document.body.style.pointerEvents === "none") {
1106
+ document.body.style.pointerEvents = "";
1107
+ }
1108
+ }
1109
+ function Sheet({ open, onOpenChange, ...props }) {
1110
+ const prevOpenRef = React3.useRef(open);
1111
+ React3.useEffect(() => {
1112
+ if (prevOpenRef.current === true && open === false) {
1113
+ const timeout = setTimeout(cleanupBodyStyles, 350);
1114
+ return () => clearTimeout(timeout);
1115
+ }
1116
+ prevOpenRef.current = open;
1117
+ }, [open]);
1118
+ React3.useEffect(() => {
1119
+ return () => {
1120
+ cleanupBodyStyles();
1121
+ };
1122
+ }, []);
1123
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SheetPrimitive.Root, { "data-slot": "sheet", open, onOpenChange, ...props });
1003
1124
  }
1004
1125
  function SheetPortal({
1005
1126
  ...props
@@ -1015,7 +1136,10 @@ function SheetOverlay({
1015
1136
  {
1016
1137
  "data-slot": "sheet-overlay",
1017
1138
  className: cn(
1018
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
1139
+ "fixed inset-0 z-50 bg-black/50",
1140
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
1141
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
1142
+ "data-[state=closed]:pointer-events-none",
1019
1143
  className
1020
1144
  ),
1021
1145
  ...props
@@ -1034,6 +1158,7 @@ function SheetContent({
1034
1158
  SheetPrimitive.Content,
1035
1159
  {
1036
1160
  "data-slot": "sheet-content",
1161
+ "aria-describedby": void 0,
1037
1162
  className: cn(
1038
1163
  "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
1039
1164
  side === "right" && "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
@@ -1099,9 +1224,9 @@ var SIDEBAR_WIDTH = "16rem";
1099
1224
  var SIDEBAR_WIDTH_MOBILE = "18rem";
1100
1225
  var SIDEBAR_WIDTH_ICON = "3rem";
1101
1226
  var SIDEBAR_KEYBOARD_SHORTCUT = "b";
1102
- var SidebarContext = React3.createContext(null);
1227
+ var SidebarContext = React4.createContext(null);
1103
1228
  function useSidebar() {
1104
- const context = React3.useContext(SidebarContext);
1229
+ const context = React4.useContext(SidebarContext);
1105
1230
  if (!context) {
1106
1231
  throw new Error("useSidebar must be used within a SidebarProvider.");
1107
1232
  }
@@ -1117,10 +1242,10 @@ function SidebarProvider({
1117
1242
  ...props
1118
1243
  }) {
1119
1244
  const isMobile = useIsMobile();
1120
- const [openMobile, setOpenMobile] = React3.useState(false);
1121
- const [_open, _setOpen] = React3.useState(defaultOpen);
1245
+ const [openMobile, setOpenMobile] = React4.useState(false);
1246
+ const [_open, _setOpen] = React4.useState(defaultOpen);
1122
1247
  const open = openProp ?? _open;
1123
- const setOpen = React3.useCallback(
1248
+ const setOpen = React4.useCallback(
1124
1249
  (value) => {
1125
1250
  const openState = typeof value === "function" ? value(open) : value;
1126
1251
  if (setOpenProp) {
@@ -1132,10 +1257,10 @@ function SidebarProvider({
1132
1257
  },
1133
1258
  [setOpenProp, open]
1134
1259
  );
1135
- const toggleSidebar = React3.useCallback(() => {
1260
+ const toggleSidebar = React4.useCallback(() => {
1136
1261
  return isMobile ? setOpenMobile((open2) => !open2) : setOpen((open2) => !open2);
1137
1262
  }, [isMobile, setOpen, setOpenMobile]);
1138
- React3.useEffect(() => {
1263
+ React4.useEffect(() => {
1139
1264
  const handleKeyDown = (event) => {
1140
1265
  if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
1141
1266
  event.preventDefault();
@@ -1146,7 +1271,7 @@ function SidebarProvider({
1146
1271
  return () => window.removeEventListener("keydown", handleKeyDown);
1147
1272
  }, [toggleSidebar]);
1148
1273
  const state = open ? "expanded" : "collapsed";
1149
- const contextValue = React3.useMemo(
1274
+ const contextValue = React4.useMemo(
1150
1275
  () => ({
1151
1276
  state,
1152
1277
  open,
@@ -1221,12 +1346,29 @@ function Sidebar({
1221
1346
  }
1222
1347
  ) });
1223
1348
  }
1349
+ const isCollapsed = state === "collapsed";
1350
+ const currentCollapsible = isCollapsed ? collapsible : "";
1351
+ const getGapWidth = () => {
1352
+ if (currentCollapsible === "offcanvas") return "0px";
1353
+ if (currentCollapsible === "icon") return SIDEBAR_WIDTH_ICON;
1354
+ return SIDEBAR_WIDTH;
1355
+ };
1356
+ const getContainerWidth = () => {
1357
+ if (currentCollapsible === "icon") return SIDEBAR_WIDTH_ICON;
1358
+ return SIDEBAR_WIDTH;
1359
+ };
1360
+ const getContainerOffset = () => {
1361
+ if (currentCollapsible === "offcanvas") {
1362
+ return side === "left" ? `calc(${SIDEBAR_WIDTH} * -1)` : `calc(${SIDEBAR_WIDTH} * -1)`;
1363
+ }
1364
+ return "0";
1365
+ };
1224
1366
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1225
1367
  "div",
1226
1368
  {
1227
- className: "group peer text-sidebar-foreground hidden md:block",
1369
+ className: "group peer text-sidebar-foreground",
1228
1370
  "data-state": state,
1229
- "data-collapsible": state === "collapsed" ? collapsible : "",
1371
+ "data-collapsible": currentCollapsible,
1230
1372
  "data-variant": variant,
1231
1373
  "data-side": side,
1232
1374
  "data-slot": "sidebar",
@@ -1236,11 +1378,10 @@ function Sidebar({
1236
1378
  {
1237
1379
  "data-slot": "sidebar-gap",
1238
1380
  className: cn(
1239
- "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
1240
- "group-data-[collapsible=offcanvas]:w-0",
1241
- "group-data-[side=right]:rotate-180",
1242
- variant === "floating" || variant === "inset" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
1243
- )
1381
+ "relative bg-transparent transition-[width] duration-200 ease-linear",
1382
+ "group-data-[side=right]:rotate-180"
1383
+ ),
1384
+ style: { width: getGapWidth() }
1244
1385
  }
1245
1386
  ),
1246
1387
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
@@ -1248,12 +1389,16 @@ function Sidebar({
1248
1389
  {
1249
1390
  "data-slot": "sidebar-container",
1250
1391
  className: cn(
1251
- "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
1252
- side === "left" ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
1392
+ "fixed inset-y-0 z-10 h-screen transition-[left,right,width] duration-200 ease-linear",
1253
1393
  // Adjust the padding for floating and inset variants.
1254
- variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
1394
+ variant === "floating" || variant === "inset" ? "p-2" : side === "left" ? "border-r" : "border-l",
1255
1395
  className
1256
1396
  ),
1397
+ style: {
1398
+ display: "flex",
1399
+ width: getContainerWidth(),
1400
+ [side === "left" ? "left" : "right"]: getContainerOffset()
1401
+ },
1257
1402
  ...props,
1258
1403
  children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1259
1404
  "div",
@@ -1276,6 +1421,15 @@ function SidebarTrigger({
1276
1421
  ...props
1277
1422
  }) {
1278
1423
  const { toggleSidebar } = useSidebar();
1424
+ const handleActivation = React4.useCallback((event) => {
1425
+ if (event.type === "touchend") {
1426
+ event.preventDefault();
1427
+ }
1428
+ if ("onClick" in event && onClick) {
1429
+ onClick(event);
1430
+ }
1431
+ toggleSidebar();
1432
+ }, [onClick, toggleSidebar]);
1279
1433
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1280
1434
  Button,
1281
1435
  {
@@ -1284,10 +1438,8 @@ function SidebarTrigger({
1284
1438
  variant: "ghost",
1285
1439
  size: "icon",
1286
1440
  className: cn("size-7", className),
1287
- onClick: (event) => {
1288
- onClick?.(event);
1289
- toggleSidebar();
1290
- },
1441
+ onClick: handleActivation,
1442
+ onTouchEnd: handleActivation,
1291
1443
  ...props,
1292
1444
  children: [
1293
1445
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.PanelLeftIcon, {}),
@@ -1529,13 +1681,34 @@ function SidebarMenuAction({
1529
1681
  }
1530
1682
 
1531
1683
  // src/components/ui/dialog.tsx
1684
+ var React5 = __toESM(require("react"), 1);
1532
1685
  var DialogPrimitive = __toESM(require("@radix-ui/react-dialog"), 1);
1533
1686
  var import_lucide_react4 = require("lucide-react");
1534
1687
  var import_jsx_runtime12 = require("react/jsx-runtime");
1688
+ function cleanupBodyStyles2() {
1689
+ if (typeof document !== "undefined" && document.body.style.pointerEvents === "none") {
1690
+ document.body.style.pointerEvents = "";
1691
+ }
1692
+ }
1535
1693
  function Dialog({
1694
+ open,
1695
+ onOpenChange,
1536
1696
  ...props
1537
1697
  }) {
1538
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
1698
+ const prevOpenRef = React5.useRef(open);
1699
+ React5.useEffect(() => {
1700
+ if (prevOpenRef.current === true && open === false) {
1701
+ const timeout = setTimeout(cleanupBodyStyles2, 250);
1702
+ return () => clearTimeout(timeout);
1703
+ }
1704
+ prevOpenRef.current = open;
1705
+ }, [open]);
1706
+ React5.useEffect(() => {
1707
+ return () => {
1708
+ cleanupBodyStyles2();
1709
+ };
1710
+ }, []);
1711
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DialogPrimitive.Root, { "data-slot": "dialog", open, onOpenChange, ...props });
1539
1712
  }
1540
1713
  function DialogTrigger({
1541
1714
  ...props
@@ -1556,7 +1729,10 @@ function DialogOverlay({
1556
1729
  {
1557
1730
  "data-slot": "dialog-overlay",
1558
1731
  className: cn(
1559
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
1732
+ "fixed inset-0 z-50 bg-black/50",
1733
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
1734
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
1735
+ "data-[state=closed]:pointer-events-none",
1560
1736
  className
1561
1737
  ),
1562
1738
  ...props
@@ -1575,6 +1751,7 @@ function DialogContent({
1575
1751
  DialogPrimitive.Content,
1576
1752
  {
1577
1753
  "data-slot": "dialog-content",
1754
+ "aria-describedby": void 0,
1578
1755
  className: cn(
1579
1756
  "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
1580
1757
  className
@@ -1649,12 +1826,33 @@ function DialogDescription({
1649
1826
  }
1650
1827
 
1651
1828
  // src/components/ui/alert-dialog.tsx
1829
+ var React6 = __toESM(require("react"), 1);
1652
1830
  var AlertDialogPrimitive = __toESM(require("@radix-ui/react-alert-dialog"), 1);
1653
1831
  var import_jsx_runtime13 = require("react/jsx-runtime");
1832
+ function cleanupBodyStyles3() {
1833
+ if (typeof document !== "undefined" && document.body.style.pointerEvents === "none") {
1834
+ document.body.style.pointerEvents = "";
1835
+ }
1836
+ }
1654
1837
  function AlertDialog({
1838
+ open,
1839
+ onOpenChange,
1655
1840
  ...props
1656
1841
  }) {
1657
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AlertDialogPrimitive.Root, { "data-slot": "alert-dialog", ...props });
1842
+ const prevOpenRef = React6.useRef(open);
1843
+ React6.useEffect(() => {
1844
+ if (prevOpenRef.current === true && open === false) {
1845
+ const timeout = setTimeout(cleanupBodyStyles3, 250);
1846
+ return () => clearTimeout(timeout);
1847
+ }
1848
+ prevOpenRef.current = open;
1849
+ }, [open]);
1850
+ React6.useEffect(() => {
1851
+ return () => {
1852
+ cleanupBodyStyles3();
1853
+ };
1854
+ }, []);
1855
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AlertDialogPrimitive.Root, { "data-slot": "alert-dialog", open, onOpenChange, ...props });
1658
1856
  }
1659
1857
  function AlertDialogPortal({
1660
1858
  ...props
@@ -1670,7 +1868,10 @@ function AlertDialogOverlay({
1670
1868
  {
1671
1869
  "data-slot": "alert-dialog-overlay",
1672
1870
  className: cn(
1673
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
1871
+ "fixed inset-0 z-50 bg-black/50",
1872
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
1873
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
1874
+ "data-[state=closed]:pointer-events-none",
1674
1875
  className
1675
1876
  ),
1676
1877
  ...props
@@ -2128,6 +2329,13 @@ var Sidebar2 = ({
2128
2329
  };
2129
2330
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Sidebar, { collapsible: "icon", ...props, children: [
2130
2331
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(SidebarHeader, { children: [
2332
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-3 px-2 py-3", children: [
2333
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex items-center justify-center shrink-0", children: config.branding?.logo || /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Avatar, { className: "h-8 w-8", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AvatarFallback, { className: "bg-primary text-primary-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.Bot, { className: "h-4 w-4" }) }) }) }),
2334
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col min-w-0 group-data-[collapsible=icon]:hidden", children: [
2335
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-sm font-semibold truncate", children: config.branding?.title || "Chat" }),
2336
+ config.branding?.subtitle && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs text-muted-foreground truncate", children: config.branding.subtitle })
2337
+ ] })
2338
+ ] }),
2131
2339
  onCreateThread && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2132
2340
  CreateThreadDialog,
2133
2341
  {
@@ -2147,7 +2355,7 @@ var Sidebar2 = ({
2147
2355
  ) }) })
2148
2356
  }
2149
2357
  ),
2150
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "px-2 py-1 mt-6", children: [
2358
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "px-2 py-1 mt-4", children: [
2151
2359
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "relative group-data-[collapsible=icon]:hidden", children: [
2152
2360
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.Search, { className: "pointer-events-none absolute left-2 top-1/2 size-4 -translate-y-1/2 select-none opacity-50" }),
2153
2361
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
@@ -2262,7 +2470,7 @@ var Sidebar2 = ({
2262
2470
  }
2263
2471
  ) }),
2264
2472
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SidebarRail, {}),
2265
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AlertDialog, { open: !!deleteThreadId, onOpenChange: () => setDeleteThreadId(null), children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(AlertDialogContent, { children: [
2473
+ deleteThreadId && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AlertDialog, { open: !!deleteThreadId, onOpenChange: () => setDeleteThreadId(null), children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(AlertDialogContent, { children: [
2266
2474
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(AlertDialogHeader, { children: [
2267
2475
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AlertDialogTitle, { children: config.labels?.deleteConfirmTitle || "Delete Conversation" }),
2268
2476
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AlertDialogDescription, { children: config.labels?.deleteConfirmDescription || "Are you sure you want to delete this conversation? This action cannot be undone." })
@@ -2297,6 +2505,10 @@ var ChatHeader = ({
2297
2505
  onClearAll,
2298
2506
  showCustomComponentButton,
2299
2507
  isMobile,
2508
+ showAgentSelector = false,
2509
+ agentOptions = [],
2510
+ selectedAgentId = null,
2511
+ onSelectAgent,
2300
2512
  className = ""
2301
2513
  }) => {
2302
2514
  const [isDarkMode, setIsDarkMode] = import_react3.default.useState(() => {
@@ -2347,22 +2559,64 @@ var ChatHeader = ({
2347
2559
  };
2348
2560
  input.click();
2349
2561
  };
2562
+ const selectedAgent = agentOptions.find((agent) => agent.id === selectedAgentId) || null;
2563
+ const agentPlaceholder = config.agentSelector?.label || "Select agent";
2350
2564
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2351
2565
  Card,
2352
2566
  {
2353
2567
  "data-chat-header": true,
2354
2568
  className: `py-0 border-b rounded-none relative z-10 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 ${className}`,
2355
2569
  style: isMobile ? { paddingTop: "env(safe-area-inset-top)" } : void 0,
2356
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CardHeader, { className: "p-2", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between", children: [
2357
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Tooltip, { children: [
2358
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SidebarTrigger, { className: "-ml-1" }) }),
2359
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TooltipContent, { children: config.labels?.sidebarToggle || "Toggle Sidebar" })
2360
- ] }) }),
2361
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-3 flex-1 justify-center", children: [
2362
- config.branding?.logo || /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Avatar, { className: "h-8 w-8", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AvatarFallback, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react8.Bot, { className: "h-4 w-4" }) }) }),
2363
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-center hidden md:block", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CardTitle, { className: "text-sm font-medium", children: config.branding?.title || "Chat Assistant" }) }),
2364
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "md:hidden text-sm font-medium truncate max-w-[150px]", children: currentThreadTitle || config.branding?.title || "Chat" })
2570
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CardHeader, { className: "p-2", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
2571
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-1", children: [
2572
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Tooltip, { children: [
2573
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SidebarTrigger, { className: "-ml-1" }) }),
2574
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TooltipContent, { children: config.labels?.sidebarToggle || "Toggle Sidebar" })
2575
+ ] }),
2576
+ showAgentSelector && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(DropdownMenu, { children: [
2577
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2578
+ Button,
2579
+ {
2580
+ variant: "ghost",
2581
+ className: "h-9 px-3 gap-1.5 font-medium text-base hover:bg-accent/50",
2582
+ children: [
2583
+ selectedAgent?.avatarUrl ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Avatar, { className: "h-5 w-5", children: [
2584
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AvatarImage, { src: selectedAgent.avatarUrl, alt: selectedAgent.name }),
2585
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AvatarFallback, { className: "text-[10px]", children: selectedAgent.name.charAt(0).toUpperCase() })
2586
+ ] }) : null,
2587
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "max-w-[200px] truncate", children: selectedAgent?.name || agentPlaceholder }),
2588
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react8.ChevronDown, { className: "h-4 w-4 opacity-50" })
2589
+ ]
2590
+ }
2591
+ ) }),
2592
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DropdownMenuContent, { align: "start", className: "w-[280px]", children: agentOptions.map((agent) => {
2593
+ const isSelected = agent.id === selectedAgentId;
2594
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2595
+ DropdownMenuItem,
2596
+ {
2597
+ onClick: () => onSelectAgent?.(agent.id),
2598
+ className: "flex items-start gap-3 p-3 cursor-pointer",
2599
+ children: [
2600
+ agent.avatarUrl ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Avatar, { className: "h-6 w-6 mt-0.5 shrink-0", children: [
2601
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AvatarImage, { src: agent.avatarUrl, alt: agent.name }),
2602
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AvatarFallback, { className: "text-[10px]", children: agent.name.charAt(0).toUpperCase() })
2603
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "h-6 w-6 mt-0.5 shrink-0 flex items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react8.Bot, { className: "h-3.5 w-3.5 text-primary" }) }),
2604
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex-1 min-w-0", children: [
2605
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-2", children: [
2606
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "font-medium text-sm", children: agent.name }),
2607
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react8.Check, { className: "h-4 w-4 text-primary shrink-0" })
2608
+ ] }),
2609
+ agent.description && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-xs text-muted-foreground mt-0.5 line-clamp-2", children: agent.description })
2610
+ ] })
2611
+ ]
2612
+ },
2613
+ agent.id
2614
+ );
2615
+ }) })
2616
+ ] }),
2617
+ !showAgentSelector && isMobile && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-sm font-medium truncate max-w-[150px] ml-2", children: currentThreadTitle || config.branding?.title || "Chat" })
2365
2618
  ] }),
2619
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex-1" }),
2366
2620
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-1", children: [
2367
2621
  showCustomComponentButton && config.customComponent && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Tooltip, { children: [
2368
2622
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
@@ -2497,7 +2751,7 @@ function Progress({
2497
2751
  // src/components/chat/ChatInput.tsx
2498
2752
  var import_lucide_react9 = require("lucide-react");
2499
2753
  var import_jsx_runtime20 = require("react/jsx-runtime");
2500
- var FileUploadItem = ({ file, progress, onCancel }) => {
2754
+ var FileUploadItem = (0, import_react5.memo)(function FileUploadItem2({ file, progress, onCancel }) {
2501
2755
  const guessTypeFromName = (name) => {
2502
2756
  const ext = (name || "").split(".").pop()?.toLowerCase();
2503
2757
  switch (ext) {
@@ -2555,8 +2809,8 @@ var FileUploadItem = ({ file, progress, onCancel }) => {
2555
2809
  }
2556
2810
  )
2557
2811
  ] }) }) });
2558
- };
2559
- var AttachmentPreview = ({ attachment, onRemove }) => {
2812
+ });
2813
+ var AttachmentPreview = (0, import_react5.memo)(function AttachmentPreview2({ attachment, onRemove }) {
2560
2814
  const [isPlaying, setIsPlaying] = (0, import_react5.useState)(false);
2561
2815
  const audioRef = (0, import_react5.useRef)(null);
2562
2816
  const handlePlayPause = () => {
@@ -2656,8 +2910,8 @@ var AttachmentPreview = ({ attachment, onRemove }) => {
2656
2910
  ] }),
2657
2911
  attachment.fileName && attachment.kind !== "audio" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "absolute bottom-0 left-0 right-0 bg-black/70 text-white text-xs p-1 rounded-b", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "truncate", children: attachment.fileName }) })
2658
2912
  ] }) });
2659
- };
2660
- var AudioRecorder = ({ isRecording, onStartRecording, onStopRecording, onCancel, recordingDuration, config }) => {
2913
+ });
2914
+ var AudioRecorder = (0, import_react5.memo)(function AudioRecorder2({ isRecording, onStartRecording, onStopRecording, onCancel, recordingDuration, config }) {
2661
2915
  const formatTime = (seconds) => {
2662
2916
  const mins = Math.floor(seconds / 60);
2663
2917
  const secs = seconds % 60;
@@ -2711,8 +2965,8 @@ var AudioRecorder = ({ isRecording, onStartRecording, onStopRecording, onCancel,
2711
2965
  )
2712
2966
  ] })
2713
2967
  ] }) }) });
2714
- };
2715
- var ChatInput = ({
2968
+ });
2969
+ var ChatInput = (0, import_react5.memo)(function ChatInput2({
2716
2970
  value,
2717
2971
  onChange,
2718
2972
  onSubmit,
@@ -2730,7 +2984,7 @@ var ChatInput = ({
2730
2984
  acceptedFileTypes = ["image/*", "video/*", "audio/*"],
2731
2985
  className = "",
2732
2986
  config
2733
- }) => {
2987
+ }) {
2734
2988
  const [isRecording, setIsRecording] = (0, import_react5.useState)(false);
2735
2989
  const { setContext } = useChatUserContext();
2736
2990
  const [recordingDuration, setRecordingDuration] = (0, import_react5.useState)(0);
@@ -3072,16 +3326,16 @@ var ChatInput = ({
3072
3326
  ] })
3073
3327
  ] })
3074
3328
  ] }) }) });
3075
- };
3329
+ });
3076
3330
 
3077
3331
  // src/components/chat/UserProfile.tsx
3078
3332
  var import_react6 = require("react");
3079
3333
 
3080
3334
  // src/components/ui/scroll-area.tsx
3081
- var React8 = __toESM(require("react"), 1);
3335
+ var React11 = __toESM(require("react"), 1);
3082
3336
  var ScrollAreaPrimitive = __toESM(require("@radix-ui/react-scroll-area"), 1);
3083
3337
  var import_jsx_runtime21 = require("react/jsx-runtime");
3084
- var ScrollArea = React8.forwardRef(({ className, children, viewportClassName, onScroll, onScrollCapture, ...props }, ref) => {
3338
+ var ScrollArea = React11.forwardRef(({ className, children, viewportClassName, onScroll, onScrollCapture, ...props }, ref) => {
3085
3339
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
3086
3340
  ScrollAreaPrimitive.Root,
3087
3341
  {
@@ -3527,10 +3781,16 @@ var ChatUI = ({
3527
3781
  user,
3528
3782
  assistant,
3529
3783
  suggestions = [],
3784
+ messageSuggestions = {},
3785
+ agentOptions = [],
3786
+ selectedAgentId = null,
3787
+ onSelectAgent,
3530
3788
  className = "",
3531
3789
  onAddMemory,
3532
3790
  onUpdateMemory,
3533
- onDeleteMemory
3791
+ onDeleteMemory,
3792
+ initialInput,
3793
+ onInitialInputConsumed
3534
3794
  }) => {
3535
3795
  const config = mergeConfig(defaultChatConfig, userConfig);
3536
3796
  const [isMobile, setIsMobile] = (0, import_react7.useState)(false);
@@ -3548,9 +3808,9 @@ var ChatUI = ({
3548
3808
  }
3549
3809
  return false;
3550
3810
  };
3811
+ const [inputValue, setInputValue] = (0, import_react7.useState)("");
3812
+ const [attachments, setAttachments] = (0, import_react7.useState)([]);
3551
3813
  const [state, setState] = (0, import_react7.useState)({
3552
- input: "",
3553
- attachments: [],
3554
3814
  isRecording: false,
3555
3815
  selectedThreadId: currentThreadId,
3556
3816
  isAtBottom: true,
@@ -3567,16 +3827,41 @@ var ChatUI = ({
3567
3827
  setState((prev) => ({ ...prev, selectedThreadId: currentThreadId }));
3568
3828
  }
3569
3829
  }, [currentThreadId]);
3830
+ const initialInputApplied = (0, import_react7.useRef)(false);
3831
+ const initialInputConsumedRef = (0, import_react7.useRef)(false);
3832
+ (0, import_react7.useEffect)(() => {
3833
+ if (initialInput && !initialInputApplied.current) {
3834
+ setInputValue(initialInput);
3835
+ initialInputApplied.current = true;
3836
+ }
3837
+ }, [initialInput]);
3570
3838
  const messagesEndRef = (0, import_react7.useRef)(null);
3571
3839
  const scrollAreaRef = (0, import_react7.useRef)(null);
3840
+ const stateRef = (0, import_react7.useRef)(state);
3841
+ const inputValueRef = (0, import_react7.useRef)(inputValue);
3842
+ const attachmentsRef = (0, import_react7.useRef)(attachments);
3843
+ (0, import_react7.useEffect)(() => {
3844
+ stateRef.current = state;
3845
+ }, [state]);
3846
+ (0, import_react7.useEffect)(() => {
3847
+ inputValueRef.current = inputValue;
3848
+ }, [inputValue]);
3849
+ (0, import_react7.useEffect)(() => {
3850
+ attachmentsRef.current = attachments;
3851
+ }, [attachments]);
3572
3852
  const [isCustomMounted, setIsCustomMounted] = (0, import_react7.useState)(false);
3573
3853
  const [isCustomVisible, setIsCustomVisible] = (0, import_react7.useState)(false);
3574
3854
  const createStateCallback = (0, import_react7.useCallback)(
3575
3855
  (setter) => ({
3576
3856
  setState: (newState) => setter?.(newState),
3577
- getState: () => state
3857
+ getState: () => ({
3858
+ ...stateRef.current,
3859
+ input: inputValueRef.current,
3860
+ attachments: attachmentsRef.current
3861
+ })
3578
3862
  }),
3579
- [state]
3863
+ []
3864
+ // No dependencies - uses refs for latest state
3580
3865
  );
3581
3866
  (0, import_react7.useEffect)(() => {
3582
3867
  const checkMobile = () => {
@@ -3613,15 +3898,16 @@ var ChatUI = ({
3613
3898
  const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
3614
3899
  setState((prev) => ({ ...prev, isAtBottom }));
3615
3900
  }, []);
3616
- const handleSendMessage = (0, import_react7.useCallback)((content, attachments = []) => {
3617
- if (!content.trim() && attachments.length === 0) return;
3618
- callbacks.onSendMessage?.(content, attachments, createStateCallback());
3619
- setState((prev) => ({
3620
- ...prev,
3621
- input: "",
3622
- attachments: []
3623
- }));
3624
- }, [callbacks, createStateCallback]);
3901
+ const handleSendMessage = (0, import_react7.useCallback)((content, messageAttachments = []) => {
3902
+ if (!content.trim() && messageAttachments.length === 0) return;
3903
+ callbacks.onSendMessage?.(content, messageAttachments, createStateCallback());
3904
+ if (initialInputApplied.current && !initialInputConsumedRef.current) {
3905
+ initialInputConsumedRef.current = true;
3906
+ onInitialInputConsumed?.();
3907
+ }
3908
+ setInputValue("");
3909
+ setAttachments([]);
3910
+ }, [callbacks, createStateCallback, onInitialInputConsumed]);
3625
3911
  const handleMessageAction = (0, import_react7.useCallback)((event) => {
3626
3912
  const { action, messageId, content } = event;
3627
3913
  switch (action) {
@@ -3642,7 +3928,7 @@ var ChatUI = ({
3642
3928
  }
3643
3929
  }, [callbacks, createStateCallback]);
3644
3930
  const handleCreateThread = (0, import_react7.useCallback)((title) => {
3645
- callbacks.onCreateThread?.(title, createStateCallback(setState));
3931
+ callbacks.onCreateThread?.(title, createStateCallback());
3646
3932
  }, [callbacks, createStateCallback]);
3647
3933
  const handleSelectThread = (0, import_react7.useCallback)((threadId) => {
3648
3934
  callbacks.onSelectThread?.(threadId, createStateCallback());
@@ -3667,23 +3953,54 @@ var ChatUI = ({
3667
3953
  }
3668
3954
  return component;
3669
3955
  }, [config?.customComponent?.component, closeSidebar, isMobile]);
3956
+ const SuggestionIconComponents = [import_lucide_react11.MessageSquare, import_lucide_react11.Lightbulb, import_lucide_react11.Zap, import_lucide_react11.HelpCircle];
3670
3957
  const renderSuggestions = () => {
3671
3958
  if (messages.length > 0 || !suggestions.length) return null;
3672
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "text-center py-8", children: [
3673
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Sparkles, { className: "w-8 h-8 text-primary" }) }),
3674
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h3", { className: "text-lg font-semibold mb-2", children: config.branding.title }),
3675
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-muted-foreground mb-6", children: config.branding.subtitle }),
3676
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 max-w-2xl mx-auto", children: suggestions.map((suggestion, index) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3677
- Card,
3959
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col items-center justify-center min-h-[60vh] py-8 px-4", children: [
3960
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "text-center mb-8", children: [
3961
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-gradient-to-br from-primary/20 to-primary/5 mb-4 shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Sparkles, { className: "w-7 h-7 text-primary" }) }),
3962
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h2", { className: "text-xl font-semibold mb-2", children: config.branding.title }),
3963
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-muted-foreground text-sm max-w-md", children: config.branding.subtitle })
3964
+ ] }),
3965
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3 w-full max-w-2xl", children: suggestions.map((suggestion, index) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3966
+ "button",
3678
3967
  {
3679
- className: "cursor-pointer hover:bg-muted/50 transition-colors",
3968
+ type: "button",
3680
3969
  onClick: () => handleSendMessage(suggestion),
3681
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CardContent, { className: "p-4 text-left", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-sm", children: suggestion }) })
3970
+ className: "group relative flex items-start gap-3 p-4 text-left rounded-xl border bg-card hover:bg-accent/50 hover:border-accent transition-all duration-200 hover:shadow-sm",
3971
+ children: [
3972
+ (() => {
3973
+ const IconComponent = SuggestionIconComponents[index % SuggestionIconComponents.length];
3974
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex items-center justify-center w-8 h-8 rounded-lg bg-primary/10 text-primary shrink-0 group-hover:bg-primary/15 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(IconComponent, { className: "h-4 w-4" }) });
3975
+ })(),
3976
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 min-w-0 pr-6", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-sm font-medium leading-snug line-clamp-2", children: suggestion }) }),
3977
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.ArrowRight, { className: "absolute right-4 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" })
3978
+ ]
3682
3979
  },
3683
3980
  index
3684
3981
  )) })
3685
3982
  ] });
3686
3983
  };
3984
+ const renderInlineSuggestions = (messageId) => {
3985
+ const items = messageSuggestions?.[messageId];
3986
+ if (!items || items.length === 0) return null;
3987
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex flex-wrap gap-2 mt-2 ml-11", children: items.map((suggestion, index) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3988
+ "button",
3989
+ {
3990
+ type: "button",
3991
+ onClick: () => handleSendMessage(suggestion),
3992
+ className: "group inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-full border border-border bg-background hover:bg-accent hover:border-accent-foreground/20 transition-all duration-150 text-foreground/80 hover:text-foreground",
3993
+ children: [
3994
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Sparkles, { className: "h-3 w-3 text-primary opacity-70 group-hover:opacity-100" }),
3995
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "max-w-[200px] truncate", children: suggestion })
3996
+ ]
3997
+ },
3998
+ `${messageId}-suggestion-${index}`
3999
+ )) });
4000
+ };
4001
+ const shouldShowAgentSelector = Boolean(
4002
+ config.agentSelector?.enabled && onSelectAgent && agentOptions.length > 0 && (!config.agentSelector?.hideIfSingle || agentOptions.length > 1)
4003
+ );
3687
4004
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SidebarProvider, { defaultOpen: true, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: `flex h-[100svh] md:h-screen bg-background w-full overflow-hidden ${className}`, children: [
3688
4005
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3689
4006
  Sidebar2,
@@ -3724,7 +4041,11 @@ var ChatUI = ({
3724
4041
  isMobile,
3725
4042
  onCustomComponentToggle: () => setState((prev) => ({ ...prev, showSidebar: !prev.showSidebar })),
3726
4043
  onNewThread: handleCreateThread,
3727
- showCustomComponentButton: !!config?.customComponent?.component
4044
+ showCustomComponentButton: !!config?.customComponent?.component,
4045
+ showAgentSelector: shouldShowAgentSelector,
4046
+ agentOptions,
4047
+ selectedAgentId,
4048
+ onSelectAgent
3728
4049
  }
3729
4050
  ),
3730
4051
  /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-1 flex-row min-h-0 overflow-hidden", children: [
@@ -3738,27 +4059,34 @@ var ChatUI = ({
3738
4059
  onScrollCapture: handleScroll,
3739
4060
  children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "max-w-4xl mx-auto space-y-4 pb-4", children: [
3740
4061
  renderSuggestions(),
3741
- messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3742
- Message,
3743
- {
3744
- message,
3745
- userAvatar: user?.avatar,
3746
- userName: user?.name,
3747
- assistantAvatar: assistant?.avatar,
3748
- assistantName: assistant?.name,
3749
- showTimestamp: config.ui.showTimestamps,
3750
- showAvatar: config.ui.showAvatars,
3751
- enableCopy: config.features.enableMessageCopy,
3752
- enableEdit: config.features.enableMessageEditing,
3753
- enableRegenerate: config.features.enableRegeneration,
3754
- enableToolCallsDisplay: config.features.enableToolCallsDisplay,
3755
- compactMode: config.ui.compactMode,
3756
- onAction: handleMessageAction,
3757
- toolUsedLabel: config.labels.toolUsed,
3758
- thinkingLabel: config.labels.thinking
3759
- },
3760
- message.id
3761
- )),
4062
+ messages.map((message, index) => {
4063
+ const prevMessage = index > 0 ? messages[index - 1] : null;
4064
+ const isGrouped = prevMessage !== null && prevMessage.role === message.role;
4065
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: isGrouped ? "space-y-1 -mt-2" : "space-y-2", children: [
4066
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4067
+ Message,
4068
+ {
4069
+ message,
4070
+ userAvatar: user?.avatar,
4071
+ userName: user?.name,
4072
+ assistantAvatar: assistant?.avatar,
4073
+ assistantName: assistant?.name,
4074
+ showTimestamp: config.ui.showTimestamps,
4075
+ showAvatar: config.ui.showAvatars,
4076
+ enableCopy: config.features.enableMessageCopy,
4077
+ enableEdit: config.features.enableMessageEditing,
4078
+ enableRegenerate: config.features.enableRegeneration,
4079
+ enableToolCallsDisplay: config.features.enableToolCallsDisplay,
4080
+ compactMode: config.ui.compactMode,
4081
+ onAction: handleMessageAction,
4082
+ toolUsedLabel: config.labels.toolUsed,
4083
+ thinkingLabel: config.labels.thinking,
4084
+ isGrouped
4085
+ }
4086
+ ),
4087
+ message.role === "assistant" && renderInlineSuggestions(message.id)
4088
+ ] }, message.id);
4089
+ }),
3762
4090
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ref: messagesEndRef })
3763
4091
  ] })
3764
4092
  }
@@ -3766,11 +4094,17 @@ var ChatUI = ({
3766
4094
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "bg-background pb-[env(safe-area-inset-bottom)]", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3767
4095
  ChatInput,
3768
4096
  {
3769
- value: state.input,
3770
- onChange: (value) => setState((prev) => ({ ...prev, input: value })),
4097
+ value: inputValue,
4098
+ onChange: (value) => {
4099
+ setInputValue(value);
4100
+ if (initialInputApplied.current && !initialInputConsumedRef.current) {
4101
+ initialInputConsumedRef.current = true;
4102
+ onInitialInputConsumed?.();
4103
+ }
4104
+ },
3771
4105
  onSubmit: handleSendMessage,
3772
- attachments: state.attachments,
3773
- onAttachmentsChange: (attachments) => setState((prev) => ({ ...prev, attachments })),
4106
+ attachments,
4107
+ onAttachmentsChange: setAttachments,
3774
4108
  placeholder: config.labels.inputPlaceholder,
3775
4109
  disabled: false,
3776
4110
  isGenerating,
@@ -3810,7 +4144,7 @@ var ChatUI = ({
3810
4144
  }
3811
4145
  )
3812
4146
  ] }),
3813
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4147
+ isUserProfileOpen && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3814
4148
  UserProfile,
3815
4149
  {
3816
4150
  isOpen: isUserProfileOpen,
@@ -4074,7 +4408,7 @@ var ThreadManager = ({
4074
4408
  ] }, group)) }) })
4075
4409
  ] })
4076
4410
  ] }) }),
4077
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AlertDialog, { open: !!deleteThreadId, onOpenChange: () => setDeleteThreadId(null), children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(AlertDialogContent, { children: [
4411
+ deleteThreadId && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AlertDialog, { open: !!deleteThreadId, onOpenChange: () => setDeleteThreadId(null), children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(AlertDialogContent, { children: [
4078
4412
  /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(AlertDialogHeader, { children: [
4079
4413
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AlertDialogTitle, { children: config?.labels?.deleteConfirmTitle || "Delete Conversation" }),
4080
4414
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AlertDialogDescription, { children: config?.labels?.deleteConfirmDescription || "Are you sure you want to delete this conversation? This action cannot be undone." })