1
+ 'use client' ;
2
+
3
+ import { useState , useRef , useEffect } from 'react' ;
4
+ import { Send , RotateCcw } from 'lucide-react' ;
5
+ import { Button } from '@/components/ui/button' ;
6
+ import { Input } from '@/components/ui/input' ;
7
+ import { ScrollArea } from '@/components/ui/scroll-area' ;
8
+ import { Card , CardContent , CardHeader , CardTitle } from '@/components/ui/card' ;
9
+ import { benchifyFileSchema } from '@/lib/schemas' ;
10
+ import { z } from 'zod' ;
11
+
12
+ interface Message {
13
+ id : string ;
14
+ type : 'user' | 'assistant' ;
15
+ content : string ;
16
+ timestamp : Date ;
17
+ }
18
+
19
+ interface ChatInterfaceProps {
20
+ initialPrompt : string ;
21
+ onUpdateResult : ( result : {
22
+ repairedFiles ?: z . infer < typeof benchifyFileSchema > ;
23
+ originalFiles ?: z . infer < typeof benchifyFileSchema > ;
24
+ buildOutput : string ;
25
+ previewUrl : string ;
26
+ } ) => void ;
27
+ }
28
+
29
+ export function ChatInterface ( { initialPrompt, onUpdateResult } : ChatInterfaceProps ) {
30
+ const [ messages , setMessages ] = useState < Message [ ] > ( [
31
+ {
32
+ id : '1' ,
33
+ type : 'user' ,
34
+ content : initialPrompt ,
35
+ timestamp : new Date ( ) ,
36
+ } ,
37
+ {
38
+ id : '2' ,
39
+ type : 'assistant' ,
40
+ content : "I've generated your UI component! You can see the preview on the right. What would you like to modify or improve?" ,
41
+ timestamp : new Date ( ) ,
42
+ } ,
43
+ ] ) ;
44
+ const [ newMessage , setNewMessage ] = useState ( '' ) ;
45
+ const [ isLoading , setIsLoading ] = useState ( false ) ;
46
+ const messagesEndRef = useRef < HTMLDivElement > ( null ) ;
47
+
48
+ const scrollToBottom = ( ) => {
49
+ messagesEndRef . current ?. scrollIntoView ( { behavior : 'smooth' } ) ;
50
+ } ;
51
+
52
+ useEffect ( ( ) => {
53
+ scrollToBottom ( ) ;
54
+ } , [ messages ] ) ;
55
+
56
+ const handleSendMessage = async ( ) => {
57
+ if ( ! newMessage . trim ( ) || isLoading ) return ;
58
+
59
+ const userMessage : Message = {
60
+ id : Date . now ( ) . toString ( ) ,
61
+ type : 'user' ,
62
+ content : newMessage ,
63
+ timestamp : new Date ( ) ,
64
+ } ;
65
+
66
+ setMessages ( prev => [ ...prev , userMessage ] ) ;
67
+ setNewMessage ( '' ) ;
68
+ setIsLoading ( true ) ;
69
+
70
+ try {
71
+ // Here you would call your API to process the new request
72
+ // For now, we'll add a placeholder response
73
+ const assistantMessage : Message = {
74
+ id : ( Date . now ( ) + 1 ) . toString ( ) ,
75
+ type : 'assistant' ,
76
+ content : "I understand your request. Let me update the component for you..." ,
77
+ timestamp : new Date ( ) ,
78
+ } ;
79
+
80
+ setMessages ( prev => [ ...prev , assistantMessage ] ) ;
81
+
82
+ // TODO: Implement actual regeneration with the new prompt
83
+ // This would call your generate API with the conversation context
84
+
85
+ } catch ( error ) {
86
+ console . error ( 'Error processing message:' , error ) ;
87
+ const errorMessage : Message = {
88
+ id : ( Date . now ( ) + 1 ) . toString ( ) ,
89
+ type : 'assistant' ,
90
+ content : "I'm sorry, there was an error processing your request. Please try again." ,
91
+ timestamp : new Date ( ) ,
92
+ } ;
93
+ setMessages ( prev => [ ...prev , errorMessage ] ) ;
94
+ } finally {
95
+ setIsLoading ( false ) ;
96
+ }
97
+ } ;
98
+
99
+ const handleKeyPress = ( e : React . KeyboardEvent ) => {
100
+ if ( e . key === 'Enter' && ! e . shiftKey ) {
101
+ e . preventDefault ( ) ;
102
+ handleSendMessage ( ) ;
103
+ }
104
+ } ;
105
+
106
+ const handleStartOver = ( ) => {
107
+ // Clear session storage and redirect to home
108
+ sessionStorage . removeItem ( 'builderResult' ) ;
109
+ sessionStorage . removeItem ( 'initialPrompt' ) ;
110
+ window . location . href = '/' ;
111
+ } ;
112
+
113
+ return (
114
+ < Card className = "h-full flex flex-col border-0 rounded-none" >
115
+ < CardHeader className = "pb-3 border-b" >
116
+ < div className = "flex items-center justify-between" >
117
+ < CardTitle className = "text-lg" > Chat</ CardTitle >
118
+ < Button
119
+ variant = "ghost"
120
+ size = "sm"
121
+ onClick = { handleStartOver }
122
+ className = "h-8 px-2"
123
+ >
124
+ < RotateCcw className = "h-4 w-4 mr-1" />
125
+ Start Over
126
+ </ Button >
127
+ </ div >
128
+ </ CardHeader >
129
+
130
+ < CardContent className = "flex-1 flex flex-col p-0" >
131
+ < ScrollArea className = "flex-1 p-4" >
132
+ < div className = "space-y-4" >
133
+ { messages . map ( ( message ) => (
134
+ < div
135
+ key = { message . id }
136
+ className = { `flex ${ message . type === 'user' ? 'justify-end' : 'justify-start' } ` }
137
+ >
138
+ < div
139
+ className = { `max-w-[80%] p-3 rounded-lg ${ message . type === 'user'
140
+ ? 'bg-primary text-primary-foreground'
141
+ : 'bg-muted text-muted-foreground'
142
+ } `}
143
+ >
144
+ < p className = "text-sm whitespace-pre-wrap" > { message . content } </ p >
145
+ < p className = "text-xs opacity-70 mt-1" >
146
+ { message . timestamp . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } ) }
147
+ </ p >
148
+ </ div >
149
+ </ div >
150
+ ) ) }
151
+ { isLoading && (
152
+ < div className = "flex justify-start" >
153
+ < div className = "bg-muted text-muted-foreground p-3 rounded-lg" >
154
+ < div className = "flex items-center space-x-2" >
155
+ < div className = "animate-pulse flex space-x-1" >
156
+ < div className = "w-2 h-2 bg-current rounded-full" > </ div >
157
+ < div className = "w-2 h-2 bg-current rounded-full" > </ div >
158
+ < div className = "w-2 h-2 bg-current rounded-full" > </ div >
159
+ </ div >
160
+ < span className = "text-sm" > Thinking...</ span >
161
+ </ div >
162
+ </ div >
163
+ </ div >
164
+ ) }
165
+ </ div >
166
+ < div ref = { messagesEndRef } />
167
+ </ ScrollArea >
168
+
169
+ < div className = "p-4 border-t" >
170
+ < div className = "flex space-x-2" >
171
+ < Input
172
+ value = { newMessage }
173
+ onChange = { ( e ) => setNewMessage ( e . target . value ) }
174
+ onKeyPress = { handleKeyPress }
175
+ placeholder = "Describe your changes..."
176
+ disabled = { isLoading }
177
+ className = "flex-1"
178
+ />
179
+ < Button
180
+ onClick = { handleSendMessage }
181
+ disabled = { ! newMessage . trim ( ) || isLoading }
182
+ size = "sm"
183
+ >
184
+ < Send className = "h-4 w-4" />
185
+ </ Button >
186
+ </ div >
187
+ </ div >
188
+ </ CardContent >
189
+ </ Card >
190
+ ) ;
191
+ }
0 commit comments