@copilotkitnext/react 0.0.14 → 0.0.16

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.mjs CHANGED
@@ -1,9 +1,18 @@
1
1
  "use client";
2
2
 
3
3
  // src/components/chat/CopilotChatInput.tsx
4
- import { useState as useState2, useRef as useRef2, useEffect as useEffect2, forwardRef as forwardRef2, useImperativeHandle as useImperativeHandle2 } from "react";
4
+ import {
5
+ useState as useState2,
6
+ useRef as useRef2,
7
+ useEffect as useEffect2,
8
+ useLayoutEffect,
9
+ forwardRef as forwardRef2,
10
+ useImperativeHandle as useImperativeHandle2,
11
+ useCallback,
12
+ useMemo as useMemo2
13
+ } from "react";
5
14
  import { twMerge as twMerge3 } from "tailwind-merge";
6
- import { Plus, Settings2, Mic, ArrowUp, X, Check } from "lucide-react";
15
+ import { Plus, Mic, ArrowUp, X, Check } from "lucide-react";
7
16
 
8
17
  // src/providers/CopilotChatConfigurationProvider.tsx
9
18
  import { createContext, useContext, useMemo, useState } from "react";
@@ -493,6 +502,8 @@ function renderSlot(slot, DefaultComponent, props) {
493
502
 
494
503
  // src/components/chat/CopilotChatInput.tsx
495
504
  import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
505
+ var SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
506
+ var SLASH_MENU_ITEM_HEIGHT_PX = 40;
496
507
  function CopilotChatInput({
497
508
  mode = "input",
498
509
  onSubmitMessage,
@@ -504,15 +515,12 @@ function CopilotChatInput({
504
515
  value,
505
516
  toolsMenu,
506
517
  autoFocus = true,
507
- additionalToolbarItems,
508
518
  textArea,
509
519
  sendButton,
510
520
  startTranscribeButton,
511
521
  cancelTranscribeButton,
512
522
  finishTranscribeButton,
513
- addFileButton,
514
- toolsButton,
515
- toolbar,
523
+ addMenuButton,
516
524
  audioRecorder,
517
525
  children,
518
526
  className,
@@ -526,10 +534,82 @@ function CopilotChatInput({
526
534
  }
527
535
  }, [isControlled, value]);
528
536
  const resolvedValue = isControlled ? value ?? "" : internalValue;
537
+ const [layout, setLayout] = useState2("compact");
538
+ const ignoreResizeRef = useRef2(false);
539
+ const resizeEvaluationRafRef = useRef2(null);
540
+ const isExpanded = mode === "input" && layout === "expanded";
541
+ const [commandQuery, setCommandQuery] = useState2(null);
542
+ const [slashHighlightIndex, setSlashHighlightIndex] = useState2(0);
529
543
  const inputRef = useRef2(null);
544
+ const gridRef = useRef2(null);
545
+ const addButtonContainerRef = useRef2(null);
546
+ const actionsContainerRef = useRef2(null);
530
547
  const audioRecorderRef = useRef2(null);
548
+ const slashMenuRef = useRef2(null);
531
549
  const config = useCopilotChatConfiguration();
550
+ const labels = config?.labels ?? CopilotChatDefaultLabels;
532
551
  const previousModalStateRef = useRef2(void 0);
552
+ const measurementCanvasRef = useRef2(null);
553
+ const measurementsRef = useRef2({
554
+ singleLineHeight: 0,
555
+ maxHeight: 0,
556
+ paddingLeft: 0,
557
+ paddingRight: 0
558
+ });
559
+ const commandItems = useMemo2(() => {
560
+ const entries = [];
561
+ const seen = /* @__PURE__ */ new Set();
562
+ const pushItem = (item) => {
563
+ if (item === "-") {
564
+ return;
565
+ }
566
+ if (item.items && item.items.length > 0) {
567
+ for (const nested of item.items) {
568
+ pushItem(nested);
569
+ }
570
+ return;
571
+ }
572
+ if (!seen.has(item.label)) {
573
+ seen.add(item.label);
574
+ entries.push(item);
575
+ }
576
+ };
577
+ if (onAddFile) {
578
+ pushItem({
579
+ label: labels.chatInputToolbarAddButtonLabel,
580
+ action: onAddFile
581
+ });
582
+ }
583
+ if (toolsMenu && toolsMenu.length > 0) {
584
+ for (const item of toolsMenu) {
585
+ pushItem(item);
586
+ }
587
+ }
588
+ return entries;
589
+ }, [labels.chatInputToolbarAddButtonLabel, onAddFile, toolsMenu]);
590
+ const filteredCommands = useMemo2(() => {
591
+ if (commandQuery === null) {
592
+ return [];
593
+ }
594
+ if (commandItems.length === 0) {
595
+ return [];
596
+ }
597
+ const query = commandQuery.trim().toLowerCase();
598
+ if (query.length === 0) {
599
+ return commandItems;
600
+ }
601
+ const startsWith = [];
602
+ const contains = [];
603
+ for (const item of commandItems) {
604
+ const label = item.label.toLowerCase();
605
+ if (label.startsWith(query)) {
606
+ startsWith.push(item);
607
+ } else if (label.includes(query)) {
608
+ contains.push(item);
609
+ }
610
+ }
611
+ return [...startsWith, ...contains];
612
+ }, [commandItems, commandQuery]);
533
613
  useEffect2(() => {
534
614
  if (!autoFocus) {
535
615
  previousModalStateRef.current = config?.isModalOpen;
@@ -540,6 +620,29 @@ function CopilotChatInput({
540
620
  }
541
621
  previousModalStateRef.current = config?.isModalOpen;
542
622
  }, [config?.isModalOpen, autoFocus]);
623
+ useEffect2(() => {
624
+ if (commandItems.length === 0 && commandQuery !== null) {
625
+ setCommandQuery(null);
626
+ }
627
+ }, [commandItems.length, commandQuery]);
628
+ const previousCommandQueryRef = useRef2(null);
629
+ useEffect2(() => {
630
+ if (commandQuery !== null && commandQuery !== previousCommandQueryRef.current && filteredCommands.length > 0) {
631
+ setSlashHighlightIndex(0);
632
+ }
633
+ previousCommandQueryRef.current = commandQuery;
634
+ }, [commandQuery, filteredCommands.length]);
635
+ useEffect2(() => {
636
+ if (commandQuery === null) {
637
+ setSlashHighlightIndex(0);
638
+ return;
639
+ }
640
+ if (filteredCommands.length === 0) {
641
+ setSlashHighlightIndex(-1);
642
+ } else if (slashHighlightIndex < 0 || slashHighlightIndex >= filteredCommands.length) {
643
+ setSlashHighlightIndex(0);
644
+ }
645
+ }, [commandQuery, filteredCommands, slashHighlightIndex]);
543
646
  useEffect2(() => {
544
647
  const recorder = audioRecorderRef.current;
545
648
  if (!recorder) {
@@ -553,14 +656,103 @@ function CopilotChatInput({
553
656
  }
554
657
  }
555
658
  }, [mode]);
659
+ useEffect2(() => {
660
+ if (mode !== "input") {
661
+ setLayout("compact");
662
+ setCommandQuery(null);
663
+ }
664
+ }, [mode]);
665
+ const updateSlashState = useCallback(
666
+ (value2) => {
667
+ if (commandItems.length === 0) {
668
+ setCommandQuery((prev) => prev === null ? prev : null);
669
+ return;
670
+ }
671
+ if (value2.startsWith("/")) {
672
+ const firstLine = value2.split(/\r?\n/, 1)[0] ?? "";
673
+ const query = firstLine.slice(1);
674
+ setCommandQuery((prev) => prev === query ? prev : query);
675
+ } else {
676
+ setCommandQuery((prev) => prev === null ? prev : null);
677
+ }
678
+ },
679
+ [commandItems.length]
680
+ );
681
+ useEffect2(() => {
682
+ updateSlashState(resolvedValue);
683
+ }, [resolvedValue, updateSlashState]);
556
684
  const handleChange = (e) => {
557
685
  const nextValue = e.target.value;
558
686
  if (!isControlled) {
559
687
  setInternalValue(nextValue);
560
688
  }
561
689
  onChange?.(nextValue);
690
+ updateSlashState(nextValue);
562
691
  };
692
+ const clearInputValue = useCallback(() => {
693
+ if (!isControlled) {
694
+ setInternalValue("");
695
+ }
696
+ if (onChange) {
697
+ onChange("");
698
+ }
699
+ }, [isControlled, onChange]);
700
+ const runCommand = useCallback(
701
+ (item) => {
702
+ clearInputValue();
703
+ item.action?.();
704
+ setCommandQuery(null);
705
+ setSlashHighlightIndex(0);
706
+ requestAnimationFrame(() => {
707
+ inputRef.current?.focus();
708
+ });
709
+ },
710
+ [clearInputValue]
711
+ );
563
712
  const handleKeyDown = (e) => {
713
+ if (commandQuery !== null && mode === "input") {
714
+ if (e.key === "ArrowDown") {
715
+ if (filteredCommands.length > 0) {
716
+ e.preventDefault();
717
+ setSlashHighlightIndex((prev) => {
718
+ if (filteredCommands.length === 0) {
719
+ return prev;
720
+ }
721
+ const next = prev === -1 ? 0 : (prev + 1) % filteredCommands.length;
722
+ return next;
723
+ });
724
+ }
725
+ return;
726
+ }
727
+ if (e.key === "ArrowUp") {
728
+ if (filteredCommands.length > 0) {
729
+ e.preventDefault();
730
+ setSlashHighlightIndex((prev) => {
731
+ if (filteredCommands.length === 0) {
732
+ return prev;
733
+ }
734
+ if (prev === -1) {
735
+ return filteredCommands.length - 1;
736
+ }
737
+ return prev <= 0 ? filteredCommands.length - 1 : prev - 1;
738
+ });
739
+ }
740
+ return;
741
+ }
742
+ if (e.key === "Enter") {
743
+ const selected = slashHighlightIndex >= 0 ? filteredCommands[slashHighlightIndex] : void 0;
744
+ if (selected) {
745
+ e.preventDefault();
746
+ runCommand(selected);
747
+ return;
748
+ }
749
+ }
750
+ if (e.key === "Escape") {
751
+ e.preventDefault();
752
+ setCommandQuery(null);
753
+ return;
754
+ }
755
+ }
564
756
  if (e.key === "Enter" && !e.shiftKey) {
565
757
  e.preventDefault();
566
758
  send();
@@ -588,7 +780,11 @@ function CopilotChatInput({
588
780
  value: resolvedValue,
589
781
  onChange: handleChange,
590
782
  onKeyDown: handleKeyDown,
591
- autoFocus
783
+ autoFocus,
784
+ className: twMerge3(
785
+ "w-full py-3",
786
+ isExpanded ? "px-5" : "pr-5"
787
+ )
592
788
  });
593
789
  const BoundAudioRecorder = renderSlot(audioRecorder, CopilotChatAudioRecorder, {
594
790
  ref: audioRecorderRef
@@ -606,45 +802,20 @@ function CopilotChatInput({
606
802
  const BoundFinishTranscribeButton = renderSlot(finishTranscribeButton, CopilotChatInput.FinishTranscribeButton, {
607
803
  onClick: onFinishTranscribe
608
804
  });
609
- const BoundAddFileButton = renderSlot(addFileButton, CopilotChatInput.AddFileButton, {
610
- onClick: onAddFile,
611
- disabled: mode === "transcribe"
612
- });
613
- const BoundToolsButton = renderSlot(toolsButton, CopilotChatInput.ToolsButton, {
805
+ const BoundAddMenuButton = renderSlot(addMenuButton, CopilotChatInput.AddMenuButton, {
614
806
  disabled: mode === "transcribe",
807
+ onAddFile,
615
808
  toolsMenu
616
809
  });
617
- const BoundToolbar = renderSlot(
618
- typeof toolbar === "string" || toolbar === void 0 ? twMerge3(toolbar, "w-full h-[60px] bg-transparent flex items-center justify-between") : toolbar,
619
- CopilotChatInput.Toolbar,
620
- {
621
- children: /* @__PURE__ */ jsxs3(Fragment, { children: [
622
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center", children: [
623
- onAddFile && BoundAddFileButton,
624
- BoundToolsButton,
625
- additionalToolbarItems
626
- ] }),
627
- /* @__PURE__ */ jsx6("div", { className: "flex items-center", children: mode === "transcribe" ? /* @__PURE__ */ jsxs3(Fragment, { children: [
628
- onCancelTranscribe && BoundCancelTranscribeButton,
629
- onFinishTranscribe && BoundFinishTranscribeButton
630
- ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
631
- onStartTranscribe && BoundStartTranscribeButton,
632
- BoundSendButton
633
- ] }) })
634
- ] })
635
- }
636
- );
637
810
  if (children) {
638
- return /* @__PURE__ */ jsx6(Fragment, { children: children({
811
+ const childProps = {
639
812
  textArea: BoundTextArea,
640
813
  audioRecorder: BoundAudioRecorder,
641
814
  sendButton: BoundSendButton,
642
815
  startTranscribeButton: BoundStartTranscribeButton,
643
816
  cancelTranscribeButton: BoundCancelTranscribeButton,
644
817
  finishTranscribeButton: BoundFinishTranscribeButton,
645
- addFileButton: BoundAddFileButton,
646
- toolsButton: BoundToolsButton,
647
- toolbar: BoundToolbar,
818
+ addMenuButton: BoundAddMenuButton,
648
819
  onSubmitMessage,
649
820
  onStartTranscribe,
650
821
  onCancelTranscribe,
@@ -652,9 +823,9 @@ function CopilotChatInput({
652
823
  onAddFile,
653
824
  mode,
654
825
  toolsMenu,
655
- autoFocus,
656
- additionalToolbarItems
657
- }) });
826
+ autoFocus
827
+ };
828
+ return /* @__PURE__ */ jsx6(Fragment, { children: children(childProps) });
658
829
  }
659
830
  const handleContainerClick = (e) => {
660
831
  const target = e.target;
@@ -662,7 +833,227 @@ function CopilotChatInput({
662
833
  inputRef.current.focus();
663
834
  }
664
835
  };
665
- return /* @__PURE__ */ jsxs3(
836
+ const ensureMeasurements = useCallback(() => {
837
+ const textarea = inputRef.current;
838
+ if (!textarea) {
839
+ return;
840
+ }
841
+ const previousValue = textarea.value;
842
+ const previousHeight = textarea.style.height;
843
+ textarea.style.height = "auto";
844
+ const computedStyle = window.getComputedStyle(textarea);
845
+ const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;
846
+ const paddingRight = parseFloat(computedStyle.paddingRight) || 0;
847
+ const paddingTop = parseFloat(computedStyle.paddingTop) || 0;
848
+ const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0;
849
+ textarea.value = "";
850
+ const singleLineHeight = textarea.scrollHeight;
851
+ textarea.value = previousValue;
852
+ const contentHeight = singleLineHeight - paddingTop - paddingBottom;
853
+ const maxHeight = contentHeight * 5 + paddingTop + paddingBottom;
854
+ measurementsRef.current = {
855
+ singleLineHeight,
856
+ maxHeight,
857
+ paddingLeft,
858
+ paddingRight
859
+ };
860
+ textarea.style.height = previousHeight;
861
+ textarea.style.maxHeight = `${maxHeight}px`;
862
+ }, []);
863
+ const adjustTextareaHeight = useCallback(() => {
864
+ const textarea = inputRef.current;
865
+ if (!textarea) {
866
+ return 0;
867
+ }
868
+ if (measurementsRef.current.singleLineHeight === 0) {
869
+ ensureMeasurements();
870
+ }
871
+ const { maxHeight } = measurementsRef.current;
872
+ if (maxHeight) {
873
+ textarea.style.maxHeight = `${maxHeight}px`;
874
+ }
875
+ textarea.style.height = "auto";
876
+ const scrollHeight = textarea.scrollHeight;
877
+ if (maxHeight) {
878
+ textarea.style.height = `${Math.min(scrollHeight, maxHeight)}px`;
879
+ } else {
880
+ textarea.style.height = `${scrollHeight}px`;
881
+ }
882
+ return scrollHeight;
883
+ }, [ensureMeasurements]);
884
+ const updateLayout = useCallback((nextLayout) => {
885
+ setLayout((prev) => {
886
+ if (prev === nextLayout) {
887
+ return prev;
888
+ }
889
+ ignoreResizeRef.current = true;
890
+ return nextLayout;
891
+ });
892
+ }, []);
893
+ const evaluateLayout = useCallback(() => {
894
+ if (mode !== "input") {
895
+ updateLayout("compact");
896
+ return;
897
+ }
898
+ if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
899
+ const isMobileViewport = window.matchMedia("(max-width: 767px)").matches;
900
+ if (isMobileViewport) {
901
+ ensureMeasurements();
902
+ adjustTextareaHeight();
903
+ updateLayout("expanded");
904
+ return;
905
+ }
906
+ }
907
+ const textarea = inputRef.current;
908
+ const grid = gridRef.current;
909
+ const addContainer = addButtonContainerRef.current;
910
+ const actionsContainer = actionsContainerRef.current;
911
+ if (!textarea || !grid || !addContainer || !actionsContainer) {
912
+ return;
913
+ }
914
+ if (measurementsRef.current.singleLineHeight === 0) {
915
+ ensureMeasurements();
916
+ }
917
+ const scrollHeight = adjustTextareaHeight();
918
+ const baseline = measurementsRef.current.singleLineHeight;
919
+ const hasExplicitBreak = resolvedValue.includes("\n");
920
+ const renderedMultiline = baseline > 0 ? scrollHeight > baseline + 1 : false;
921
+ let shouldExpand = hasExplicitBreak || renderedMultiline;
922
+ if (!shouldExpand) {
923
+ const gridStyles = window.getComputedStyle(grid);
924
+ const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
925
+ const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
926
+ const columnGap = parseFloat(gridStyles.columnGap) || 0;
927
+ const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
928
+ if (gridAvailableWidth > 0) {
929
+ const addWidth = addContainer.getBoundingClientRect().width;
930
+ const actionsWidth = actionsContainer.getBoundingClientRect().width;
931
+ const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
932
+ const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
933
+ if (!measurementCanvasRef.current) {
934
+ measurementCanvasRef.current = canvas;
935
+ }
936
+ const context = canvas.getContext("2d");
937
+ if (context) {
938
+ const textareaStyles = window.getComputedStyle(textarea);
939
+ const font = textareaStyles.font || `${textareaStyles.fontStyle} ${textareaStyles.fontVariant} ${textareaStyles.fontWeight} ${textareaStyles.fontSize}/${textareaStyles.lineHeight} ${textareaStyles.fontFamily}`;
940
+ context.font = font;
941
+ const compactInnerWidth = Math.max(
942
+ compactWidth - (measurementsRef.current.paddingLeft || 0) - (measurementsRef.current.paddingRight || 0),
943
+ 0
944
+ );
945
+ if (compactInnerWidth > 0) {
946
+ const lines = resolvedValue.length > 0 ? resolvedValue.split("\n") : [""];
947
+ let longestWidth = 0;
948
+ for (const line of lines) {
949
+ const metrics = context.measureText(line || " ");
950
+ if (metrics.width > longestWidth) {
951
+ longestWidth = metrics.width;
952
+ }
953
+ }
954
+ if (longestWidth > compactInnerWidth) {
955
+ shouldExpand = true;
956
+ }
957
+ }
958
+ }
959
+ }
960
+ }
961
+ const nextLayout = shouldExpand ? "expanded" : "compact";
962
+ updateLayout(nextLayout);
963
+ }, [adjustTextareaHeight, ensureMeasurements, mode, resolvedValue, updateLayout]);
964
+ useLayoutEffect(() => {
965
+ evaluateLayout();
966
+ }, [evaluateLayout]);
967
+ useEffect2(() => {
968
+ if (typeof ResizeObserver === "undefined") {
969
+ return;
970
+ }
971
+ const textarea = inputRef.current;
972
+ const grid = gridRef.current;
973
+ const addContainer = addButtonContainerRef.current;
974
+ const actionsContainer = actionsContainerRef.current;
975
+ if (!textarea || !grid || !addContainer || !actionsContainer) {
976
+ return;
977
+ }
978
+ const scheduleEvaluation = () => {
979
+ if (ignoreResizeRef.current) {
980
+ ignoreResizeRef.current = false;
981
+ return;
982
+ }
983
+ if (typeof window === "undefined") {
984
+ evaluateLayout();
985
+ return;
986
+ }
987
+ if (resizeEvaluationRafRef.current !== null) {
988
+ cancelAnimationFrame(resizeEvaluationRafRef.current);
989
+ }
990
+ resizeEvaluationRafRef.current = window.requestAnimationFrame(() => {
991
+ resizeEvaluationRafRef.current = null;
992
+ evaluateLayout();
993
+ });
994
+ };
995
+ const observer = new ResizeObserver(() => {
996
+ scheduleEvaluation();
997
+ });
998
+ observer.observe(grid);
999
+ observer.observe(addContainer);
1000
+ observer.observe(actionsContainer);
1001
+ observer.observe(textarea);
1002
+ return () => {
1003
+ observer.disconnect();
1004
+ if (typeof window !== "undefined" && resizeEvaluationRafRef.current !== null) {
1005
+ cancelAnimationFrame(resizeEvaluationRafRef.current);
1006
+ resizeEvaluationRafRef.current = null;
1007
+ }
1008
+ };
1009
+ }, [evaluateLayout]);
1010
+ const slashMenuVisible = commandQuery !== null && commandItems.length > 0;
1011
+ useEffect2(() => {
1012
+ if (!slashMenuVisible || slashHighlightIndex < 0) {
1013
+ return;
1014
+ }
1015
+ const active = slashMenuRef.current?.querySelector(
1016
+ `[data-slash-index="${slashHighlightIndex}"]`
1017
+ );
1018
+ active?.scrollIntoView({ block: "nearest" });
1019
+ }, [slashMenuVisible, slashHighlightIndex]);
1020
+ const slashMenu = slashMenuVisible ? /* @__PURE__ */ jsx6(
1021
+ "div",
1022
+ {
1023
+ "data-testid": "copilot-slash-menu",
1024
+ role: "listbox",
1025
+ "aria-label": "Slash commands",
1026
+ ref: slashMenuRef,
1027
+ className: "absolute bottom-full left-0 right-0 z-30 mb-2 max-h-64 overflow-y-auto rounded-lg border border-border bg-white shadow-lg dark:border-[#3a3a3a] dark:bg-[#1f1f1f]",
1028
+ style: { maxHeight: `${SLASH_MENU_MAX_VISIBLE_ITEMS * SLASH_MENU_ITEM_HEIGHT_PX}px` },
1029
+ children: filteredCommands.length === 0 ? /* @__PURE__ */ jsx6("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: "No commands found" }) : filteredCommands.map((item, index) => {
1030
+ const isActive = index === slashHighlightIndex;
1031
+ return /* @__PURE__ */ jsx6(
1032
+ "button",
1033
+ {
1034
+ type: "button",
1035
+ role: "option",
1036
+ "aria-selected": isActive,
1037
+ "data-active": isActive ? "true" : void 0,
1038
+ "data-slash-index": index,
1039
+ className: twMerge3(
1040
+ "w-full px-3 py-2 text-left text-sm transition-colors",
1041
+ "hover:bg-muted dark:hover:bg-[#2f2f2f]",
1042
+ isActive ? "bg-muted dark:bg-[#2f2f2f]" : "bg-transparent"
1043
+ ),
1044
+ onMouseEnter: () => setSlashHighlightIndex(index),
1045
+ onMouseDown: (event) => {
1046
+ event.preventDefault();
1047
+ runCommand(item);
1048
+ },
1049
+ children: item.label
1050
+ },
1051
+ `${item.label}-${index}`
1052
+ );
1053
+ })
1054
+ }
1055
+ ) : null;
1056
+ return /* @__PURE__ */ jsx6(
666
1057
  "div",
667
1058
  {
668
1059
  className: twMerge3(
@@ -680,10 +1071,62 @@ function CopilotChatInput({
680
1071
  ),
681
1072
  onClick: handleContainerClick,
682
1073
  ...props,
683
- children: [
684
- mode === "transcribe" ? BoundAudioRecorder : BoundTextArea,
685
- BoundToolbar
686
- ]
1074
+ "data-layout": isExpanded ? "expanded" : "compact",
1075
+ children: /* @__PURE__ */ jsxs3(
1076
+ "div",
1077
+ {
1078
+ ref: gridRef,
1079
+ className: twMerge3(
1080
+ "grid w-full gap-x-3 gap-y-3 px-3 py-2",
1081
+ isExpanded ? "grid-cols-[auto_minmax(0,1fr)_auto] grid-rows-[auto_auto]" : "grid-cols-[auto_minmax(0,1fr)_auto] items-center"
1082
+ ),
1083
+ "data-layout": isExpanded ? "expanded" : "compact",
1084
+ children: [
1085
+ /* @__PURE__ */ jsx6(
1086
+ "div",
1087
+ {
1088
+ ref: addButtonContainerRef,
1089
+ className: twMerge3(
1090
+ "flex items-center",
1091
+ isExpanded ? "row-start-2" : "row-start-1",
1092
+ "col-start-1"
1093
+ ),
1094
+ children: BoundAddMenuButton
1095
+ }
1096
+ ),
1097
+ /* @__PURE__ */ jsx6(
1098
+ "div",
1099
+ {
1100
+ className: twMerge3(
1101
+ "relative flex min-w-0 flex-col",
1102
+ isExpanded ? "col-span-3 row-start-1" : "col-start-2 row-start-1"
1103
+ ),
1104
+ children: mode === "transcribe" ? BoundAudioRecorder : /* @__PURE__ */ jsxs3(Fragment, { children: [
1105
+ BoundTextArea,
1106
+ slashMenu
1107
+ ] })
1108
+ }
1109
+ ),
1110
+ /* @__PURE__ */ jsx6(
1111
+ "div",
1112
+ {
1113
+ ref: actionsContainerRef,
1114
+ className: twMerge3(
1115
+ "flex items-center justify-end gap-2",
1116
+ isExpanded ? "col-start-3 row-start-2" : "col-start-3 row-start-1"
1117
+ ),
1118
+ children: mode === "transcribe" ? /* @__PURE__ */ jsxs3(Fragment, { children: [
1119
+ onCancelTranscribe && BoundCancelTranscribeButton,
1120
+ onFinishTranscribe && BoundFinishTranscribeButton
1121
+ ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
1122
+ onStartTranscribe && BoundStartTranscribeButton,
1123
+ BoundSendButton
1124
+ ] })
1125
+ }
1126
+ )
1127
+ ]
1128
+ }
1129
+ )
687
1130
  }
688
1131
  );
689
1132
  }
@@ -744,124 +1187,110 @@ function CopilotChatInput({
744
1187
  ...props
745
1188
  }
746
1189
  );
747
- CopilotChatInput2.AddFileButton = (props) => /* @__PURE__ */ jsx6(
748
- CopilotChatInput2.ToolbarButton,
749
- {
750
- icon: /* @__PURE__ */ jsx6(Plus, { className: "size-[20px]" }),
751
- labelKey: "chatInputToolbarAddButtonLabel",
752
- defaultClassName: "ml-2",
753
- ...props
754
- }
755
- );
756
- CopilotChatInput2.ToolsButton = ({ className, toolsMenu, ...props }) => {
1190
+ CopilotChatInput2.AddMenuButton = ({ className, toolsMenu, onAddFile, disabled, ...props }) => {
757
1191
  const config = useCopilotChatConfiguration();
758
1192
  const labels = config?.labels ?? CopilotChatDefaultLabels;
759
- const renderMenuItems = (items) => {
760
- return items.map((item, index) => {
1193
+ const menuItems = useMemo2(() => {
1194
+ const items = [];
1195
+ if (onAddFile) {
1196
+ items.push({
1197
+ label: labels.chatInputToolbarAddButtonLabel,
1198
+ action: onAddFile
1199
+ });
1200
+ }
1201
+ if (toolsMenu && toolsMenu.length > 0) {
1202
+ if (items.length > 0) {
1203
+ items.push("-");
1204
+ }
1205
+ for (const item of toolsMenu) {
1206
+ if (item === "-") {
1207
+ if (items.length === 0 || items[items.length - 1] === "-") {
1208
+ continue;
1209
+ }
1210
+ items.push(item);
1211
+ } else {
1212
+ items.push(item);
1213
+ }
1214
+ }
1215
+ while (items.length > 0 && items[items.length - 1] === "-") {
1216
+ items.pop();
1217
+ }
1218
+ }
1219
+ return items;
1220
+ }, [onAddFile, toolsMenu, labels.chatInputToolbarAddButtonLabel]);
1221
+ const renderMenuItems = useCallback(
1222
+ (items) => items.map((item, index) => {
761
1223
  if (item === "-") {
762
- return /* @__PURE__ */ jsx6(DropdownMenuSeparator, {}, index);
763
- } else if (item.items && item.items.length > 0) {
1224
+ return /* @__PURE__ */ jsx6(DropdownMenuSeparator, {}, `separator-${index}`);
1225
+ }
1226
+ if (item.items && item.items.length > 0) {
764
1227
  return /* @__PURE__ */ jsxs3(DropdownMenuSub, { children: [
765
1228
  /* @__PURE__ */ jsx6(DropdownMenuSubTrigger, { children: item.label }),
766
1229
  /* @__PURE__ */ jsx6(DropdownMenuSubContent, { children: renderMenuItems(item.items) })
767
- ] }, index);
768
- } else {
769
- return /* @__PURE__ */ jsx6(DropdownMenuItem, { onClick: item.action, children: item.label }, index);
1230
+ ] }, `group-${index}`);
770
1231
  }
771
- });
772
- };
773
- if (!toolsMenu || toolsMenu.length === 0) {
774
- return null;
775
- }
1232
+ return /* @__PURE__ */ jsx6(DropdownMenuItem, { onClick: item.action, children: item.label }, `item-${index}`);
1233
+ }),
1234
+ []
1235
+ );
1236
+ const hasMenuItems = menuItems.length > 0;
1237
+ const isDisabled = disabled || !hasMenuItems;
776
1238
  return /* @__PURE__ */ jsxs3(DropdownMenu, { children: [
777
- /* @__PURE__ */ jsx6(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs3(
778
- Button,
779
- {
780
- type: "button",
781
- variant: "chatInputToolbarSecondary",
782
- size: "chatInputToolbarIconLabel",
783
- className,
784
- ...props,
785
- children: [
786
- /* @__PURE__ */ jsx6(Settings2, { className: "size-[18px]" }),
787
- /* @__PURE__ */ jsx6("span", { className: "text-sm font-normal", children: labels.chatInputToolbarToolsButtonLabel })
788
- ]
789
- }
790
- ) }),
791
- /* @__PURE__ */ jsx6(DropdownMenuContent, { side: "top", align: "end", children: renderMenuItems(toolsMenu) })
1239
+ /* @__PURE__ */ jsxs3(Tooltip, { children: [
1240
+ /* @__PURE__ */ jsx6(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx6(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx6(
1241
+ Button,
1242
+ {
1243
+ type: "button",
1244
+ variant: "chatInputToolbarSecondary",
1245
+ size: "chatInputToolbarIcon",
1246
+ className: twMerge3("ml-1", className),
1247
+ disabled: isDisabled,
1248
+ ...props,
1249
+ children: /* @__PURE__ */ jsx6(Plus, { className: "size-[20px]" })
1250
+ }
1251
+ ) }) }),
1252
+ /* @__PURE__ */ jsx6(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxs3("p", { className: "flex items-center gap-1 text-xs font-medium", children: [
1253
+ /* @__PURE__ */ jsx6("span", { children: "Add files and more" }),
1254
+ /* @__PURE__ */ jsx6("code", { className: "rounded bg-[#4a4a4a] px-1 py-[1px] font-mono text-[11px] text-white dark:bg-[#e0e0e0] dark:text-black", children: "/" })
1255
+ ] }) })
1256
+ ] }),
1257
+ hasMenuItems && /* @__PURE__ */ jsx6(DropdownMenuContent, { side: "top", align: "start", children: renderMenuItems(menuItems) })
792
1258
  ] });
793
1259
  };
794
- CopilotChatInput2.Toolbar = ({ className, ...props }) => /* @__PURE__ */ jsx6("div", { className: twMerge3("w-full h-[60px] bg-transparent flex items-center", className), ...props });
795
- CopilotChatInput2.TextArea = forwardRef2(function TextArea2({ maxRows = 5, style, className, ...props }, ref) {
1260
+ CopilotChatInput2.TextArea = forwardRef2(function TextArea2({ style, className, autoFocus, ...props }, ref) {
796
1261
  const internalTextareaRef = useRef2(null);
797
- const [maxHeight, setMaxHeight] = useState2(0);
798
1262
  const config = useCopilotChatConfiguration();
799
1263
  const labels = config?.labels ?? CopilotChatDefaultLabels;
800
1264
  useImperativeHandle2(ref, () => internalTextareaRef.current);
801
- const adjustHeight = () => {
802
- const textarea = internalTextareaRef.current;
803
- if (textarea && maxHeight > 0) {
804
- textarea.style.height = "auto";
805
- textarea.style.height = `${Math.min(textarea.scrollHeight, maxHeight)}px`;
806
- }
807
- };
808
1265
  useEffect2(() => {
809
- const calculateMaxHeight = () => {
810
- const textarea = internalTextareaRef.current;
811
- if (textarea) {
812
- const currentValue = textarea.value;
813
- textarea.value = "";
814
- textarea.style.height = "auto";
815
- const computedStyle = window.getComputedStyle(textarea);
816
- const paddingTop = parseFloat(computedStyle.paddingTop);
817
- const paddingBottom = parseFloat(computedStyle.paddingBottom);
818
- const contentHeight = textarea.scrollHeight - paddingTop - paddingBottom;
819
- setMaxHeight(contentHeight * maxRows + paddingTop + paddingBottom);
820
- textarea.value = currentValue;
821
- if (currentValue) {
822
- textarea.style.height = "auto";
823
- textarea.style.height = `${Math.min(textarea.scrollHeight, contentHeight * maxRows + paddingTop + paddingBottom)}px`;
824
- }
825
- if (props.autoFocus) {
826
- textarea.focus();
827
- }
828
- }
1266
+ const textarea = internalTextareaRef.current;
1267
+ if (!textarea) return;
1268
+ const handleFocus = () => {
1269
+ setTimeout(() => {
1270
+ textarea.scrollIntoView({ behavior: "smooth", block: "nearest" });
1271
+ }, 300);
829
1272
  };
830
- calculateMaxHeight();
831
- }, [maxRows, props.autoFocus]);
1273
+ textarea.addEventListener("focus", handleFocus);
1274
+ return () => textarea.removeEventListener("focus", handleFocus);
1275
+ }, []);
832
1276
  useEffect2(() => {
833
- adjustHeight();
834
- }, [props.value, maxHeight]);
835
- const handleInput = (e) => {
836
- adjustHeight();
837
- if (props.onChange) {
838
- props.onChange(e);
1277
+ if (autoFocus) {
1278
+ internalTextareaRef.current?.focus();
839
1279
  }
840
- };
1280
+ }, [autoFocus]);
841
1281
  return /* @__PURE__ */ jsx6(
842
1282
  "textarea",
843
1283
  {
844
1284
  ref: internalTextareaRef,
845
1285
  ...props,
846
- onChange: handleInput,
847
1286
  style: {
848
1287
  overflow: "auto",
849
1288
  resize: "none",
850
- maxHeight: `${maxHeight}px`,
851
1289
  ...style
852
1290
  },
853
1291
  placeholder: labels.chatInputPlaceholder,
854
1292
  className: twMerge3(
855
- // Layout and sizing
856
- "w-full p-5 pb-0",
857
- // Behavior
858
- "outline-none resize-none",
859
- // Background
860
- "bg-transparent",
861
- // Typography
862
- "antialiased font-regular leading-relaxed text-[16px]",
863
- // Placeholder styles
864
- "placeholder:text-[#00000077] dark:placeholder:text-[#fffc]",
1293
+ "bg-transparent outline-none antialiased font-regular leading-relaxed text-[16px] placeholder:text-[#00000077] dark:placeholder:text-[#fffc]",
865
1294
  className
866
1295
  ),
867
1296
  rows: 1
@@ -876,9 +1305,7 @@ CopilotChatInput.ToolbarButton.displayName = "CopilotChatInput.ToolbarButton";
876
1305
  CopilotChatInput.StartTranscribeButton.displayName = "CopilotChatInput.StartTranscribeButton";
877
1306
  CopilotChatInput.CancelTranscribeButton.displayName = "CopilotChatInput.CancelTranscribeButton";
878
1307
  CopilotChatInput.FinishTranscribeButton.displayName = "CopilotChatInput.FinishTranscribeButton";
879
- CopilotChatInput.AddFileButton.displayName = "CopilotChatInput.AddButton";
880
- CopilotChatInput.ToolsButton.displayName = "CopilotChatInput.ToolsButton";
881
- CopilotChatInput.Toolbar.displayName = "CopilotChatInput.Toolbar";
1308
+ CopilotChatInput.AddMenuButton.displayName = "CopilotChatInput.AddMenuButton";
882
1309
  var CopilotChatInput_default = CopilotChatInput;
883
1310
 
884
1311
  // src/components/chat/CopilotChatAssistantMessage.tsx
@@ -896,14 +1323,14 @@ import "katex/dist/katex.min.css";
896
1323
  import { Streamdown } from "streamdown";
897
1324
 
898
1325
  // src/hooks/use-render-tool-call.tsx
899
- import { useCallback, useEffect as useEffect5, useState as useState4, useSyncExternalStore } from "react";
1326
+ import { useCallback as useCallback2, useEffect as useEffect5, useState as useState4, useSyncExternalStore } from "react";
900
1327
  import { ToolCallStatus } from "@copilotkitnext/core";
901
1328
 
902
1329
  // src/providers/CopilotKitProvider.tsx
903
1330
  import {
904
1331
  createContext as createContext2,
905
1332
  useContext as useContext2,
906
- useMemo as useMemo2,
1333
+ useMemo as useMemo3,
907
1334
  useEffect as useEffect4,
908
1335
  useReducer,
909
1336
  useRef as useRef4,
@@ -992,7 +1419,7 @@ var CopilotKitContext = createContext2({
992
1419
  copilotkit: null
993
1420
  });
994
1421
  function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
995
- const empty = useMemo2(() => [], []);
1422
+ const empty = useMemo3(() => [], []);
996
1423
  const value = prop ?? empty;
997
1424
  const initial = useRef4(value);
998
1425
  useEffect4(() => {
@@ -1057,7 +1484,7 @@ var CopilotKitProvider = ({
1057
1484
  humanInTheLoop,
1058
1485
  "humanInTheLoop must be a stable array. If you want to dynamically add or remove human-in-the-loop tools, use `useHumanInTheLoop` instead."
1059
1486
  );
1060
- const processedHumanInTheLoopTools = useMemo2(() => {
1487
+ const processedHumanInTheLoopTools = useMemo3(() => {
1061
1488
  const processedTools = [];
1062
1489
  const processedRenderToolCalls = [];
1063
1490
  humanInTheLoopList.forEach((tool) => {
@@ -1086,13 +1513,13 @@ var CopilotKitProvider = ({
1086
1513
  });
1087
1514
  return { tools: processedTools, renderToolCalls: processedRenderToolCalls };
1088
1515
  }, [humanInTheLoopList]);
1089
- const allTools = useMemo2(() => {
1516
+ const allTools = useMemo3(() => {
1090
1517
  const tools = [];
1091
1518
  tools.push(...frontendToolsList);
1092
1519
  tools.push(...processedHumanInTheLoopTools.tools);
1093
1520
  return tools;
1094
1521
  }, [frontendToolsList, processedHumanInTheLoopTools]);
1095
- const allRenderToolCalls = useMemo2(() => {
1522
+ const allRenderToolCalls = useMemo3(() => {
1096
1523
  const combined = [...renderToolCallsList];
1097
1524
  frontendToolsList.forEach((tool) => {
1098
1525
  if (tool.render) {
@@ -1109,7 +1536,7 @@ var CopilotKitProvider = ({
1109
1536
  combined.push(...processedHumanInTheLoopTools.renderToolCalls);
1110
1537
  return combined;
1111
1538
  }, [renderToolCallsList, frontendToolsList, processedHumanInTheLoopTools]);
1112
- const copilotkit = useMemo2(() => {
1539
+ const copilotkit = useMemo3(() => {
1113
1540
  const copilotkit2 = new CopilotKitCoreReact({
1114
1541
  runtimeUrl,
1115
1542
  headers,
@@ -1209,7 +1636,7 @@ function useRenderToolCall() {
1209
1636
  });
1210
1637
  return () => unsubscribe();
1211
1638
  }, [copilotkit]);
1212
- const renderToolCall = useCallback(
1639
+ const renderToolCall = useCallback2(
1213
1640
  ({
1214
1641
  toolCall,
1215
1642
  toolMessage
@@ -1358,7 +1785,7 @@ function useFrontendTool(tool) {
1358
1785
  }
1359
1786
 
1360
1787
  // src/hooks/use-human-in-the-loop.tsx
1361
- import { useState as useState5, useCallback as useCallback2, useRef as useRef5, useEffect as useEffect7 } from "react";
1788
+ import { useState as useState5, useCallback as useCallback3, useRef as useRef5, useEffect as useEffect7 } from "react";
1362
1789
  import React7 from "react";
1363
1790
  function useHumanInTheLoop(tool) {
1364
1791
  const { copilotkit } = useCopilotKit();
@@ -1368,20 +1795,20 @@ function useHumanInTheLoop(tool) {
1368
1795
  const statusRef = useRef5(status);
1369
1796
  const resolvePromiseRef = useRef5(null);
1370
1797
  statusRef.current = status;
1371
- const respond = useCallback2(async (result) => {
1798
+ const respond = useCallback3(async (result) => {
1372
1799
  if (resolvePromiseRef.current) {
1373
1800
  resolvePromiseRef.current(result);
1374
1801
  setStatus("complete");
1375
1802
  resolvePromiseRef.current = null;
1376
1803
  }
1377
1804
  }, []);
1378
- const handler = useCallback2(async () => {
1805
+ const handler = useCallback3(async () => {
1379
1806
  return new Promise((resolve) => {
1380
1807
  setStatus("executing");
1381
1808
  resolvePromiseRef.current = resolve;
1382
1809
  });
1383
1810
  }, []);
1384
- const RenderComponent = useCallback2(
1811
+ const RenderComponent = useCallback3(
1385
1812
  (props) => {
1386
1813
  const ToolComponent = tool.render;
1387
1814
  const currentStatus = statusRef.current;
@@ -1433,7 +1860,7 @@ function useHumanInTheLoop(tool) {
1433
1860
  }
1434
1861
 
1435
1862
  // src/hooks/use-agent.tsx
1436
- import { useMemo as useMemo3, useEffect as useEffect8, useReducer as useReducer2 } from "react";
1863
+ import { useMemo as useMemo4, useEffect as useEffect8, useReducer as useReducer2 } from "react";
1437
1864
  import { DEFAULT_AGENT_ID as DEFAULT_AGENT_ID3 } from "@copilotkitnext/shared";
1438
1865
  var ALL_UPDATES = [
1439
1866
  "OnMessagesChanged" /* OnMessagesChanged */,
@@ -1444,11 +1871,11 @@ function useAgent({ agentId, updates } = {}) {
1444
1871
  agentId ??= DEFAULT_AGENT_ID3;
1445
1872
  const { copilotkit } = useCopilotKit();
1446
1873
  const [, forceUpdate] = useReducer2((x) => x + 1, 0);
1447
- const updateFlags = useMemo3(
1874
+ const updateFlags = useMemo4(
1448
1875
  () => updates ?? ALL_UPDATES,
1449
1876
  [JSON.stringify(updates)]
1450
1877
  );
1451
- const agent = useMemo3(() => {
1878
+ const agent = useMemo4(() => {
1452
1879
  return copilotkit.getAgent(agentId);
1453
1880
  }, [
1454
1881
  agentId,
@@ -1508,12 +1935,12 @@ function useAgentContext(context) {
1508
1935
  }
1509
1936
 
1510
1937
  // src/hooks/use-suggestions.tsx
1511
- import { useCallback as useCallback3, useEffect as useEffect10, useMemo as useMemo4, useState as useState6 } from "react";
1938
+ import { useCallback as useCallback4, useEffect as useEffect10, useMemo as useMemo5, useState as useState6 } from "react";
1512
1939
  import { DEFAULT_AGENT_ID as DEFAULT_AGENT_ID4 } from "@copilotkitnext/shared";
1513
1940
  function useSuggestions({ agentId } = {}) {
1514
1941
  const { copilotkit } = useCopilotKit();
1515
1942
  const config = useCopilotChatConfiguration();
1516
- const resolvedAgentId = useMemo4(() => agentId ?? config?.agentId ?? DEFAULT_AGENT_ID4, [agentId, config?.agentId]);
1943
+ const resolvedAgentId = useMemo5(() => agentId ?? config?.agentId ?? DEFAULT_AGENT_ID4, [agentId, config?.agentId]);
1517
1944
  const [suggestions, setSuggestions] = useState6(() => {
1518
1945
  const result = copilotkit.getSuggestions(resolvedAgentId);
1519
1946
  return result.suggestions;
@@ -1557,10 +1984,10 @@ function useSuggestions({ agentId } = {}) {
1557
1984
  unsubscribe();
1558
1985
  };
1559
1986
  }, [copilotkit, resolvedAgentId]);
1560
- const reloadSuggestions = useCallback3(() => {
1987
+ const reloadSuggestions = useCallback4(() => {
1561
1988
  copilotkit.reloadSuggestions(resolvedAgentId);
1562
1989
  }, [copilotkit, resolvedAgentId]);
1563
- const clearSuggestions = useCallback3(() => {
1990
+ const clearSuggestions = useCallback4(() => {
1564
1991
  copilotkit.clearSuggestions(resolvedAgentId);
1565
1992
  }, [copilotkit, resolvedAgentId]);
1566
1993
  return {
@@ -1572,20 +1999,20 @@ function useSuggestions({ agentId } = {}) {
1572
1999
  }
1573
2000
 
1574
2001
  // src/hooks/use-configure-suggestions.tsx
1575
- import { useCallback as useCallback4, useEffect as useEffect11, useMemo as useMemo5, useRef as useRef6 } from "react";
2002
+ import { useCallback as useCallback5, useEffect as useEffect11, useMemo as useMemo6, useRef as useRef6 } from "react";
1576
2003
  import { DEFAULT_AGENT_ID as DEFAULT_AGENT_ID5 } from "@copilotkitnext/shared";
1577
2004
  var EMPTY_DEPS = [];
1578
2005
  function useConfigureSuggestions(config, options) {
1579
2006
  const { copilotkit } = useCopilotKit();
1580
2007
  const chatConfig = useCopilotChatConfiguration();
1581
2008
  const extraDeps = options?.deps ?? EMPTY_DEPS;
1582
- const resolvedConsumerAgentId = useMemo5(() => chatConfig?.agentId ?? DEFAULT_AGENT_ID5, [chatConfig?.agentId]);
1583
- const rawConsumerAgentId = useMemo5(() => config ? config.consumerAgentId : void 0, [config]);
2009
+ const resolvedConsumerAgentId = useMemo6(() => chatConfig?.agentId ?? DEFAULT_AGENT_ID5, [chatConfig?.agentId]);
2010
+ const rawConsumerAgentId = useMemo6(() => config ? config.consumerAgentId : void 0, [config]);
1584
2011
  const normalizationCacheRef = useRef6({
1585
2012
  serialized: null,
1586
2013
  config: null
1587
2014
  });
1588
- const { normalizedConfig, serializedConfig } = useMemo5(() => {
2015
+ const { normalizedConfig, serializedConfig } = useMemo6(() => {
1589
2016
  if (!config) {
1590
2017
  normalizationCacheRef.current = { serialized: null, config: null };
1591
2018
  return { normalizedConfig: null, serializedConfig: null };
@@ -1618,7 +2045,7 @@ function useConfigureSuggestions(config, options) {
1618
2045
  const latestConfigRef = useRef6(null);
1619
2046
  latestConfigRef.current = normalizedConfig;
1620
2047
  const previousSerializedConfigRef = useRef6(null);
1621
- const targetAgentId = useMemo5(() => {
2048
+ const targetAgentId = useMemo6(() => {
1622
2049
  if (!normalizedConfig) {
1623
2050
  return resolvedConsumerAgentId;
1624
2051
  }
@@ -1629,7 +2056,7 @@ function useConfigureSuggestions(config, options) {
1629
2056
  return consumer;
1630
2057
  }, [normalizedConfig, resolvedConsumerAgentId]);
1631
2058
  const isGlobalConfig = rawConsumerAgentId === void 0 || rawConsumerAgentId === "*";
1632
- const requestReload = useCallback4(() => {
2059
+ const requestReload = useCallback5(() => {
1633
2060
  if (!normalizedConfig) {
1634
2061
  return;
1635
2062
  }
@@ -1811,7 +2238,8 @@ function CopilotChatAssistantMessage({
1811
2238
  }
1812
2239
  );
1813
2240
  const hasContent = !!(message.content && message.content.trim().length > 0);
1814
- const shouldShowToolbar = toolbarVisible && hasContent;
2241
+ const isLatestAssistantMessage = message.role === "assistant" && messages?.[messages.length - 1]?.id === message.id;
2242
+ const shouldShowToolbar = toolbarVisible && hasContent && !(isRunning && isLatestAssistantMessage);
1815
2243
  if (children) {
1816
2244
  return /* @__PURE__ */ jsx12(Fragment3, { children: children({
1817
2245
  markdownRenderer: boundMarkdownRenderer,
@@ -2203,7 +2631,7 @@ var CopilotChatUserMessage_default = CopilotChatUserMessage;
2203
2631
  import React9 from "react";
2204
2632
  import { Loader2 } from "lucide-react";
2205
2633
  import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
2206
- var baseClasses = "group inline-flex h-8 items-center gap-1.5 rounded-full border border-border/60 bg-background px-3 text-xs leading-none text-foreground transition-colors cursor-pointer hover:bg-accent/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:text-muted-foreground disabled:hover:bg-background disabled:hover:text-muted-foreground pointer-events-auto";
2634
+ var baseClasses = "group inline-flex h-7 sm:h-8 items-center gap-1 sm:gap-1.5 rounded-full border border-border/60 bg-background px-2.5 sm:px-3 text-[11px] sm:text-xs leading-none text-foreground transition-colors cursor-pointer hover:bg-accent/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:text-muted-foreground disabled:hover:bg-background disabled:hover:text-muted-foreground pointer-events-auto";
2207
2635
  var labelClasses = "whitespace-nowrap font-medium leading-none";
2208
2636
  var CopilotChatSuggestionPill = React9.forwardRef(function CopilotChatSuggestionPill2({ className, children, icon, isLoading, type, ...props }, ref) {
2209
2637
  const showIcon = !isLoading && icon;
@@ -2218,7 +2646,7 @@ var CopilotChatSuggestionPill = React9.forwardRef(function CopilotChatSuggestion
2218
2646
  disabled: isLoading || props.disabled,
2219
2647
  ...props,
2220
2648
  children: [
2221
- isLoading ? /* @__PURE__ */ jsx14("span", { className: "flex h-4 w-4 items-center justify-center text-muted-foreground", children: /* @__PURE__ */ jsx14(Loader2, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" }) }) : showIcon && /* @__PURE__ */ jsx14("span", { className: "flex h-4 w-4 items-center justify-center text-muted-foreground", children: icon }),
2649
+ isLoading ? /* @__PURE__ */ jsx14("span", { className: "flex h-3.5 sm:h-4 w-3.5 sm:w-4 items-center justify-center text-muted-foreground", children: /* @__PURE__ */ jsx14(Loader2, { className: "h-3.5 sm:h-4 w-3.5 sm:w-4 animate-spin", "aria-hidden": "true" }) }) : showIcon && /* @__PURE__ */ jsx14("span", { className: "flex h-3.5 sm:h-4 w-3.5 sm:w-4 items-center justify-center text-muted-foreground", children: icon }),
2222
2650
  /* @__PURE__ */ jsx14("span", { className: labelClasses, children })
2223
2651
  ]
2224
2652
  }
@@ -2236,7 +2664,7 @@ var DefaultContainer = React10.forwardRef(function DefaultContainer2({ className
2236
2664
  {
2237
2665
  ref,
2238
2666
  className: cn(
2239
- "flex flex-wrap items-center gap-2 px-4 sm:px-0 pointer-events-none",
2667
+ "flex flex-wrap items-center gap-1.5 sm:gap-2 pl-0 pr-4 sm:px-0 pointer-events-none",
2240
2668
  className
2241
2669
  ),
2242
2670
  ...props
@@ -2379,10 +2807,52 @@ CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
2379
2807
  var CopilotChatMessageView_default = CopilotChatMessageView;
2380
2808
 
2381
2809
  // src/components/chat/CopilotChatView.tsx
2382
- import React11, { useRef as useRef7, useState as useState9, useEffect as useEffect12 } from "react";
2810
+ import React11, { useRef as useRef7, useState as useState10, useEffect as useEffect13 } from "react";
2383
2811
  import { twMerge as twMerge7 } from "tailwind-merge";
2384
2812
  import { StickToBottom, useStickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
2385
2813
  import { ChevronDown } from "lucide-react";
2814
+
2815
+ // src/hooks/use-keyboard-height.tsx
2816
+ import { useState as useState9, useEffect as useEffect12 } from "react";
2817
+ function useKeyboardHeight() {
2818
+ const [keyboardState, setKeyboardState] = useState9({
2819
+ isKeyboardOpen: false,
2820
+ keyboardHeight: 0,
2821
+ availableHeight: typeof window !== "undefined" ? window.innerHeight : 0,
2822
+ viewportHeight: typeof window !== "undefined" ? window.innerHeight : 0
2823
+ });
2824
+ useEffect12(() => {
2825
+ if (typeof window === "undefined") {
2826
+ return;
2827
+ }
2828
+ const visualViewport = window.visualViewport;
2829
+ if (!visualViewport) {
2830
+ return;
2831
+ }
2832
+ const updateKeyboardState = () => {
2833
+ const layoutHeight = window.innerHeight;
2834
+ const visualHeight = visualViewport.height;
2835
+ const keyboardHeight = Math.max(0, layoutHeight - visualHeight);
2836
+ const isKeyboardOpen = keyboardHeight > 150;
2837
+ setKeyboardState({
2838
+ isKeyboardOpen,
2839
+ keyboardHeight,
2840
+ availableHeight: visualHeight,
2841
+ viewportHeight: layoutHeight
2842
+ });
2843
+ };
2844
+ updateKeyboardState();
2845
+ visualViewport.addEventListener("resize", updateKeyboardState);
2846
+ visualViewport.addEventListener("scroll", updateKeyboardState);
2847
+ return () => {
2848
+ visualViewport.removeEventListener("resize", updateKeyboardState);
2849
+ visualViewport.removeEventListener("scroll", updateKeyboardState);
2850
+ };
2851
+ }, []);
2852
+ return keyboardState;
2853
+ }
2854
+
2855
+ // src/components/chat/CopilotChatView.tsx
2386
2856
  import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
2387
2857
  function CopilotChatView({
2388
2858
  messageView,
@@ -2405,10 +2875,11 @@ function CopilotChatView({
2405
2875
  ...props
2406
2876
  }) {
2407
2877
  const inputContainerRef = useRef7(null);
2408
- const [inputContainerHeight, setInputContainerHeight] = useState9(0);
2409
- const [isResizing, setIsResizing] = useState9(false);
2878
+ const [inputContainerHeight, setInputContainerHeight] = useState10(0);
2879
+ const [isResizing, setIsResizing] = useState10(false);
2410
2880
  const resizeTimeoutRef = useRef7(null);
2411
- useEffect12(() => {
2881
+ const { isKeyboardOpen, keyboardHeight, availableHeight } = useKeyboardHeight();
2882
+ useEffect13(() => {
2412
2883
  const element = inputContainerRef.current;
2413
2884
  if (!element) return;
2414
2885
  const resizeObserver = new ResizeObserver((entries) => {
@@ -2462,15 +2933,16 @@ function CopilotChatView({
2462
2933
  isResizing,
2463
2934
  children: /* @__PURE__ */ jsx17("div", { style: { paddingBottom: `${inputContainerHeight + (hasSuggestions ? 4 : 32)}px` }, children: /* @__PURE__ */ jsxs10("div", { className: "max-w-3xl mx-auto", children: [
2464
2935
  BoundMessageView,
2465
- hasSuggestions ? /* @__PURE__ */ jsx17("div", { className: "px-4 sm:px-0 mt-4", children: BoundSuggestionView }) : null
2936
+ hasSuggestions ? /* @__PURE__ */ jsx17("div", { className: "pl-0 pr-4 sm:px-0 mt-4", children: BoundSuggestionView }) : null
2466
2937
  ] }) })
2467
2938
  });
2468
2939
  const BoundScrollToBottomButton = renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, {});
2469
2940
  const BoundDisclaimer = renderSlot(disclaimer, CopilotChatView.Disclaimer, {});
2470
2941
  const BoundInputContainer = renderSlot(inputContainer, CopilotChatView.InputContainer, {
2471
2942
  ref: inputContainerRef,
2943
+ keyboardHeight: isKeyboardOpen ? keyboardHeight : 0,
2472
2944
  children: /* @__PURE__ */ jsxs10(Fragment6, { children: [
2473
- /* @__PURE__ */ jsx17("div", { className: "max-w-3xl mx-auto py-0 px-4 sm:px-0 [div[data-sidebar-chat]_&]:px-8 pointer-events-auto", children: BoundInput }),
2945
+ /* @__PURE__ */ jsx17("div", { className: "max-w-3xl mx-auto py-0 px-4 sm:px-0 [div[data-sidebar-chat]_&]:px-8 [div[data-popup-chat]_&]:px-6 pointer-events-auto", children: BoundInput }),
2474
2946
  BoundDisclaimer
2475
2947
  ] })
2476
2948
  });
@@ -2496,7 +2968,7 @@ function CopilotChatView({
2496
2968
  const ScrollContent = ({ children, scrollToBottomButton, inputContainerHeight, isResizing }) => {
2497
2969
  const { isAtBottom, scrollToBottom } = useStickToBottomContext();
2498
2970
  return /* @__PURE__ */ jsxs10(Fragment6, { children: [
2499
- /* @__PURE__ */ jsx17(StickToBottom.Content, { className: "overflow-y-scroll overflow-x-hidden", children: /* @__PURE__ */ jsx17("div", { className: "px-4 sm:px-0 [div[data-sidebar-chat]_&]:px-8", children }) }),
2971
+ /* @__PURE__ */ jsx17(StickToBottom.Content, { className: "overflow-y-scroll overflow-x-hidden", children: /* @__PURE__ */ jsx17("div", { className: "px-4 sm:px-0 [div[data-sidebar-chat]_&]:px-8 [div[data-popup-chat]_&]:px-6", children }) }),
2500
2972
  !isAtBottom && !isResizing && /* @__PURE__ */ jsx17(
2501
2973
  "div",
2502
2974
  {
@@ -2520,13 +2992,13 @@ function CopilotChatView({
2520
2992
  className,
2521
2993
  ...props
2522
2994
  }) => {
2523
- const [hasMounted, setHasMounted] = useState9(false);
2995
+ const [hasMounted, setHasMounted] = useState10(false);
2524
2996
  const { scrollRef, contentRef, scrollToBottom } = useStickToBottom();
2525
- const [showScrollButton, setShowScrollButton] = useState9(false);
2526
- useEffect12(() => {
2997
+ const [showScrollButton, setShowScrollButton] = useState10(false);
2998
+ useEffect13(() => {
2527
2999
  setHasMounted(true);
2528
3000
  }, []);
2529
- useEffect12(() => {
3001
+ useEffect13(() => {
2530
3002
  if (autoScroll) return;
2531
3003
  const scrollElement = scrollRef.current;
2532
3004
  if (!scrollElement) return;
@@ -2544,7 +3016,7 @@ function CopilotChatView({
2544
3016
  };
2545
3017
  }, [scrollRef, autoScroll]);
2546
3018
  if (!hasMounted) {
2547
- return /* @__PURE__ */ jsx17("div", { className: "h-full max-h-full flex flex-col min-h-0 overflow-y-scroll overflow-x-hidden", children: /* @__PURE__ */ jsx17("div", { className: "px-4 sm:px-0 [div[data-sidebar-chat]_&]:px-8", children }) });
3019
+ return /* @__PURE__ */ jsx17("div", { className: "h-full max-h-full flex flex-col min-h-0 overflow-y-scroll overflow-x-hidden", children: /* @__PURE__ */ jsx17("div", { className: "px-4 sm:px-0 [div[data-sidebar-chat]_&]:px-8 [div[data-popup-chat]_&]:px-6", children }) });
2548
3020
  }
2549
3021
  if (!autoScroll) {
2550
3022
  return /* @__PURE__ */ jsxs10(
@@ -2557,7 +3029,7 @@ function CopilotChatView({
2557
3029
  ),
2558
3030
  ...props,
2559
3031
  children: [
2560
- /* @__PURE__ */ jsx17("div", { ref: contentRef, className: "px-4 sm:px-0 [div[data-sidebar-chat]_&]:px-8", children }),
3032
+ /* @__PURE__ */ jsx17("div", { ref: contentRef, className: "px-4 sm:px-0 [div[data-sidebar-chat]_&]:px-8 [div[data-popup-chat]_&]:px-6", children }),
2561
3033
  showScrollButton && !isResizing && /* @__PURE__ */ jsx17(
2562
3034
  "div",
2563
3035
  {
@@ -2626,7 +3098,20 @@ function CopilotChatView({
2626
3098
  ...props
2627
3099
  }
2628
3100
  );
2629
- CopilotChatView2.InputContainer = React11.forwardRef(({ children, className, ...props }, ref) => /* @__PURE__ */ jsx17("div", { ref, className: cn("absolute bottom-0 left-0 right-0 z-20 pointer-events-none", className), ...props, children }));
3101
+ CopilotChatView2.InputContainer = React11.forwardRef(({ children, className, keyboardHeight = 0, ...props }, ref) => /* @__PURE__ */ jsx17(
3102
+ "div",
3103
+ {
3104
+ ref,
3105
+ className: cn("absolute bottom-0 left-0 right-0 z-20 pointer-events-none", className),
3106
+ style: {
3107
+ // Adjust position when keyboard is open to keep input visible
3108
+ transform: keyboardHeight > 0 ? `translateY(-${keyboardHeight}px)` : void 0,
3109
+ transition: "transform 0.2s ease-out"
3110
+ },
3111
+ ...props,
3112
+ children
3113
+ }
3114
+ ));
2630
3115
  CopilotChatView2.InputContainer.displayName = "CopilotChatView.InputContainer";
2631
3116
  CopilotChatView2.Disclaimer = ({ className, ...props }) => {
2632
3117
  const config = useCopilotChatConfiguration();
@@ -2645,14 +3130,14 @@ var CopilotChatView_default = CopilotChatView;
2645
3130
 
2646
3131
  // src/components/chat/CopilotChat.tsx
2647
3132
  import { DEFAULT_AGENT_ID as DEFAULT_AGENT_ID6, randomUUID as randomUUID2 } from "@copilotkitnext/shared";
2648
- import { useCallback as useCallback5, useEffect as useEffect13, useMemo as useMemo6 } from "react";
3133
+ import { useCallback as useCallback6, useEffect as useEffect14, useMemo as useMemo7 } from "react";
2649
3134
  import { merge } from "ts-deepmerge";
2650
3135
  import { AGUIConnectNotImplementedError } from "@ag-ui/client";
2651
3136
  import { jsx as jsx18 } from "react/jsx-runtime";
2652
3137
  function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, ...props }) {
2653
3138
  const existingConfig = useCopilotChatConfiguration();
2654
3139
  const resolvedAgentId = agentId ?? existingConfig?.agentId ?? DEFAULT_AGENT_ID6;
2655
- const resolvedThreadId = useMemo6(
3140
+ const resolvedThreadId = useMemo7(
2656
3141
  () => threadId ?? existingConfig?.threadId ?? randomUUID2(),
2657
3142
  [threadId, existingConfig?.threadId]
2658
3143
  );
@@ -2665,7 +3150,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
2665
3150
  suggestionView: providedSuggestionView,
2666
3151
  ...restProps
2667
3152
  } = props;
2668
- useEffect13(() => {
3153
+ useEffect14(() => {
2669
3154
  const connect = async (agent2) => {
2670
3155
  try {
2671
3156
  await copilotkit.connectAgent({ agent: agent2 });
@@ -2683,7 +3168,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
2683
3168
  return () => {
2684
3169
  };
2685
3170
  }, [resolvedThreadId, agent, copilotkit, resolvedAgentId]);
2686
- const onSubmitInput = useCallback5(
3171
+ const onSubmitInput = useCallback6(
2687
3172
  async (value) => {
2688
3173
  agent?.addMessage({
2689
3174
  id: randomUUID2(),
@@ -2700,7 +3185,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
2700
3185
  },
2701
3186
  [agent, copilotkit]
2702
3187
  );
2703
- const handleSelectSuggestion = useCallback5(
3188
+ const handleSelectSuggestion = useCallback6(
2704
3189
  async (suggestion) => {
2705
3190
  if (!agent) {
2706
3191
  return;
@@ -2754,7 +3239,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
2754
3239
  })(CopilotChat || (CopilotChat = {}));
2755
3240
 
2756
3241
  // src/components/chat/CopilotChatToggleButton.tsx
2757
- import React12, { useState as useState10 } from "react";
3242
+ import React12, { useState as useState11 } from "react";
2758
3243
  import { MessageCircle, X as X2 } from "lucide-react";
2759
3244
  import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
2760
3245
  var DefaultOpenIcon = ({
@@ -2785,7 +3270,7 @@ var CopilotChatToggleButton = React12.forwardRef(function CopilotChatToggleButto
2785
3270
  const { onClick, type, disabled, ...restProps } = buttonProps;
2786
3271
  const configuration = useCopilotChatConfiguration();
2787
3272
  const labels = configuration?.labels ?? CopilotChatDefaultLabels;
2788
- const [fallbackOpen, setFallbackOpen] = useState10(false);
3273
+ const [fallbackOpen, setFallbackOpen] = useState11(false);
2789
3274
  const isOpen = configuration?.isModalOpen ?? fallbackOpen;
2790
3275
  const setModalOpen = configuration?.setModalOpen ?? setFallbackOpen;
2791
3276
  const handleClick = (event) => {
@@ -2871,10 +3356,10 @@ CopilotChatToggleButton.displayName = "CopilotChatToggleButton";
2871
3356
  var CopilotChatToggleButton_default = CopilotChatToggleButton;
2872
3357
 
2873
3358
  // src/components/chat/CopilotSidebarView.tsx
2874
- import { useEffect as useEffect14, useRef as useRef8, useState as useState11 } from "react";
3359
+ import { useEffect as useEffect15, useRef as useRef8, useState as useState12 } from "react";
2875
3360
 
2876
3361
  // src/components/chat/CopilotModalHeader.tsx
2877
- import { useCallback as useCallback6 } from "react";
3362
+ import { useCallback as useCallback7 } from "react";
2878
3363
  import { X as X3 } from "lucide-react";
2879
3364
  import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
2880
3365
  function CopilotModalHeader({
@@ -2888,7 +3373,7 @@ function CopilotModalHeader({
2888
3373
  const configuration = useCopilotChatConfiguration();
2889
3374
  const fallbackTitle = configuration?.labels.modalHeaderTitle ?? CopilotChatDefaultLabels.modalHeaderTitle;
2890
3375
  const resolvedTitle = title ?? fallbackTitle;
2891
- const handleClose = useCallback6(() => {
3376
+ const handleClose = useCallback7(() => {
2892
3377
  configuration?.setModalOpen(false);
2893
3378
  }, [configuration]);
2894
3379
  const BoundTitle = renderSlot(titleContent, CopilotModalHeader.Title, {
@@ -2965,7 +3450,7 @@ function CopilotSidebarView({ header, width, ...props }) {
2965
3450
  const configuration = useCopilotChatConfiguration();
2966
3451
  const isSidebarOpen = configuration?.isModalOpen ?? false;
2967
3452
  const sidebarRef = useRef8(null);
2968
- const [sidebarWidth, setSidebarWidth] = useState11(width ?? DEFAULT_SIDEBAR_WIDTH);
3453
+ const [sidebarWidth, setSidebarWidth] = useState12(width ?? DEFAULT_SIDEBAR_WIDTH);
2969
3454
  const widthToCss = (w) => {
2970
3455
  return typeof w === "number" ? `${w}px` : w;
2971
3456
  };
@@ -2975,7 +3460,7 @@ function CopilotSidebarView({ header, width, ...props }) {
2975
3460
  }
2976
3461
  return w;
2977
3462
  };
2978
- useEffect14(() => {
3463
+ useEffect15(() => {
2979
3464
  if (width !== void 0) {
2980
3465
  return;
2981
3466
  }
@@ -3007,10 +3492,13 @@ function CopilotSidebarView({ header, width, ...props }) {
3007
3492
  "style",
3008
3493
  {
3009
3494
  dangerouslySetInnerHTML: {
3010
- __html: `body {
3011
- margin-inline-end: ${widthToMargin(sidebarWidth)};
3012
- transition: margin-inline-end ${SIDEBAR_TRANSITION_MS}ms ease;
3013
- }`
3495
+ __html: `
3496
+ @media (min-width: 768px) {
3497
+ body {
3498
+ margin-inline-end: ${widthToMargin(sidebarWidth)};
3499
+ transition: margin-inline-end ${SIDEBAR_TRANSITION_MS}ms ease;
3500
+ }
3501
+ }`
3014
3502
  }
3015
3503
  }
3016
3504
  ),
@@ -3021,12 +3509,22 @@ function CopilotSidebarView({ header, width, ...props }) {
3021
3509
  ref: sidebarRef,
3022
3510
  "data-copilot-sidebar": true,
3023
3511
  className: cn(
3024
- "fixed right-0 top-0 z-[1200] flex h-dvh max-h-screen",
3512
+ "fixed right-0 top-0 z-[1200] flex",
3513
+ // Height with dvh fallback and safe area support
3514
+ "h-[100vh] h-[100dvh] max-h-screen",
3515
+ // Responsive width: full on mobile, custom on desktop
3516
+ "w-full",
3025
3517
  "border-l border-border bg-background text-foreground shadow-xl",
3026
3518
  "transition-transform duration-300 ease-out",
3027
3519
  isSidebarOpen ? "translate-x-0" : "translate-x-full pointer-events-none"
3028
3520
  ),
3029
- style: { width: widthToCss(sidebarWidth) },
3521
+ style: {
3522
+ // Use CSS custom property for responsive width
3523
+ ["--sidebar-width"]: widthToCss(sidebarWidth),
3524
+ // Safe area insets for iOS
3525
+ paddingTop: "env(safe-area-inset-top)",
3526
+ paddingBottom: "env(safe-area-inset-bottom)"
3527
+ },
3030
3528
  "aria-hidden": !isSidebarOpen,
3031
3529
  "aria-label": "Copilot chat sidebar",
3032
3530
  role: "complementary",
@@ -3040,14 +3538,173 @@ function CopilotSidebarView({ header, width, ...props }) {
3040
3538
  }
3041
3539
  CopilotSidebarView.displayName = "CopilotSidebarView";
3042
3540
 
3541
+ // src/components/chat/CopilotPopupView.tsx
3542
+ import { useEffect as useEffect16, useMemo as useMemo8, useRef as useRef9, useState as useState13 } from "react";
3543
+ import { Fragment as Fragment8, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
3544
+ var DEFAULT_POPUP_WIDTH = 420;
3545
+ var DEFAULT_POPUP_HEIGHT = 560;
3546
+ var dimensionToCss = (value, fallback) => {
3547
+ if (typeof value === "number" && Number.isFinite(value)) {
3548
+ return `${value}px`;
3549
+ }
3550
+ if (typeof value === "string" && value.trim().length > 0) {
3551
+ return value;
3552
+ }
3553
+ return `${fallback}px`;
3554
+ };
3555
+ function CopilotPopupView({
3556
+ header,
3557
+ width,
3558
+ height,
3559
+ clickOutsideToClose,
3560
+ className,
3561
+ ...restProps
3562
+ }) {
3563
+ const configuration = useCopilotChatConfiguration();
3564
+ const isPopupOpen = configuration?.isModalOpen ?? false;
3565
+ const setModalOpen = configuration?.setModalOpen;
3566
+ const labels = configuration?.labels ?? CopilotChatDefaultLabels;
3567
+ const containerRef = useRef9(null);
3568
+ const [isRendered, setIsRendered] = useState13(isPopupOpen);
3569
+ const [isAnimatingOut, setIsAnimatingOut] = useState13(false);
3570
+ useEffect16(() => {
3571
+ if (isPopupOpen) {
3572
+ setIsRendered(true);
3573
+ setIsAnimatingOut(false);
3574
+ return;
3575
+ }
3576
+ if (!isRendered) {
3577
+ return;
3578
+ }
3579
+ setIsAnimatingOut(true);
3580
+ const timeout = setTimeout(() => {
3581
+ setIsRendered(false);
3582
+ setIsAnimatingOut(false);
3583
+ }, 200);
3584
+ return () => clearTimeout(timeout);
3585
+ }, [isPopupOpen, isRendered]);
3586
+ useEffect16(() => {
3587
+ if (!isPopupOpen) {
3588
+ return;
3589
+ }
3590
+ if (typeof window === "undefined") {
3591
+ return;
3592
+ }
3593
+ const handleKeyDown = (event) => {
3594
+ if (event.key === "Escape") {
3595
+ event.preventDefault();
3596
+ setModalOpen?.(false);
3597
+ }
3598
+ };
3599
+ window.addEventListener("keydown", handleKeyDown);
3600
+ return () => window.removeEventListener("keydown", handleKeyDown);
3601
+ }, [isPopupOpen, setModalOpen]);
3602
+ useEffect16(() => {
3603
+ if (!isPopupOpen) {
3604
+ return;
3605
+ }
3606
+ const focusTimer = setTimeout(() => {
3607
+ containerRef.current?.focus({ preventScroll: true });
3608
+ }, 200);
3609
+ return () => clearTimeout(focusTimer);
3610
+ }, [isPopupOpen]);
3611
+ useEffect16(() => {
3612
+ if (!isPopupOpen || !clickOutsideToClose) {
3613
+ return;
3614
+ }
3615
+ if (typeof document === "undefined") {
3616
+ return;
3617
+ }
3618
+ const handlePointerDown = (event) => {
3619
+ const target = event.target;
3620
+ if (!target) {
3621
+ return;
3622
+ }
3623
+ const container = containerRef.current;
3624
+ if (container?.contains(target)) {
3625
+ return;
3626
+ }
3627
+ const toggleButton = document.querySelector("[data-slot='chat-toggle-button']");
3628
+ if (toggleButton && toggleButton.contains(target)) {
3629
+ return;
3630
+ }
3631
+ setModalOpen?.(false);
3632
+ };
3633
+ document.addEventListener("pointerdown", handlePointerDown);
3634
+ return () => document.removeEventListener("pointerdown", handlePointerDown);
3635
+ }, [isPopupOpen, clickOutsideToClose, setModalOpen]);
3636
+ const headerElement = useMemo8(() => renderSlot(header, CopilotModalHeader, {}), [header]);
3637
+ const resolvedWidth = dimensionToCss(width, DEFAULT_POPUP_WIDTH);
3638
+ const resolvedHeight = dimensionToCss(height, DEFAULT_POPUP_HEIGHT);
3639
+ const popupStyle = useMemo8(
3640
+ () => ({
3641
+ "--copilot-popup-width": resolvedWidth,
3642
+ "--copilot-popup-height": resolvedHeight,
3643
+ "--copilot-popup-max-width": "calc(100vw - 3rem)",
3644
+ "--copilot-popup-max-height": "calc(100dvh - 7.5rem)",
3645
+ paddingTop: "env(safe-area-inset-top)",
3646
+ paddingBottom: "env(safe-area-inset-bottom)",
3647
+ paddingLeft: "env(safe-area-inset-left)",
3648
+ paddingRight: "env(safe-area-inset-right)"
3649
+ }),
3650
+ [resolvedHeight, resolvedWidth]
3651
+ );
3652
+ const popupAnimationClass = isPopupOpen && !isAnimatingOut ? "pointer-events-auto translate-y-0 opacity-100 md:scale-100" : "pointer-events-none translate-y-4 opacity-0 md:translate-y-5 md:scale-[0.95]";
3653
+ const popupContent = isRendered ? /* @__PURE__ */ jsx22(
3654
+ "div",
3655
+ {
3656
+ className: cn(
3657
+ "fixed inset-0 z-[1200] flex max-w-full flex-col items-stretch",
3658
+ "md:inset-auto md:bottom-24 md:right-6 md:items-end md:gap-4"
3659
+ ),
3660
+ children: /* @__PURE__ */ jsxs14(
3661
+ "div",
3662
+ {
3663
+ ref: containerRef,
3664
+ tabIndex: -1,
3665
+ role: "dialog",
3666
+ "aria-label": labels.modalHeaderTitle,
3667
+ "data-copilot-popup": true,
3668
+ className: cn(
3669
+ "relative flex h-full w-full flex-col overflow-hidden bg-background text-foreground",
3670
+ "origin-bottom focus:outline-none transform-gpu transition-transform transition-opacity duration-200 ease-out",
3671
+ "md:transition-transform md:transition-opacity",
3672
+ "rounded-none border border-border/0 shadow-none ring-0",
3673
+ "md:h-[var(--copilot-popup-height)] md:w-[var(--copilot-popup-width)]",
3674
+ "md:max-h-[var(--copilot-popup-max-height)] md:max-w-[var(--copilot-popup-max-width)]",
3675
+ "md:origin-bottom-right md:rounded-2xl md:border-border md:shadow-xl md:ring-1 md:ring-border/40",
3676
+ popupAnimationClass
3677
+ ),
3678
+ style: popupStyle,
3679
+ children: [
3680
+ headerElement,
3681
+ /* @__PURE__ */ jsx22("div", { className: "flex-1 overflow-hidden", "data-popup-chat": true, children: /* @__PURE__ */ jsx22(
3682
+ CopilotChatView_default,
3683
+ {
3684
+ ...restProps,
3685
+ className: cn("h-full min-h-0", className)
3686
+ }
3687
+ ) })
3688
+ ]
3689
+ }
3690
+ )
3691
+ }
3692
+ ) : null;
3693
+ return /* @__PURE__ */ jsxs14(Fragment8, { children: [
3694
+ /* @__PURE__ */ jsx22(CopilotChatToggleButton_default, {}),
3695
+ popupContent
3696
+ ] });
3697
+ }
3698
+ CopilotPopupView.displayName = "CopilotPopupView";
3699
+
3043
3700
  // src/components/chat/CopilotSidebar.tsx
3044
- import { useMemo as useMemo7 } from "react";
3045
- import { jsx as jsx22 } from "react/jsx-runtime";
3701
+ import { useMemo as useMemo9 } from "react";
3702
+ import { jsx as jsx23 } from "react/jsx-runtime";
3046
3703
  function CopilotSidebar({ header, defaultOpen, width, ...chatProps }) {
3047
- const SidebarViewOverride = useMemo7(() => {
3704
+ const SidebarViewOverride = useMemo9(() => {
3048
3705
  const Component = (viewProps) => {
3049
3706
  const { header: viewHeader, width: viewWidth, ...restProps } = viewProps;
3050
- return /* @__PURE__ */ jsx22(
3707
+ return /* @__PURE__ */ jsx23(
3051
3708
  CopilotSidebarView,
3052
3709
  {
3053
3710
  ...restProps,
@@ -3058,7 +3715,7 @@ function CopilotSidebar({ header, defaultOpen, width, ...chatProps }) {
3058
3715
  };
3059
3716
  return Object.assign(Component, CopilotChatView_default);
3060
3717
  }, [header, width]);
3061
- return /* @__PURE__ */ jsx22(
3718
+ return /* @__PURE__ */ jsx23(
3062
3719
  CopilotChat,
3063
3720
  {
3064
3721
  ...chatProps,
@@ -3069,6 +3726,50 @@ function CopilotSidebar({ header, defaultOpen, width, ...chatProps }) {
3069
3726
  }
3070
3727
  CopilotSidebar.displayName = "CopilotSidebar";
3071
3728
 
3729
+ // src/components/chat/CopilotPopup.tsx
3730
+ import { useMemo as useMemo10 } from "react";
3731
+ import { jsx as jsx24 } from "react/jsx-runtime";
3732
+ function CopilotPopup({
3733
+ header,
3734
+ defaultOpen,
3735
+ width,
3736
+ height,
3737
+ clickOutsideToClose,
3738
+ ...chatProps
3739
+ }) {
3740
+ const PopupViewOverride = useMemo10(() => {
3741
+ const Component = (viewProps) => {
3742
+ const {
3743
+ header: viewHeader,
3744
+ width: viewWidth,
3745
+ height: viewHeight,
3746
+ clickOutsideToClose: viewClickOutsideToClose,
3747
+ ...restProps
3748
+ } = viewProps;
3749
+ return /* @__PURE__ */ jsx24(
3750
+ CopilotPopupView,
3751
+ {
3752
+ ...restProps,
3753
+ header: header ?? viewHeader,
3754
+ width: width ?? viewWidth,
3755
+ height: height ?? viewHeight,
3756
+ clickOutsideToClose: clickOutsideToClose ?? viewClickOutsideToClose
3757
+ }
3758
+ );
3759
+ };
3760
+ return Object.assign(Component, CopilotChatView_default);
3761
+ }, [clickOutsideToClose, header, height, width]);
3762
+ return /* @__PURE__ */ jsx24(
3763
+ CopilotChat,
3764
+ {
3765
+ ...chatProps,
3766
+ chatView: PopupViewOverride,
3767
+ isModalDefaultOpen: defaultOpen
3768
+ }
3769
+ );
3770
+ }
3771
+ CopilotPopup.displayName = "CopilotPopup";
3772
+
3072
3773
  // src/types/defineToolCallRenderer.ts
3073
3774
  import { z as z2 } from "zod";
3074
3775
  function defineToolCallRenderer(def) {
@@ -3082,25 +3783,25 @@ function defineToolCallRenderer(def) {
3082
3783
  }
3083
3784
 
3084
3785
  // src/components/WildcardToolCallRender.tsx
3085
- import { useState as useState12 } from "react";
3086
- import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
3786
+ import { useState as useState14 } from "react";
3787
+ import { jsx as jsx25, jsxs as jsxs15 } from "react/jsx-runtime";
3087
3788
  var WildcardToolCallRender = defineToolCallRenderer({
3088
3789
  name: "*",
3089
3790
  render: ({ args, result, name, status }) => {
3090
- const [isExpanded, setIsExpanded] = useState12(false);
3791
+ const [isExpanded, setIsExpanded] = useState14(false);
3091
3792
  const statusString = String(status);
3092
3793
  const isActive = statusString === "inProgress" || statusString === "executing";
3093
3794
  const isComplete = statusString === "complete";
3094
3795
  const statusStyles = isActive ? "bg-amber-100 text-amber-800 dark:bg-amber-500/15 dark:text-amber-400" : isComplete ? "bg-emerald-100 text-emerald-800 dark:bg-emerald-500/15 dark:text-emerald-400" : "bg-zinc-100 text-zinc-800 dark:bg-zinc-700/40 dark:text-zinc-300";
3095
- return /* @__PURE__ */ jsx23("div", { className: "mt-2 pb-2", children: /* @__PURE__ */ jsxs14("div", { className: "rounded-xl border border-zinc-200/60 dark:border-zinc-800/60 bg-white/70 dark:bg-zinc-900/50 shadow-sm backdrop-blur p-4", children: [
3096
- /* @__PURE__ */ jsxs14(
3796
+ return /* @__PURE__ */ jsx25("div", { className: "mt-2 pb-2", children: /* @__PURE__ */ jsxs15("div", { className: "rounded-xl border border-zinc-200/60 dark:border-zinc-800/60 bg-white/70 dark:bg-zinc-900/50 shadow-sm backdrop-blur p-4", children: [
3797
+ /* @__PURE__ */ jsxs15(
3097
3798
  "div",
3098
3799
  {
3099
3800
  className: "flex items-center justify-between gap-3 cursor-pointer",
3100
3801
  onClick: () => setIsExpanded(!isExpanded),
3101
3802
  children: [
3102
- /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 min-w-0", children: [
3103
- /* @__PURE__ */ jsx23(
3803
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 min-w-0", children: [
3804
+ /* @__PURE__ */ jsx25(
3104
3805
  "svg",
3105
3806
  {
3106
3807
  className: `h-4 w-4 text-zinc-500 dark:text-zinc-400 transition-transform ${isExpanded ? "rotate-90" : ""}`,
@@ -3108,7 +3809,7 @@ var WildcardToolCallRender = defineToolCallRenderer({
3108
3809
  viewBox: "0 0 24 24",
3109
3810
  strokeWidth: 2,
3110
3811
  stroke: "currentColor",
3111
- children: /* @__PURE__ */ jsx23(
3812
+ children: /* @__PURE__ */ jsx25(
3112
3813
  "path",
3113
3814
  {
3114
3815
  strokeLinecap: "round",
@@ -3118,10 +3819,10 @@ var WildcardToolCallRender = defineToolCallRenderer({
3118
3819
  )
3119
3820
  }
3120
3821
  ),
3121
- /* @__PURE__ */ jsx23("span", { className: "inline-block h-2 w-2 rounded-full bg-blue-500" }),
3122
- /* @__PURE__ */ jsx23("span", { className: "truncate text-sm font-medium text-zinc-900 dark:text-zinc-100", children: name })
3822
+ /* @__PURE__ */ jsx25("span", { className: "inline-block h-2 w-2 rounded-full bg-blue-500" }),
3823
+ /* @__PURE__ */ jsx25("span", { className: "truncate text-sm font-medium text-zinc-900 dark:text-zinc-100", children: name })
3123
3824
  ] }),
3124
- /* @__PURE__ */ jsx23(
3825
+ /* @__PURE__ */ jsx25(
3125
3826
  "span",
3126
3827
  {
3127
3828
  className: `inline-flex items-center rounded-full px-2 py-1 text-xs font-medium ${statusStyles}`,
@@ -3131,14 +3832,14 @@ var WildcardToolCallRender = defineToolCallRenderer({
3131
3832
  ]
3132
3833
  }
3133
3834
  ),
3134
- isExpanded && /* @__PURE__ */ jsxs14("div", { className: "mt-3 grid gap-4", children: [
3135
- /* @__PURE__ */ jsxs14("div", { children: [
3136
- /* @__PURE__ */ jsx23("div", { className: "text-xs uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "Arguments" }),
3137
- /* @__PURE__ */ jsx23("pre", { className: "mt-2 max-h-64 overflow-auto rounded-md bg-zinc-50 dark:bg-zinc-800/60 p-3 text-xs leading-relaxed text-zinc-800 dark:text-zinc-200 whitespace-pre-wrap break-words", children: JSON.stringify(args ?? {}, null, 2) })
3835
+ isExpanded && /* @__PURE__ */ jsxs15("div", { className: "mt-3 grid gap-4", children: [
3836
+ /* @__PURE__ */ jsxs15("div", { children: [
3837
+ /* @__PURE__ */ jsx25("div", { className: "text-xs uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "Arguments" }),
3838
+ /* @__PURE__ */ jsx25("pre", { className: "mt-2 max-h-64 overflow-auto rounded-md bg-zinc-50 dark:bg-zinc-800/60 p-3 text-xs leading-relaxed text-zinc-800 dark:text-zinc-200 whitespace-pre-wrap break-words", children: JSON.stringify(args ?? {}, null, 2) })
3138
3839
  ] }),
3139
- result !== void 0 && /* @__PURE__ */ jsxs14("div", { children: [
3140
- /* @__PURE__ */ jsx23("div", { className: "text-xs uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "Result" }),
3141
- /* @__PURE__ */ jsx23("pre", { className: "mt-2 max-h-64 overflow-auto rounded-md bg-zinc-50 dark:bg-zinc-800/60 p-3 text-xs leading-relaxed text-zinc-800 dark:text-zinc-200 whitespace-pre-wrap break-words", children: typeof result === "string" ? result : JSON.stringify(result, null, 2) })
3840
+ result !== void 0 && /* @__PURE__ */ jsxs15("div", { children: [
3841
+ /* @__PURE__ */ jsx25("div", { className: "text-xs uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "Result" }),
3842
+ /* @__PURE__ */ jsx25("pre", { className: "mt-2 max-h-64 overflow-auto rounded-md bg-zinc-50 dark:bg-zinc-800/60 p-3 text-xs leading-relaxed text-zinc-800 dark:text-zinc-200 whitespace-pre-wrap break-words", children: typeof result === "string" ? result : JSON.stringify(result, null, 2) })
3142
3843
  ] })
3143
3844
  ] })
3144
3845
  ] }) });
@@ -3164,6 +3865,8 @@ export {
3164
3865
  CopilotKitInspector,
3165
3866
  CopilotKitProvider,
3166
3867
  CopilotModalHeader,
3868
+ CopilotPopup,
3869
+ CopilotPopupView,
3167
3870
  CopilotSidebar,
3168
3871
  CopilotSidebarView,
3169
3872
  WildcardToolCallRender,