@@ -10,7 +10,9 @@ import { useEditor, EditorContent } from "@tiptap/react";
1010import StarterKit from "@tiptap/starter-kit" ;
1111import { useEffect , useState } from "react" ;
1212import { CopilotKit , useCoAgent , useCopilotAction , useCopilotChat } from "@copilotkit/react-core" ;
13- import { CopilotSidebar } from "@copilotkit/react-ui" ;
13+ import { CopilotChat , CopilotSidebar } from "@copilotkit/react-ui" ;
14+ import { useMobileView } from "@/utils/use-mobile-view" ;
15+ import { useMobileChat } from "@/utils/use-mobile-chat" ;
1416
1517const extensions = [ StarterKit ] ;
1618
@@ -22,6 +24,19 @@ interface PredictiveStateUpdatesProps {
2224
2325export default function PredictiveStateUpdates ( { params } : PredictiveStateUpdatesProps ) {
2426 const { integrationId } = React . use ( params ) ;
27+ const { isMobile } = useMobileView ( ) ;
28+ const defaultChatHeight = 50
29+ const {
30+ isChatOpen,
31+ setChatHeight,
32+ setIsChatOpen,
33+ isDragging,
34+ chatHeight,
35+ handleDragStart
36+ } = useMobileChat ( defaultChatHeight )
37+ const chatTitle = 'AI Document Editor'
38+ const chatDescription = 'Ask me to create or edit a document'
39+ const initialLabel = 'Hi 👋 How can I help with your document?'
2540
2641 return (
2742 < CopilotKit
@@ -39,16 +54,99 @@ export default function PredictiveStateUpdates({ params }: PredictiveStateUpdate
3954 } as React . CSSProperties
4055 }
4156 >
42- < CopilotSidebar
43- defaultOpen = { true }
44- labels = { {
45- title : "AI Document Editor" ,
46- initial : "Hi 👋 How can I help with your document?" ,
47- } }
48- clickOutsideToClose = { false }
49- >
50- < DocumentEditor />
51- </ CopilotSidebar >
57+ { isMobile ? (
58+ < >
59+ { /* Chat Toggle Button */ }
60+ < div className = "fixed bottom-0 left-0 right-0 z-50" >
61+ < div className = "bg-gradient-to-t from-white via-white to-transparent h-6" > </ div >
62+ < div
63+ className = "bg-white border-t border-gray-200 px-4 py-3 flex items-center justify-between cursor-pointer shadow-lg"
64+ onClick = { ( ) => {
65+ if ( ! isChatOpen ) {
66+ setChatHeight ( defaultChatHeight ) ; // Reset to good default when opening
67+ }
68+ setIsChatOpen ( ! isChatOpen ) ;
69+ } }
70+ >
71+ < div className = "flex items-center gap-3" >
72+ < div >
73+ < div className = "font-medium text-gray-900" > { chatTitle } </ div >
74+ < div className = "text-sm text-gray-500" > { chatDescription } </ div >
75+ </ div >
76+ </ div >
77+ < div className = { `transform transition-transform duration-300 ${ isChatOpen ? 'rotate-180' : '' } ` } >
78+ < svg className = "w-6 h-6 text-gray-400" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
79+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M5 15l7-7 7 7" />
80+ </ svg >
81+ </ div >
82+ </ div >
83+ </ div >
84+
85+ { /* Pull-Up Chat Container */ }
86+ < div
87+ className = { `fixed inset-x-0 bottom-0 z-40 bg-white rounded-t-2xl shadow-[0px_0px_20px_0px_rgba(0,0,0,0.15)] transform transition-all duration-300 ease-in-out flex flex-col ${
88+ isChatOpen ? 'translate-y-0' : 'translate-y-full'
89+ } ${ isDragging ? 'transition-none' : '' } `}
90+ style = { {
91+ height : `${ chatHeight } vh` ,
92+ paddingBottom : 'env(safe-area-inset-bottom)' // Handle iPhone bottom padding
93+ } }
94+ >
95+ { /* Drag Handle Bar */ }
96+ < div
97+ className = "flex justify-center pt-3 pb-2 flex-shrink-0 cursor-grab active:cursor-grabbing"
98+ onMouseDown = { handleDragStart }
99+ >
100+ < div className = "w-12 h-1 bg-gray-400 rounded-full hover:bg-gray-500 transition-colors" > </ div >
101+ </ div >
102+
103+ { /* Chat Header */ }
104+ < div className = "px-4 py-3 border-b border-gray-100 flex-shrink-0" >
105+ < div className = "flex items-center justify-between" >
106+ < div className = "flex items-center gap-3" >
107+ < h3 className = "font-semibold text-gray-900" > { chatTitle } </ h3 >
108+ </ div >
109+ < button
110+ onClick = { ( ) => setIsChatOpen ( false ) }
111+ className = "p-2 hover:bg-gray-100 rounded-full transition-colors"
112+ >
113+ < svg className = "w-5 h-5 text-gray-500" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
114+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M6 18L18 6M6 6l12 12" />
115+ </ svg >
116+ </ button >
117+ </ div >
118+ </ div >
119+
120+ { /* Chat Content - Flexible container for messages and input */ }
121+ < div className = "flex-1 flex flex-col min-h-0 overflow-hidden pb-16" >
122+ < CopilotChat
123+ className = "h-full flex flex-col"
124+ labels = { {
125+ initial : initialLabel ,
126+ } }
127+ />
128+ </ div >
129+ </ div >
130+
131+ { /* Backdrop */ }
132+ { isChatOpen && (
133+ < div
134+ className = "fixed inset-0 z-30"
135+ onClick = { ( ) => setIsChatOpen ( false ) }
136+ />
137+ ) }
138+ </ >
139+ ) : (
140+ < CopilotSidebar
141+ defaultOpen = { true }
142+ labels = { {
143+ title : chatTitle ,
144+ initial : initialLabel ,
145+ } }
146+ clickOutsideToClose = { false }
147+ />
148+ ) }
149+ < DocumentEditor />
52150 </ div >
53151 </ CopilotKit >
54152 ) ;
0 commit comments