@copilotkitnext/react 0.0.14 → 0.0.15

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
  }
@@ -2203,7 +2630,7 @@ var CopilotChatUserMessage_default = CopilotChatUserMessage;
2203
2630
  import React9 from "react";
2204
2631
  import { Loader2 } from "lucide-react";
2205
2632
  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";
2633
+ 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
2634
  var labelClasses = "whitespace-nowrap font-medium leading-none";
2208
2635
  var CopilotChatSuggestionPill = React9.forwardRef(function CopilotChatSuggestionPill2({ className, children, icon, isLoading, type, ...props }, ref) {
2209
2636
  const showIcon = !isLoading && icon;
@@ -2218,7 +2645,7 @@ var CopilotChatSuggestionPill = React9.forwardRef(function CopilotChatSuggestion
2218
2645
  disabled: isLoading || props.disabled,
2219
2646
  ...props,
2220
2647
  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 }),
2648
+ 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
2649
  /* @__PURE__ */ jsx14("span", { className: labelClasses, children })
2223
2650
  ]
2224
2651
  }
@@ -2236,7 +2663,7 @@ var DefaultContainer = React10.forwardRef(function DefaultContainer2({ className
2236
2663
  {
2237
2664
  ref,
2238
2665
  className: cn(
2239
- "flex flex-wrap items-center gap-2 px-4 sm:px-0 pointer-events-none",
2666
+ "flex flex-wrap items-center gap-1.5 sm:gap-2 pl-0 pr-4 sm:px-0 pointer-events-none",
2240
2667
  className
2241
2668
  ),
2242
2669
  ...props
@@ -2379,10 +2806,52 @@ CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
2379
2806
  var CopilotChatMessageView_default = CopilotChatMessageView;
2380
2807
 
2381
2808
  // src/components/chat/CopilotChatView.tsx
2382
- import React11, { useRef as useRef7, useState as useState9, useEffect as useEffect12 } from "react";
2809
+ import React11, { useRef as useRef7, useState as useState10, useEffect as useEffect13 } from "react";
2383
2810
  import { twMerge as twMerge7 } from "tailwind-merge";
2384
2811
  import { StickToBottom, useStickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
2385
2812
  import { ChevronDown } from "lucide-react";
2813
+
2814
+ // src/hooks/use-keyboard-height.tsx
2815
+ import { useState as useState9, useEffect as useEffect12 } from "react";
2816
+ function useKeyboardHeight() {
2817
+ const [keyboardState, setKeyboardState] = useState9({
2818
+ isKeyboardOpen: false,
2819
+ keyboardHeight: 0,
2820
+ availableHeight: typeof window !== "undefined" ? window.innerHeight : 0,
2821
+ viewportHeight: typeof window !== "undefined" ? window.innerHeight : 0
2822
+ });
2823
+ useEffect12(() => {
2824
+ if (typeof window === "undefined") {
2825
+ return;
2826
+ }
2827
+ const visualViewport = window.visualViewport;
2828
+ if (!visualViewport) {
2829
+ return;
2830
+ }
2831
+ const updateKeyboardState = () => {
2832
+ const layoutHeight = window.innerHeight;
2833
+ const visualHeight = visualViewport.height;
2834
+ const keyboardHeight = Math.max(0, layoutHeight - visualHeight);
2835
+ const isKeyboardOpen = keyboardHeight > 150;
2836
+ setKeyboardState({
2837
+ isKeyboardOpen,
2838
+ keyboardHeight,
2839
+ availableHeight: visualHeight,
2840
+ viewportHeight: layoutHeight
2841
+ });
2842
+ };
2843
+ updateKeyboardState();
2844
+ visualViewport.addEventListener("resize", updateKeyboardState);
2845
+ visualViewport.addEventListener("scroll", updateKeyboardState);
2846
+ return () => {
2847
+ visualViewport.removeEventListener("resize", updateKeyboardState);
2848
+ visualViewport.removeEventListener("scroll", updateKeyboardState);
2849
+ };
2850
+ }, []);
2851
+ return keyboardState;
2852
+ }
2853
+
2854
+ // src/components/chat/CopilotChatView.tsx
2386
2855
  import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
2387
2856
  function CopilotChatView({
2388
2857
  messageView,
@@ -2405,10 +2874,11 @@ function CopilotChatView({
2405
2874
  ...props
2406
2875
  }) {
2407
2876
  const inputContainerRef = useRef7(null);
2408
- const [inputContainerHeight, setInputContainerHeight] = useState9(0);
2409
- const [isResizing, setIsResizing] = useState9(false);
2877
+ const [inputContainerHeight, setInputContainerHeight] = useState10(0);
2878
+ const [isResizing, setIsResizing] = useState10(false);
2410
2879
  const resizeTimeoutRef = useRef7(null);
2411
- useEffect12(() => {
2880
+ const { isKeyboardOpen, keyboardHeight, availableHeight } = useKeyboardHeight();
2881
+ useEffect13(() => {
2412
2882
  const element = inputContainerRef.current;
2413
2883
  if (!element) return;
2414
2884
  const resizeObserver = new ResizeObserver((entries) => {
@@ -2462,15 +2932,16 @@ function CopilotChatView({
2462
2932
  isResizing,
2463
2933
  children: /* @__PURE__ */ jsx17("div", { style: { paddingBottom: `${inputContainerHeight + (hasSuggestions ? 4 : 32)}px` }, children: /* @__PURE__ */ jsxs10("div", { className: "max-w-3xl mx-auto", children: [
2464
2934
  BoundMessageView,
2465
- hasSuggestions ? /* @__PURE__ */ jsx17("div", { className: "px-4 sm:px-0 mt-4", children: BoundSuggestionView }) : null
2935
+ hasSuggestions ? /* @__PURE__ */ jsx17("div", { className: "pl-0 pr-4 sm:px-0 mt-4", children: BoundSuggestionView }) : null
2466
2936
  ] }) })
2467
2937
  });
2468
2938
  const BoundScrollToBottomButton = renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, {});
2469
2939
  const BoundDisclaimer = renderSlot(disclaimer, CopilotChatView.Disclaimer, {});
2470
2940
  const BoundInputContainer = renderSlot(inputContainer, CopilotChatView.InputContainer, {
2471
2941
  ref: inputContainerRef,
2942
+ keyboardHeight: isKeyboardOpen ? keyboardHeight : 0,
2472
2943
  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 }),
2944
+ /* @__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
2945
  BoundDisclaimer
2475
2946
  ] })
2476
2947
  });
@@ -2496,7 +2967,7 @@ function CopilotChatView({
2496
2967
  const ScrollContent = ({ children, scrollToBottomButton, inputContainerHeight, isResizing }) => {
2497
2968
  const { isAtBottom, scrollToBottom } = useStickToBottomContext();
2498
2969
  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 }) }),
2970
+ /* @__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
2971
  !isAtBottom && !isResizing && /* @__PURE__ */ jsx17(
2501
2972
  "div",
2502
2973
  {
@@ -2520,13 +2991,13 @@ function CopilotChatView({
2520
2991
  className,
2521
2992
  ...props
2522
2993
  }) => {
2523
- const [hasMounted, setHasMounted] = useState9(false);
2994
+ const [hasMounted, setHasMounted] = useState10(false);
2524
2995
  const { scrollRef, contentRef, scrollToBottom } = useStickToBottom();
2525
- const [showScrollButton, setShowScrollButton] = useState9(false);
2526
- useEffect12(() => {
2996
+ const [showScrollButton, setShowScrollButton] = useState10(false);
2997
+ useEffect13(() => {
2527
2998
  setHasMounted(true);
2528
2999
  }, []);
2529
- useEffect12(() => {
3000
+ useEffect13(() => {
2530
3001
  if (autoScroll) return;
2531
3002
  const scrollElement = scrollRef.current;
2532
3003
  if (!scrollElement) return;
@@ -2544,7 +3015,7 @@ function CopilotChatView({
2544
3015
  };
2545
3016
  }, [scrollRef, autoScroll]);
2546
3017
  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 }) });
3018
+ 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
3019
  }
2549
3020
  if (!autoScroll) {
2550
3021
  return /* @__PURE__ */ jsxs10(
@@ -2557,7 +3028,7 @@ function CopilotChatView({
2557
3028
  ),
2558
3029
  ...props,
2559
3030
  children: [
2560
- /* @__PURE__ */ jsx17("div", { ref: contentRef, className: "px-4 sm:px-0 [div[data-sidebar-chat]_&]:px-8", children }),
3031
+ /* @__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
3032
  showScrollButton && !isResizing && /* @__PURE__ */ jsx17(
2562
3033
  "div",
2563
3034
  {
@@ -2626,7 +3097,20 @@ function CopilotChatView({
2626
3097
  ...props
2627
3098
  }
2628
3099
  );
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 }));
3100
+ CopilotChatView2.InputContainer = React11.forwardRef(({ children, className, keyboardHeight = 0, ...props }, ref) => /* @__PURE__ */ jsx17(
3101
+ "div",
3102
+ {
3103
+ ref,
3104
+ className: cn("absolute bottom-0 left-0 right-0 z-20 pointer-events-none", className),
3105
+ style: {
3106
+ // Adjust position when keyboard is open to keep input visible
3107
+ transform: keyboardHeight > 0 ? `translateY(-${keyboardHeight}px)` : void 0,
3108
+ transition: "transform 0.2s ease-out"
3109
+ },
3110
+ ...props,
3111
+ children
3112
+ }
3113
+ ));
2630
3114
  CopilotChatView2.InputContainer.displayName = "CopilotChatView.InputContainer";
2631
3115
  CopilotChatView2.Disclaimer = ({ className, ...props }) => {
2632
3116
  const config = useCopilotChatConfiguration();
@@ -2645,14 +3129,14 @@ var CopilotChatView_default = CopilotChatView;
2645
3129
 
2646
3130
  // src/components/chat/CopilotChat.tsx
2647
3131
  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";
3132
+ import { useCallback as useCallback6, useEffect as useEffect14, useMemo as useMemo7 } from "react";
2649
3133
  import { merge } from "ts-deepmerge";
2650
3134
  import { AGUIConnectNotImplementedError } from "@ag-ui/client";
2651
3135
  import { jsx as jsx18 } from "react/jsx-runtime";
2652
3136
  function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, ...props }) {
2653
3137
  const existingConfig = useCopilotChatConfiguration();
2654
3138
  const resolvedAgentId = agentId ?? existingConfig?.agentId ?? DEFAULT_AGENT_ID6;
2655
- const resolvedThreadId = useMemo6(
3139
+ const resolvedThreadId = useMemo7(
2656
3140
  () => threadId ?? existingConfig?.threadId ?? randomUUID2(),
2657
3141
  [threadId, existingConfig?.threadId]
2658
3142
  );
@@ -2665,7 +3149,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
2665
3149
  suggestionView: providedSuggestionView,
2666
3150
  ...restProps
2667
3151
  } = props;
2668
- useEffect13(() => {
3152
+ useEffect14(() => {
2669
3153
  const connect = async (agent2) => {
2670
3154
  try {
2671
3155
  await copilotkit.connectAgent({ agent: agent2 });
@@ -2683,7 +3167,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
2683
3167
  return () => {
2684
3168
  };
2685
3169
  }, [resolvedThreadId, agent, copilotkit, resolvedAgentId]);
2686
- const onSubmitInput = useCallback5(
3170
+ const onSubmitInput = useCallback6(
2687
3171
  async (value) => {
2688
3172
  agent?.addMessage({
2689
3173
  id: randomUUID2(),
@@ -2700,7 +3184,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
2700
3184
  },
2701
3185
  [agent, copilotkit]
2702
3186
  );
2703
- const handleSelectSuggestion = useCallback5(
3187
+ const handleSelectSuggestion = useCallback6(
2704
3188
  async (suggestion) => {
2705
3189
  if (!agent) {
2706
3190
  return;
@@ -2754,7 +3238,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
2754
3238
  })(CopilotChat || (CopilotChat = {}));
2755
3239
 
2756
3240
  // src/components/chat/CopilotChatToggleButton.tsx
2757
- import React12, { useState as useState10 } from "react";
3241
+ import React12, { useState as useState11 } from "react";
2758
3242
  import { MessageCircle, X as X2 } from "lucide-react";
2759
3243
  import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
2760
3244
  var DefaultOpenIcon = ({
@@ -2785,7 +3269,7 @@ var CopilotChatToggleButton = React12.forwardRef(function CopilotChatToggleButto
2785
3269
  const { onClick, type, disabled, ...restProps } = buttonProps;
2786
3270
  const configuration = useCopilotChatConfiguration();
2787
3271
  const labels = configuration?.labels ?? CopilotChatDefaultLabels;
2788
- const [fallbackOpen, setFallbackOpen] = useState10(false);
3272
+ const [fallbackOpen, setFallbackOpen] = useState11(false);
2789
3273
  const isOpen = configuration?.isModalOpen ?? fallbackOpen;
2790
3274
  const setModalOpen = configuration?.setModalOpen ?? setFallbackOpen;
2791
3275
  const handleClick = (event) => {
@@ -2871,10 +3355,10 @@ CopilotChatToggleButton.displayName = "CopilotChatToggleButton";
2871
3355
  var CopilotChatToggleButton_default = CopilotChatToggleButton;
2872
3356
 
2873
3357
  // src/components/chat/CopilotSidebarView.tsx
2874
- import { useEffect as useEffect14, useRef as useRef8, useState as useState11 } from "react";
3358
+ import { useEffect as useEffect15, useRef as useRef8, useState as useState12 } from "react";
2875
3359
 
2876
3360
  // src/components/chat/CopilotModalHeader.tsx
2877
- import { useCallback as useCallback6 } from "react";
3361
+ import { useCallback as useCallback7 } from "react";
2878
3362
  import { X as X3 } from "lucide-react";
2879
3363
  import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
2880
3364
  function CopilotModalHeader({
@@ -2888,7 +3372,7 @@ function CopilotModalHeader({
2888
3372
  const configuration = useCopilotChatConfiguration();
2889
3373
  const fallbackTitle = configuration?.labels.modalHeaderTitle ?? CopilotChatDefaultLabels.modalHeaderTitle;
2890
3374
  const resolvedTitle = title ?? fallbackTitle;
2891
- const handleClose = useCallback6(() => {
3375
+ const handleClose = useCallback7(() => {
2892
3376
  configuration?.setModalOpen(false);
2893
3377
  }, [configuration]);
2894
3378
  const BoundTitle = renderSlot(titleContent, CopilotModalHeader.Title, {
@@ -2965,7 +3449,7 @@ function CopilotSidebarView({ header, width, ...props }) {
2965
3449
  const configuration = useCopilotChatConfiguration();
2966
3450
  const isSidebarOpen = configuration?.isModalOpen ?? false;
2967
3451
  const sidebarRef = useRef8(null);
2968
- const [sidebarWidth, setSidebarWidth] = useState11(width ?? DEFAULT_SIDEBAR_WIDTH);
3452
+ const [sidebarWidth, setSidebarWidth] = useState12(width ?? DEFAULT_SIDEBAR_WIDTH);
2969
3453
  const widthToCss = (w) => {
2970
3454
  return typeof w === "number" ? `${w}px` : w;
2971
3455
  };
@@ -2975,7 +3459,7 @@ function CopilotSidebarView({ header, width, ...props }) {
2975
3459
  }
2976
3460
  return w;
2977
3461
  };
2978
- useEffect14(() => {
3462
+ useEffect15(() => {
2979
3463
  if (width !== void 0) {
2980
3464
  return;
2981
3465
  }
@@ -3007,10 +3491,13 @@ function CopilotSidebarView({ header, width, ...props }) {
3007
3491
  "style",
3008
3492
  {
3009
3493
  dangerouslySetInnerHTML: {
3010
- __html: `body {
3011
- margin-inline-end: ${widthToMargin(sidebarWidth)};
3012
- transition: margin-inline-end ${SIDEBAR_TRANSITION_MS}ms ease;
3013
- }`
3494
+ __html: `
3495
+ @media (min-width: 768px) {
3496
+ body {
3497
+ margin-inline-end: ${widthToMargin(sidebarWidth)};
3498
+ transition: margin-inline-end ${SIDEBAR_TRANSITION_MS}ms ease;
3499
+ }
3500
+ }`
3014
3501
  }
3015
3502
  }
3016
3503
  ),
@@ -3021,12 +3508,22 @@ function CopilotSidebarView({ header, width, ...props }) {
3021
3508
  ref: sidebarRef,
3022
3509
  "data-copilot-sidebar": true,
3023
3510
  className: cn(
3024
- "fixed right-0 top-0 z-[1200] flex h-dvh max-h-screen",
3511
+ "fixed right-0 top-0 z-[1200] flex",
3512
+ // Height with dvh fallback and safe area support
3513
+ "h-[100vh] h-[100dvh] max-h-screen",
3514
+ // Responsive width: full on mobile, custom on desktop
3515
+ "w-full",
3025
3516
  "border-l border-border bg-background text-foreground shadow-xl",
3026
3517
  "transition-transform duration-300 ease-out",
3027
3518
  isSidebarOpen ? "translate-x-0" : "translate-x-full pointer-events-none"
3028
3519
  ),
3029
- style: { width: widthToCss(sidebarWidth) },
3520
+ style: {
3521
+ // Use CSS custom property for responsive width
3522
+ ["--sidebar-width"]: widthToCss(sidebarWidth),
3523
+ // Safe area insets for iOS
3524
+ paddingTop: "env(safe-area-inset-top)",
3525
+ paddingBottom: "env(safe-area-inset-bottom)"
3526
+ },
3030
3527
  "aria-hidden": !isSidebarOpen,
3031
3528
  "aria-label": "Copilot chat sidebar",
3032
3529
  role: "complementary",
@@ -3040,14 +3537,173 @@ function CopilotSidebarView({ header, width, ...props }) {
3040
3537
  }
3041
3538
  CopilotSidebarView.displayName = "CopilotSidebarView";
3042
3539
 
3540
+ // src/components/chat/CopilotPopupView.tsx
3541
+ import { useEffect as useEffect16, useMemo as useMemo8, useRef as useRef9, useState as useState13 } from "react";
3542
+ import { Fragment as Fragment8, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
3543
+ var DEFAULT_POPUP_WIDTH = 420;
3544
+ var DEFAULT_POPUP_HEIGHT = 560;
3545
+ var dimensionToCss = (value, fallback) => {
3546
+ if (typeof value === "number" && Number.isFinite(value)) {
3547
+ return `${value}px`;
3548
+ }
3549
+ if (typeof value === "string" && value.trim().length > 0) {
3550
+ return value;
3551
+ }
3552
+ return `${fallback}px`;
3553
+ };
3554
+ function CopilotPopupView({
3555
+ header,
3556
+ width,
3557
+ height,
3558
+ clickOutsideToClose,
3559
+ className,
3560
+ ...restProps
3561
+ }) {
3562
+ const configuration = useCopilotChatConfiguration();
3563
+ const isPopupOpen = configuration?.isModalOpen ?? false;
3564
+ const setModalOpen = configuration?.setModalOpen;
3565
+ const labels = configuration?.labels ?? CopilotChatDefaultLabels;
3566
+ const containerRef = useRef9(null);
3567
+ const [isRendered, setIsRendered] = useState13(isPopupOpen);
3568
+ const [isAnimatingOut, setIsAnimatingOut] = useState13(false);
3569
+ useEffect16(() => {
3570
+ if (isPopupOpen) {
3571
+ setIsRendered(true);
3572
+ setIsAnimatingOut(false);
3573
+ return;
3574
+ }
3575
+ if (!isRendered) {
3576
+ return;
3577
+ }
3578
+ setIsAnimatingOut(true);
3579
+ const timeout = setTimeout(() => {
3580
+ setIsRendered(false);
3581
+ setIsAnimatingOut(false);
3582
+ }, 200);
3583
+ return () => clearTimeout(timeout);
3584
+ }, [isPopupOpen, isRendered]);
3585
+ useEffect16(() => {
3586
+ if (!isPopupOpen) {
3587
+ return;
3588
+ }
3589
+ if (typeof window === "undefined") {
3590
+ return;
3591
+ }
3592
+ const handleKeyDown = (event) => {
3593
+ if (event.key === "Escape") {
3594
+ event.preventDefault();
3595
+ setModalOpen?.(false);
3596
+ }
3597
+ };
3598
+ window.addEventListener("keydown", handleKeyDown);
3599
+ return () => window.removeEventListener("keydown", handleKeyDown);
3600
+ }, [isPopupOpen, setModalOpen]);
3601
+ useEffect16(() => {
3602
+ if (!isPopupOpen) {
3603
+ return;
3604
+ }
3605
+ const focusTimer = setTimeout(() => {
3606
+ containerRef.current?.focus({ preventScroll: true });
3607
+ }, 200);
3608
+ return () => clearTimeout(focusTimer);
3609
+ }, [isPopupOpen]);
3610
+ useEffect16(() => {
3611
+ if (!isPopupOpen || !clickOutsideToClose) {
3612
+ return;
3613
+ }
3614
+ if (typeof document === "undefined") {
3615
+ return;
3616
+ }
3617
+ const handlePointerDown = (event) => {
3618
+ const target = event.target;
3619
+ if (!target) {
3620
+ return;
3621
+ }
3622
+ const container = containerRef.current;
3623
+ if (container?.contains(target)) {
3624
+ return;
3625
+ }
3626
+ const toggleButton = document.querySelector("[data-slot='chat-toggle-button']");
3627
+ if (toggleButton && toggleButton.contains(target)) {
3628
+ return;
3629
+ }
3630
+ setModalOpen?.(false);
3631
+ };
3632
+ document.addEventListener("pointerdown", handlePointerDown);
3633
+ return () => document.removeEventListener("pointerdown", handlePointerDown);
3634
+ }, [isPopupOpen, clickOutsideToClose, setModalOpen]);
3635
+ const headerElement = useMemo8(() => renderSlot(header, CopilotModalHeader, {}), [header]);
3636
+ const resolvedWidth = dimensionToCss(width, DEFAULT_POPUP_WIDTH);
3637
+ const resolvedHeight = dimensionToCss(height, DEFAULT_POPUP_HEIGHT);
3638
+ const popupStyle = useMemo8(
3639
+ () => ({
3640
+ "--copilot-popup-width": resolvedWidth,
3641
+ "--copilot-popup-height": resolvedHeight,
3642
+ "--copilot-popup-max-width": "calc(100vw - 3rem)",
3643
+ "--copilot-popup-max-height": "calc(100dvh - 7.5rem)",
3644
+ paddingTop: "env(safe-area-inset-top)",
3645
+ paddingBottom: "env(safe-area-inset-bottom)",
3646
+ paddingLeft: "env(safe-area-inset-left)",
3647
+ paddingRight: "env(safe-area-inset-right)"
3648
+ }),
3649
+ [resolvedHeight, resolvedWidth]
3650
+ );
3651
+ 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]";
3652
+ const popupContent = isRendered ? /* @__PURE__ */ jsx22(
3653
+ "div",
3654
+ {
3655
+ className: cn(
3656
+ "fixed inset-0 z-[1200] flex max-w-full flex-col items-stretch",
3657
+ "md:inset-auto md:bottom-24 md:right-6 md:items-end md:gap-4"
3658
+ ),
3659
+ children: /* @__PURE__ */ jsxs14(
3660
+ "div",
3661
+ {
3662
+ ref: containerRef,
3663
+ tabIndex: -1,
3664
+ role: "dialog",
3665
+ "aria-label": labels.modalHeaderTitle,
3666
+ "data-copilot-popup": true,
3667
+ className: cn(
3668
+ "relative flex h-full w-full flex-col overflow-hidden bg-background text-foreground",
3669
+ "origin-bottom focus:outline-none transform-gpu transition-transform transition-opacity duration-200 ease-out",
3670
+ "md:transition-transform md:transition-opacity",
3671
+ "rounded-none border border-border/0 shadow-none ring-0",
3672
+ "md:h-[var(--copilot-popup-height)] md:w-[var(--copilot-popup-width)]",
3673
+ "md:max-h-[var(--copilot-popup-max-height)] md:max-w-[var(--copilot-popup-max-width)]",
3674
+ "md:origin-bottom-right md:rounded-2xl md:border-border md:shadow-xl md:ring-1 md:ring-border/40",
3675
+ popupAnimationClass
3676
+ ),
3677
+ style: popupStyle,
3678
+ children: [
3679
+ headerElement,
3680
+ /* @__PURE__ */ jsx22("div", { className: "flex-1 overflow-hidden", "data-popup-chat": true, children: /* @__PURE__ */ jsx22(
3681
+ CopilotChatView_default,
3682
+ {
3683
+ ...restProps,
3684
+ className: cn("h-full min-h-0", className)
3685
+ }
3686
+ ) })
3687
+ ]
3688
+ }
3689
+ )
3690
+ }
3691
+ ) : null;
3692
+ return /* @__PURE__ */ jsxs14(Fragment8, { children: [
3693
+ /* @__PURE__ */ jsx22(CopilotChatToggleButton_default, {}),
3694
+ popupContent
3695
+ ] });
3696
+ }
3697
+ CopilotPopupView.displayName = "CopilotPopupView";
3698
+
3043
3699
  // src/components/chat/CopilotSidebar.tsx
3044
- import { useMemo as useMemo7 } from "react";
3045
- import { jsx as jsx22 } from "react/jsx-runtime";
3700
+ import { useMemo as useMemo9 } from "react";
3701
+ import { jsx as jsx23 } from "react/jsx-runtime";
3046
3702
  function CopilotSidebar({ header, defaultOpen, width, ...chatProps }) {
3047
- const SidebarViewOverride = useMemo7(() => {
3703
+ const SidebarViewOverride = useMemo9(() => {
3048
3704
  const Component = (viewProps) => {
3049
3705
  const { header: viewHeader, width: viewWidth, ...restProps } = viewProps;
3050
- return /* @__PURE__ */ jsx22(
3706
+ return /* @__PURE__ */ jsx23(
3051
3707
  CopilotSidebarView,
3052
3708
  {
3053
3709
  ...restProps,
@@ -3058,7 +3714,7 @@ function CopilotSidebar({ header, defaultOpen, width, ...chatProps }) {
3058
3714
  };
3059
3715
  return Object.assign(Component, CopilotChatView_default);
3060
3716
  }, [header, width]);
3061
- return /* @__PURE__ */ jsx22(
3717
+ return /* @__PURE__ */ jsx23(
3062
3718
  CopilotChat,
3063
3719
  {
3064
3720
  ...chatProps,
@@ -3069,6 +3725,50 @@ function CopilotSidebar({ header, defaultOpen, width, ...chatProps }) {
3069
3725
  }
3070
3726
  CopilotSidebar.displayName = "CopilotSidebar";
3071
3727
 
3728
+ // src/components/chat/CopilotPopup.tsx
3729
+ import { useMemo as useMemo10 } from "react";
3730
+ import { jsx as jsx24 } from "react/jsx-runtime";
3731
+ function CopilotPopup({
3732
+ header,
3733
+ defaultOpen,
3734
+ width,
3735
+ height,
3736
+ clickOutsideToClose,
3737
+ ...chatProps
3738
+ }) {
3739
+ const PopupViewOverride = useMemo10(() => {
3740
+ const Component = (viewProps) => {
3741
+ const {
3742
+ header: viewHeader,
3743
+ width: viewWidth,
3744
+ height: viewHeight,
3745
+ clickOutsideToClose: viewClickOutsideToClose,
3746
+ ...restProps
3747
+ } = viewProps;
3748
+ return /* @__PURE__ */ jsx24(
3749
+ CopilotPopupView,
3750
+ {
3751
+ ...restProps,
3752
+ header: header ?? viewHeader,
3753
+ width: width ?? viewWidth,
3754
+ height: height ?? viewHeight,
3755
+ clickOutsideToClose: clickOutsideToClose ?? viewClickOutsideToClose
3756
+ }
3757
+ );
3758
+ };
3759
+ return Object.assign(Component, CopilotChatView_default);
3760
+ }, [clickOutsideToClose, header, height, width]);
3761
+ return /* @__PURE__ */ jsx24(
3762
+ CopilotChat,
3763
+ {
3764
+ ...chatProps,
3765
+ chatView: PopupViewOverride,
3766
+ isModalDefaultOpen: defaultOpen
3767
+ }
3768
+ );
3769
+ }
3770
+ CopilotPopup.displayName = "CopilotPopup";
3771
+
3072
3772
  // src/types/defineToolCallRenderer.ts
3073
3773
  import { z as z2 } from "zod";
3074
3774
  function defineToolCallRenderer(def) {
@@ -3082,25 +3782,25 @@ function defineToolCallRenderer(def) {
3082
3782
  }
3083
3783
 
3084
3784
  // src/components/WildcardToolCallRender.tsx
3085
- import { useState as useState12 } from "react";
3086
- import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
3785
+ import { useState as useState14 } from "react";
3786
+ import { jsx as jsx25, jsxs as jsxs15 } from "react/jsx-runtime";
3087
3787
  var WildcardToolCallRender = defineToolCallRenderer({
3088
3788
  name: "*",
3089
3789
  render: ({ args, result, name, status }) => {
3090
- const [isExpanded, setIsExpanded] = useState12(false);
3790
+ const [isExpanded, setIsExpanded] = useState14(false);
3091
3791
  const statusString = String(status);
3092
3792
  const isActive = statusString === "inProgress" || statusString === "executing";
3093
3793
  const isComplete = statusString === "complete";
3094
3794
  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(
3795
+ 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: [
3796
+ /* @__PURE__ */ jsxs15(
3097
3797
  "div",
3098
3798
  {
3099
3799
  className: "flex items-center justify-between gap-3 cursor-pointer",
3100
3800
  onClick: () => setIsExpanded(!isExpanded),
3101
3801
  children: [
3102
- /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 min-w-0", children: [
3103
- /* @__PURE__ */ jsx23(
3802
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 min-w-0", children: [
3803
+ /* @__PURE__ */ jsx25(
3104
3804
  "svg",
3105
3805
  {
3106
3806
  className: `h-4 w-4 text-zinc-500 dark:text-zinc-400 transition-transform ${isExpanded ? "rotate-90" : ""}`,
@@ -3108,7 +3808,7 @@ var WildcardToolCallRender = defineToolCallRenderer({
3108
3808
  viewBox: "0 0 24 24",
3109
3809
  strokeWidth: 2,
3110
3810
  stroke: "currentColor",
3111
- children: /* @__PURE__ */ jsx23(
3811
+ children: /* @__PURE__ */ jsx25(
3112
3812
  "path",
3113
3813
  {
3114
3814
  strokeLinecap: "round",
@@ -3118,10 +3818,10 @@ var WildcardToolCallRender = defineToolCallRenderer({
3118
3818
  )
3119
3819
  }
3120
3820
  ),
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 })
3821
+ /* @__PURE__ */ jsx25("span", { className: "inline-block h-2 w-2 rounded-full bg-blue-500" }),
3822
+ /* @__PURE__ */ jsx25("span", { className: "truncate text-sm font-medium text-zinc-900 dark:text-zinc-100", children: name })
3123
3823
  ] }),
3124
- /* @__PURE__ */ jsx23(
3824
+ /* @__PURE__ */ jsx25(
3125
3825
  "span",
3126
3826
  {
3127
3827
  className: `inline-flex items-center rounded-full px-2 py-1 text-xs font-medium ${statusStyles}`,
@@ -3131,14 +3831,14 @@ var WildcardToolCallRender = defineToolCallRenderer({
3131
3831
  ]
3132
3832
  }
3133
3833
  ),
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) })
3834
+ isExpanded && /* @__PURE__ */ jsxs15("div", { className: "mt-3 grid gap-4", children: [
3835
+ /* @__PURE__ */ jsxs15("div", { children: [
3836
+ /* @__PURE__ */ jsx25("div", { className: "text-xs uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "Arguments" }),
3837
+ /* @__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
3838
  ] }),
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) })
3839
+ result !== void 0 && /* @__PURE__ */ jsxs15("div", { children: [
3840
+ /* @__PURE__ */ jsx25("div", { className: "text-xs uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "Result" }),
3841
+ /* @__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
3842
  ] })
3143
3843
  ] })
3144
3844
  ] }) });
@@ -3164,6 +3864,8 @@ export {
3164
3864
  CopilotKitInspector,
3165
3865
  CopilotKitProvider,
3166
3866
  CopilotModalHeader,
3867
+ CopilotPopup,
3868
+ CopilotPopupView,
3167
3869
  CopilotSidebar,
3168
3870
  CopilotSidebarView,
3169
3871
  WildcardToolCallRender,