22import React , { useEffect , useRef , useState } from 'react' ;
33import { motion , AnimatePresence } from 'framer-motion' ;
44import TextareaAutosize from 'react-textarea-autosize' ;
5- import { PaperclipIcon , Send , X , Code } from 'lucide-react' ;
5+ import { PaperclipIcon , Send , X , Code , Wand2 } from 'lucide-react' ;
66import { cn } from '@/lib/utils' ;
77import { Message } from '../../const/MessageType' ;
88import Image from 'next/image' ;
@@ -12,7 +12,8 @@ import {
1212 TooltipProvider ,
1313 TooltipTrigger ,
1414} from '@/components/ui/tooltip' ;
15- import { ComponentInspector } from './code-engine/componentInspector' ;
15+ import { ComponentInspector } from './code-engine/component-inspector' ;
16+ import { Badge } from '@/components/ui/badge' ;
1617
1718interface ChatBottombarProps {
1819 messages : Message [ ] ;
@@ -28,6 +29,20 @@ interface ChatBottombarProps {
2829 setIsInspectMode ?: React . Dispatch < React . SetStateAction < boolean > > ;
2930}
3031
32+ // Add subtle pulse animation for Component Mode badge
33+ const pulseBadge = {
34+ initial : { scale : 1 } ,
35+ animate : {
36+ scale : [ 1 , 1.03 , 1 ] ,
37+ transition : {
38+ repeat : Infinity ,
39+ repeatType : "mirror" as const ,
40+ duration : 2 ,
41+ ease : "easeInOut"
42+ }
43+ }
44+ } ;
45+
3146export default function ChatBottombar ( {
3247 messages,
3348 input,
@@ -43,6 +58,7 @@ export default function ChatBottombar({
4358 const [ isMobile , setIsMobile ] = useState ( false ) ;
4459 const [ isFocused , setIsFocused ] = useState ( false ) ;
4560 const [ attachments , setAttachments ] = useState < File [ ] > ( [ ] ) ;
61+ const [ isComponentMode , setIsComponentMode ] = useState ( false ) ;
4662 const inputRef = useRef < HTMLTextAreaElement > ( null ) ;
4763 const fileInputRef = useRef < HTMLInputElement > ( null ) ;
4864
@@ -89,6 +105,34 @@ export default function ChatBottombar({
89105 setAttachments ( [ ] ) ;
90106 } ;
91107
108+ const populateChatInput = ( content : string ) => {
109+ if ( setInput ) {
110+ setInput ( content ) ;
111+ setIsComponentMode ( true ) ;
112+ // Focus the input after populating
113+ setTimeout ( ( ) => {
114+ inputRef . current ?. focus ( ) ;
115+ } , 100 ) ;
116+ }
117+ } ;
118+
119+ // Check if input still contains component text
120+ useEffect ( ( ) => {
121+ if ( input . includes ( 'Help me modify this component:' ) ) {
122+ setIsComponentMode ( true ) ;
123+ } else {
124+ setIsComponentMode ( false ) ;
125+ }
126+ } , [ input ] ) ;
127+
128+ // Function to exit component mode
129+ const exitComponentMode = ( ) => {
130+ if ( setInput ) {
131+ setInput ( '' ) ;
132+ setIsComponentMode ( false ) ;
133+ }
134+ } ;
135+
92136 useEffect ( ( ) => {
93137 if ( inputRef . current ) {
94138 inputRef . current . focus ( ) ;
@@ -179,12 +223,50 @@ export default function ChatBottombar({
179223 </ div >
180224 </ div >
181225 < div className = "flex-1 overflow-auto" >
182- < ComponentInspector />
226+ < ComponentInspector
227+ setIsInspectMode = { setIsInspectMode }
228+ populateChatInput = { populateChatInput }
229+ />
183230 </ div >
184231 </ motion . div >
185232 ) }
186233 </ AnimatePresence >
187234
235+ { /* Component Mode Badge - outside and above the input box */ }
236+ < AnimatePresence >
237+ { isComponentMode && (
238+ < motion . div
239+ initial = { { y : - 5 , opacity : 0 } }
240+ animate = { { y : 0 , opacity : 1 } }
241+ exit = { { y : - 5 , opacity : 0 } }
242+ transition = { { duration : 0.2 } }
243+ className = "flex items-center justify-end gap-1.5 mb-1.5 pr-0.5"
244+ >
245+ < motion . div
246+ variants = { pulseBadge }
247+ initial = "initial"
248+ animate = "animate"
249+ >
250+ < Badge
251+ variant = "outline"
252+ className = "flex items-center gap-1 text-[10px] py-0.5 px-2 bg-purple-50 text-purple-600 border-purple-200 dark:bg-purple-950/60 dark:text-purple-300 dark:border-purple-800/50 shadow-sm"
253+ >
254+ < Wand2 className = "w-2.5 h-2.5" />
255+ < span > Component Mode</ span >
256+ </ Badge >
257+ </ motion . div >
258+ < button
259+ type = "button"
260+ onClick = { exitComponentMode }
261+ className = "h-4 w-4 rounded-full bg-purple-100 flex items-center justify-center text-purple-600 hover:bg-purple-200 dark:bg-purple-900/40 dark:text-purple-300 dark:hover:bg-purple-800/60"
262+ aria-label = "Exit component mode"
263+ >
264+ < X className = "h-2 w-2" />
265+ </ button >
266+ </ motion . div >
267+ ) }
268+ </ AnimatePresence >
269+
188270 < motion . div
189271 initial = { { y : 10 , opacity : 0 } }
190272 animate = { { y : 0 , opacity : 1 } }
@@ -194,9 +276,16 @@ export default function ChatBottombar({
194276 isFocused
195277 ? 'ring-1 ring-blue-500 border-blue-500'
196278 : 'border-gray-200 hover:border-gray-300 dark:border-zinc-700 dark:hover:border-zinc-600' ,
197- 'bg-white dark:bg-[#1e1e1e]'
279+ isComponentMode
280+ ? 'bg-purple-50/20 dark:bg-purple-900/5'
281+ : 'bg-white dark:bg-[#1e1e1e]'
198282 ) }
199283 >
284+ { /* Component mode indicator */ }
285+ { isComponentMode && (
286+ < div className = "absolute top-0 left-0 w-full h-1 bg-gradient-to-r" />
287+ ) }
288+
200289 { /* Attachments preview */ }
201290 < AnimatePresence >
202291 { attachments . length > 0 && (
@@ -295,10 +384,10 @@ export default function ChatBottombar({
295384 'h-7 w-7 rounded-md flex items-center justify-center' ,
296385 isInspectMode
297386 ? 'bg-blue-100 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400'
298- : 'bg-gray-100 hover:bg-gray-200 text-gray-700 dark:bg-zinc-700 dark:hover:bg-zinc-600 dark:text-zinc-200'
387+ : 'bg-gray-100 hover:bg-gray-200 text-gray-700 dark:bg-zinc-700 dark:hover:bg-zinc-600 dark:text-zinc-200'
299388 ) }
300- aria-label = "Toggle UI Edit Mode"
301- >
389+ aria-label = "Toggle UI Edit Mode"
390+ >
302391 < Code className = "h-3.5 w-3.5" />
303392 </ button >
304393 </ TooltipTrigger >
@@ -311,6 +400,17 @@ export default function ChatBottombar({
311400
312401 { /* Text input */ }
313402 < div className = "relative flex-1 flex items-center" >
403+ < AnimatePresence >
404+ { isComponentMode && (
405+ < motion . div
406+ initial = { { opacity : 0 , width : 0 } }
407+ animate = { { opacity : 1 , width : "2px" } }
408+ exit = { { opacity : 0 , width : 0 } }
409+ transition = { { duration : 0.3 , delay : 0.1 } }
410+ className = "absolute left-0 top-1/2 transform -translate-y-1/2 h-[60%] bg-gradient-to-b rounded-r-full"
411+ />
412+ ) }
413+ </ AnimatePresence >
314414 < TextareaAutosize
315415 autoComplete = "off"
316416 value = { input }
@@ -320,8 +420,11 @@ export default function ChatBottombar({
320420 onFocus = { ( ) => setIsFocused ( true ) }
321421 onBlur = { ( ) => setIsFocused ( false ) }
322422 name = "message"
323- placeholder = "Message Agent..."
324- className = "resize-none px-2 py-2.5 w-full focus:outline-none bg-transparent text-gray-800 dark:text-zinc-200 text-sm placeholder:text-gray-400 dark:placeholder:text-zinc-400"
423+ placeholder = { isComponentMode ? "Describe what you want to change..." : "Message Agent..." }
424+ className = { cn (
425+ "resize-none px-2 w-full focus:outline-none bg-transparent text-gray-800 dark:text-zinc-200 text-sm placeholder:text-gray-400 dark:placeholder:text-zinc-400" ,
426+ isComponentMode ? "pt-4 pb-2.5" : "py-2.5"
427+ ) }
325428 maxRows = { 5 }
326429 />
327430 </ div >
0 commit comments