@@ -11,7 +11,7 @@ export default function BotWidget() {
11
11
const [ messages , setMessages ] = useState ( [
12
12
{
13
13
role : "bot" ,
14
- content : "👋 Hi! I’ m your DSAMate Bot. Tell me what you’ ve done so far!" ,
14
+ content : "👋 Hi! I' m your DSAMate Bot. Tell me what you' ve done so far!" ,
15
15
} ,
16
16
] ) ;
17
17
const [ loading , setLoading ] = useState ( false ) ;
@@ -72,88 +72,101 @@ export default function BotWidget() {
72
72
} ;
73
73
74
74
return (
75
- < div ref = { chatRef } className = "fixed bottom-10 right-6 z-50" >
75
+ < div ref = { chatRef } className = "fixed bottom-4 right-4 sm:bottom-6 sm:right-6 z-40" >
76
+ { /* Bot Button - Responsive sizing */ }
76
77
< button
77
78
onClick = { ( ) => setOpen ( ! open ) }
78
- className = "bg-blue-600 w-16 h-16 rounded-full shadow-lg flex items-center justify-center"
79
+ className = "w-12 h-12 sm:w-16 sm:h-16 md:w-16 md:h-16 lg:w-16 lg:h-16
80
+ bg-blue-600 rounded-full shadow-lg hover:shadow-xl transition-all duration-300
81
+ flex items-center justify-center hover:bg-blue-700 active:scale-95"
79
82
>
80
83
< Image
81
84
src = "/assets/bot.gif"
82
85
alt = "BOT"
83
86
width = { 80 }
84
87
height = { 80 }
85
- className = "object-contain"
88
+ className = "w-full h-full object-contain p-1 "
86
89
unoptimized
87
90
/>
88
91
</ button >
89
92
93
+ { /* Chat Window - Responsive positioning and sizing */ }
90
94
{ open && (
91
- < div className = "mt-2 w-96 bg-white rounded-xl shadow-md border max-h-[70vh] flex flex-col" >
92
- < div className = "flex-1 p-4 overflow-y-auto max-h-[50vh] space-y-2" >
95
+ < div className = "absolute bottom-16 right-0 sm:bottom-20 sm:right-0
96
+ w-72 sm:w-80 md:w-96 lg:w-[400px] xl:w-[420px]
97
+ h-[50vh] sm:h-[60vh] md:h-[65vh] max-h-[450px]
98
+ bg-white rounded-xl shadow-lg border flex flex-col" >
99
+
100
+ { /* Messages Container - Responsive padding and text sizing */ }
101
+ < div className = "flex-1 p-3 sm:p-4 md:p-5 overflow-y-auto space-y-3" >
93
102
{ messages . map ( ( msg , i ) => {
94
- let contentElement ;
103
+ let contentElement ;
95
104
96
- // Try to parse JSON
97
- try {
98
- const data = JSON . parse ( msg . content ) ;
99
- if ( Array . isArray ( data ) && data [ 0 ] ?. title && data [ 0 ] ?. description ) {
100
- // Render as question cards
101
- contentElement = (
102
- < div className = "space-y-3" >
103
- { data . map ( ( q , idx ) => (
104
- < div
105
- key = { idx }
106
- className = "border rounded-lg p-3 bg-white text-black shadow-sm"
107
- >
108
- < h3 className = "font-bold text-blue-600" > { q . title } </ h3 >
109
- < p className = "text-sm mt-1" > { q . description } </ p >
110
- < p className = "text-xs mt-2 text-gray-500" >
111
- < b > Topic:</ b > { q . topic } | < b > Level:</ b > { q . level }
112
- </ p >
113
- </ div >
114
- ) ) }
115
- </ div >
116
- ) ;
117
- } else {
118
- contentElement = < span > { msg . content } </ span > ;
119
- }
120
- } catch {
121
- // Parse markdown-style formatting
122
- const formattedContent = msg . content
123
- . replace ( / \* \* ( .* ?) \* \* / g, '<strong>$1</strong>' ) // **bold**
124
- . replace ( / \* ( .* ?) \* / g, '<em>$1</em>' ) // *italic*
125
- . replace ( / ^ ( \d + \. ) \s / gm, '<strong>$1</strong> ' ) ; // numbered lists
126
-
127
- contentElement = (
128
- < span
129
- dangerouslySetInnerHTML = { { __html : formattedContent } }
130
- />
131
- ) ;
132
- }
105
+ // Try to parse JSON
106
+ try {
107
+ const data = JSON . parse ( msg . content ) ;
108
+ if ( Array . isArray ( data ) && data [ 0 ] ?. title && data [ 0 ] ?. description ) {
109
+ // Render as question cards
110
+ contentElement = (
111
+ < div className = "space-y-2 sm: space-y-3" >
112
+ { data . map ( ( q , idx ) => (
113
+ < div
114
+ key = { idx }
115
+ className = "border rounded-lg p-3 sm:p-4 bg-white text-black shadow-sm hover:shadow-md transition-shadow "
116
+ >
117
+ < h3 className = "font-bold text-blue-600 text-sm sm:text-base md:text-lg " > { q . title } </ h3 >
118
+ < p className = "text-sm sm:text-base md:text-lg mt-1" > { q . description } </ p >
119
+ < p className = "text-xs sm:text-sm md:text-base mt-2 text-gray-500" >
120
+ < b > Topic:</ b > { q . topic } | < b > Level:</ b > { q . level }
121
+ </ p >
122
+ </ div >
123
+ ) ) }
124
+ </ div >
125
+ ) ;
126
+ } else {
127
+ contentElement = < span > { msg . content } </ span > ;
128
+ }
129
+ } catch {
130
+ // Parse markdown-style formatting
131
+ const formattedContent = msg . content
132
+ . replace ( / \* \* ( .* ?) \* \* / g, '<strong>$1</strong>' ) // **bold**
133
+ . replace ( / \* ( .* ?) \* / g, '<em>$1</em>' ) // *italic*
134
+ . replace ( / ^ ( \d + \. ) \s / gm, '<strong>$1</strong> ' ) ; // numbered lists
135
+
136
+ contentElement = (
137
+ < span
138
+ dangerouslySetInnerHTML = { { __html : formattedContent } }
139
+ />
140
+ ) ;
141
+ }
133
142
134
- return (
135
- < div
136
- key = { i }
137
- className = { `p-2 rounded text-black text-sm whitespace-pre-line ${
138
- msg . role === "user"
139
- ? "bg-blue-100 text-right"
140
- : "bg-gray-100 text-left"
141
- } `}
142
- >
143
- { contentElement }
144
- </ div >
145
- ) ;
146
- } ) }
143
+ return (
144
+ < div
145
+ key = { i }
146
+ className = { `rounded-lg text-black whitespace-pre-line transition-all
147
+ p-3 sm:p-4
148
+ text-sm sm:text-base
149
+ ${ msg . role === "user"
150
+ ? "bg-blue-100 text-right ml-4"
151
+ : "bg-gray-100 text-left mr-4"
152
+ } `}
153
+ >
154
+ { contentElement }
155
+ </ div >
156
+ ) ;
157
+ } ) }
147
158
< div ref = { messagesEndRef } />
148
159
</ div >
149
160
150
- < div className = "p-4 border-t" >
151
- < div className = "flex items-center gap-2" >
161
+ { /* Input Area - Responsive padding and sizing */ }
162
+ < div className = "p-3 sm:p-4 border-t bg-gray-50" >
163
+ < div className = "flex items-center gap-2 sm:gap-3" >
152
164
< Input
153
165
value = { input }
154
166
onChange = { ( e ) => setInput ( e . target . value ) }
155
167
placeholder = "What have you covered so far?"
156
- className = "flex-grow border p-2 rounded text-black"
168
+ className = "flex-grow border p-2 sm:p-3 rounded-lg text-black focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all
169
+ text-sm sm:text-base"
157
170
disabled = { loading }
158
171
onKeyDown = { ( e ) => {
159
172
if ( e . key === "Enter" && ! loading ) {
@@ -164,21 +177,25 @@ export default function BotWidget() {
164
177
< button
165
178
onClick = { handleSend }
166
179
disabled = { loading }
167
- className = { `p-2 rounded text-white ${
180
+ className = { `p-2 sm:p-3 rounded-lg text-white transition-all duration-200 ${
168
181
loading
169
182
? "bg-gray-400 cursor-not-allowed"
170
- : "bg-blue-600 hover:bg-blue-700"
183
+ : "bg-blue-600 hover:bg-blue-700 hover:scale-105 active:scale-95 shadow-md hover:shadow-lg "
171
184
} `}
172
185
>
173
- < SendHorizonal size = { 16 } />
186
+ < SendHorizonal
187
+ className = "w-4 h-4 sm:w-5 sm:h-5"
188
+ />
174
189
</ button >
175
190
</ div >
176
191
{ loading && (
177
- < p className = "text-sm text-gray-500 mt-2" > 🤖 Thinking...</ p >
192
+ < p className = "text-gray-500 mt-2 animate-pulse text-sm" >
193
+ 🤖 Thinking...
194
+ </ p >
178
195
) }
179
196
</ div >
180
197
</ div >
181
198
) }
182
199
</ div >
183
200
) ;
184
- }
201
+ }
0 commit comments