Reubencf commited on
Commit
f7e5865
·
1 Parent(s): 270fb0f

made some changes with respect to quiz and flutter MCP

Browse files
app/components/Calendar.tsx CHANGED
@@ -30,6 +30,7 @@ interface CalendarEvent extends Holiday {
30
  type ViewMode = 'day' | 'week' | 'month' | 'year'
31
 
32
  export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: CalendarProps) {
 
33
  const [currentDate, setCurrentDate] = useState(new Date())
34
  const [view, setView] = useState<ViewMode>('month')
35
  const [customEvents, setCustomEvents] = useKV<CalendarEvent[]>('custom-calendar-events', [])
@@ -38,6 +39,25 @@ export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: C
38
  const scrollContainerRef = useRef<HTMLDivElement>(null)
39
  const [sidebarOpen, setSidebarOpen] = useState(true)
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  // Scroll to 8 AM on mount for day/week views
42
  useEffect(() => {
43
  if ((view === 'day' || view === 'week') && scrollContainerRef.current) {
@@ -184,8 +204,8 @@ export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: C
184
  onMaximize={onMaximize}
185
  onFocus={onFocus}
186
  zIndex={zIndex}
187
- width={1100}
188
- height={750}
189
  x={50}
190
  y={50}
191
  className="calendar-window !bg-[#1e1e1e]/80 !backdrop-blur-2xl border border-white/10 shadow-2xl !rounded-xl overflow-hidden"
@@ -253,28 +273,34 @@ export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: C
253
  {/* Main Content */}
254
  <div className="flex-1 flex flex-col bg-transparent">
255
  {/* Toolbar */}
256
- <div className="h-14 flex items-center justify-between px-6 border-b border-white/10 bg-[#252525]/30 backdrop-blur-sm">
257
- <div className="flex items-center gap-4">
258
- <button onClick={() => setSidebarOpen(!sidebarOpen)} className="p-2 hover:bg-white/10 rounded-md transition-colors text-gray-400 hover:text-white">
259
- <List size={20} />
260
  </button>
261
- <div className="text-2xl font-light tracking-tight">{renderHeaderTitle()}</div>
 
 
 
 
 
 
262
  </div>
263
 
264
- <div className="flex items-center gap-4">
265
- <div className="flex items-center bg-black/20 rounded-lg p-1 border border-white/5">
266
- <button onClick={handlePrev} className="p-1.5 hover:bg-white/10 rounded-md text-gray-400 hover:text-white transition-colors">
267
- <CaretLeft size={16} />
268
  </button>
269
- <button onClick={handleToday} className="px-3 py-1 text-sm font-medium text-gray-300 hover:text-white transition-colors">
270
  Today
271
  </button>
272
- <button onClick={handleNext} className="p-1.5 hover:bg-white/10 rounded-md text-gray-400 hover:text-white transition-colors">
273
- <CaretRight size={16} />
274
  </button>
275
  </div>
276
 
277
- <div className="flex items-center bg-black/20 rounded-lg p-1 border border-white/5">
278
  {(['day', 'week', 'month', 'year'] as ViewMode[]).map((v) => (
279
  <button
280
  key={v}
@@ -289,10 +315,22 @@ export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: C
289
  ))}
290
  </div>
291
 
292
- <button className="p-2 hover:bg-white/10 rounded-full transition-colors text-gray-400 hover:text-white">
 
 
 
 
 
 
 
 
 
 
 
 
293
  <Plus size={20} />
294
  </button>
295
- <button className="p-2 hover:bg-white/10 rounded-full transition-colors text-gray-400 hover:text-white">
296
  <MagnifyingGlass size={20} />
297
  </button>
298
  </div>
@@ -310,11 +348,12 @@ export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: C
310
  transition={{ duration: 0.2 }}
311
  className="h-full flex flex-col overflow-auto [&::-webkit-scrollbar-thumb]:bg-white/20 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar]:h-2"
312
  >
313
- <div className="min-w-[700px] min-h-[600px] flex-1 flex flex-col">
314
  <div className="grid grid-cols-7 border-b border-white/5 bg-black/10 sticky top-0 z-10 backdrop-blur-sm">
315
  {weekDays.map(day => (
316
- <div key={day} className="py-2 text-right pr-4 text-xs font-medium text-gray-500 uppercase tracking-wider">
317
- {day}
 
318
  </div>
319
  ))}
320
  </div>
@@ -328,7 +367,7 @@ export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: C
328
  <div
329
  key={index}
330
  className={`
331
- border-b border-r border-white/5 p-1 relative group transition-colors
332
  ${!dateObj.isCurrentMonth ? 'bg-black/20 text-gray-600' : 'hover:bg-white/5'}
333
  `}
334
  onClick={() => {
@@ -336,10 +375,10 @@ export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: C
336
  setView('day')
337
  }}
338
  >
339
- <div className="flex justify-end mb-1">
340
  <span
341
  className={`
342
- text-sm font-medium w-7 h-7 flex items-center justify-center rounded-full
343
  ${isCurrentDay
344
  ? 'bg-red-500 text-white shadow-lg shadow-red-900/50'
345
  : dateObj.isCurrentMonth ? 'text-gray-300' : 'text-gray-600'}
@@ -348,18 +387,24 @@ export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: C
348
  {dateObj.day}
349
  </span>
350
  </div>
351
- <div className="space-y-1 overflow-y-auto max-h-[calc(100%-2rem)] [&::-webkit-scrollbar]:hidden">
352
- {dayEvents.map((event, i) => (
353
  <div
354
  key={i}
355
  className={`
356
- text-[10px] px-1.5 py-0.5 rounded-sm truncate cursor-pointer hover:opacity-80
357
  ${event.color || 'bg-blue-500'} text-white font-medium shadow-sm backdrop-blur-sm bg-opacity-80
358
  `}
 
359
  >
360
  {event.title}
361
  </div>
362
  ))}
 
 
 
 
 
363
  </div>
364
  </div>
365
  )
 
30
  type ViewMode = 'day' | 'week' | 'month' | 'year'
31
 
32
  export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: CalendarProps) {
33
+ const [windowSize, setWindowSize] = useState({ width: 1100, height: 750 })
34
  const [currentDate, setCurrentDate] = useState(new Date())
35
  const [view, setView] = useState<ViewMode>('month')
36
  const [customEvents, setCustomEvents] = useKV<CalendarEvent[]>('custom-calendar-events', [])
 
39
  const scrollContainerRef = useRef<HTMLDivElement>(null)
40
  const [sidebarOpen, setSidebarOpen] = useState(true)
41
 
42
+ // Handle window resize
43
+ useEffect(() => {
44
+ const handleResize = () => {
45
+ const isMobile = window.innerWidth < 768
46
+ setWindowSize({
47
+ width: Math.min(1100, window.innerWidth - (isMobile ? 20 : 40)),
48
+ height: Math.min(750, window.innerHeight - (isMobile ? 20 : 40))
49
+ })
50
+ // Auto-close sidebar on mobile
51
+ if (isMobile && sidebarOpen) {
52
+ setSidebarOpen(false)
53
+ }
54
+ }
55
+
56
+ handleResize() // Set initial size
57
+ window.addEventListener('resize', handleResize)
58
+ return () => window.removeEventListener('resize', handleResize)
59
+ }, [sidebarOpen])
60
+
61
  // Scroll to 8 AM on mount for day/week views
62
  useEffect(() => {
63
  if ((view === 'day' || view === 'week') && scrollContainerRef.current) {
 
204
  onMaximize={onMaximize}
205
  onFocus={onFocus}
206
  zIndex={zIndex}
207
+ width={windowSize.width}
208
+ height={windowSize.height}
209
  x={50}
210
  y={50}
211
  className="calendar-window !bg-[#1e1e1e]/80 !backdrop-blur-2xl border border-white/10 shadow-2xl !rounded-xl overflow-hidden"
 
273
  {/* Main Content */}
274
  <div className="flex-1 flex flex-col bg-transparent">
275
  {/* Toolbar */}
276
+ <div className="h-12 sm:h-14 flex items-center justify-between px-2 sm:px-6 border-b border-white/10 bg-[#252525]/30 backdrop-blur-sm">
277
+ <div className="flex items-center gap-2 sm:gap-4">
278
+ <button onClick={() => setSidebarOpen(!sidebarOpen)} className="p-1.5 sm:p-2 hover:bg-white/10 rounded-md transition-colors text-gray-400 hover:text-white">
279
+ <List size={18} className="sm:w-5 sm:h-5" />
280
  </button>
281
+ <div className="text-base sm:text-2xl font-light tracking-tight hidden sm:block">{renderHeaderTitle()}</div>
282
+ <div className="text-sm font-medium tracking-tight sm:hidden">
283
+ {view === 'month' && `${monthNames[currentDate.getMonth()].substr(0, 3)} ${currentDate.getFullYear()}`}
284
+ {view === 'year' && currentDate.getFullYear()}
285
+ {view === 'day' && `${currentDate.getDate()} ${monthNames[currentDate.getMonth()].substr(0, 3)}`}
286
+ {view === 'week' && 'Week View'}
287
+ </div>
288
  </div>
289
 
290
+ <div className="flex items-center gap-1 sm:gap-4">
291
+ <div className="flex items-center bg-black/20 rounded-lg p-0.5 sm:p-1 border border-white/5">
292
+ <button onClick={handlePrev} className="p-1 sm:p-1.5 hover:bg-white/10 rounded-md text-gray-400 hover:text-white transition-colors">
293
+ <CaretLeft size={14} className="sm:w-4 sm:h-4" />
294
  </button>
295
+ <button onClick={handleToday} className="hidden sm:block px-3 py-1 text-sm font-medium text-gray-300 hover:text-white transition-colors">
296
  Today
297
  </button>
298
+ <button onClick={handleNext} className="p-1 sm:p-1.5 hover:bg-white/10 rounded-md text-gray-400 hover:text-white transition-colors">
299
+ <CaretRight size={14} className="sm:w-4 sm:h-4" />
300
  </button>
301
  </div>
302
 
303
+ <div className="hidden sm:flex items-center bg-black/20 rounded-lg p-1 border border-white/5">
304
  {(['day', 'week', 'month', 'year'] as ViewMode[]).map((v) => (
305
  <button
306
  key={v}
 
315
  ))}
316
  </div>
317
 
318
+ {/* Mobile view selector */}
319
+ <select
320
+ value={view}
321
+ onChange={(e) => setView(e.target.value as ViewMode)}
322
+ className="sm:hidden bg-black/20 border border-white/5 rounded-lg px-2 py-1 text-xs text-gray-300 focus:outline-none focus:ring-1 focus:ring-white/20"
323
+ >
324
+ <option value="day">Day</option>
325
+ <option value="week">Week</option>
326
+ <option value="month">Month</option>
327
+ <option value="year">Year</option>
328
+ </select>
329
+
330
+ <button className="hidden sm:block p-2 hover:bg-white/10 rounded-full transition-colors text-gray-400 hover:text-white">
331
  <Plus size={20} />
332
  </button>
333
+ <button className="hidden sm:block p-2 hover:bg-white/10 rounded-full transition-colors text-gray-400 hover:text-white">
334
  <MagnifyingGlass size={20} />
335
  </button>
336
  </div>
 
348
  transition={{ duration: 0.2 }}
349
  className="h-full flex flex-col overflow-auto [&::-webkit-scrollbar-thumb]:bg-white/20 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar]:h-2"
350
  >
351
+ <div className="flex-1 flex flex-col">
352
  <div className="grid grid-cols-7 border-b border-white/5 bg-black/10 sticky top-0 z-10 backdrop-blur-sm">
353
  {weekDays.map(day => (
354
+ <div key={day} className="py-1 sm:py-2 text-center sm:text-right pr-1 sm:pr-4 text-[10px] sm:text-xs font-medium text-gray-500 uppercase tracking-wider">
355
+ <span className="hidden sm:inline">{day}</span>
356
+ <span className="sm:hidden">{day.charAt(0)}</span>
357
  </div>
358
  ))}
359
  </div>
 
367
  <div
368
  key={index}
369
  className={`
370
+ border-b border-r border-white/5 p-0.5 sm:p-1 relative group transition-colors min-h-[50px] sm:min-h-[80px]
371
  ${!dateObj.isCurrentMonth ? 'bg-black/20 text-gray-600' : 'hover:bg-white/5'}
372
  `}
373
  onClick={() => {
 
375
  setView('day')
376
  }}
377
  >
378
+ <div className="flex justify-center sm:justify-end mb-0.5 sm:mb-1">
379
  <span
380
  className={`
381
+ text-xs sm:text-sm font-medium w-5 h-5 sm:w-7 sm:h-7 flex items-center justify-center rounded-full
382
  ${isCurrentDay
383
  ? 'bg-red-500 text-white shadow-lg shadow-red-900/50'
384
  : dateObj.isCurrentMonth ? 'text-gray-300' : 'text-gray-600'}
 
387
  {dateObj.day}
388
  </span>
389
  </div>
390
+ <div className="hidden sm:block space-y-0.5 overflow-y-auto max-h-[calc(100%-1.5rem)] [&::-webkit-scrollbar]:hidden px-0.5">
391
+ {dayEvents.slice(0, 3).map((event, i) => (
392
  <div
393
  key={i}
394
  className={`
395
+ text-[9px] px-1 py-0.5 rounded-sm truncate cursor-pointer hover:opacity-80
396
  ${event.color || 'bg-blue-500'} text-white font-medium shadow-sm backdrop-blur-sm bg-opacity-80
397
  `}
398
+ title={event.title}
399
  >
400
  {event.title}
401
  </div>
402
  ))}
403
+ {dayEvents.length > 3 && (
404
+ <div className="text-[8px] text-gray-400 px-1">
405
+ +{dayEvents.length - 3} more
406
+ </div>
407
+ )}
408
  </div>
409
  </div>
410
  )
app/components/FlutterRunner.tsx CHANGED
@@ -27,8 +27,7 @@ interface FileNode {
27
  isOpen?: boolean
28
  }
29
 
30
- export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }: FlutterRunnerProps) {
31
- const [code, setCode] = useState(initialCode || `import 'package:flutter/material.dart';
32
 
33
  void main() {
34
  runApp(const MyApp());
@@ -95,53 +94,146 @@ class _MyHomePageState extends State<MyHomePage> {
95
  ),
96
  );
97
  }
98
- }`)
99
 
 
 
100
  const [key, setKey] = useState(0)
101
  const [showFiles, setShowFiles] = useState(true)
102
- const [files, setFiles] = useState<FileNode[]>([
103
- {
104
- id: 'root',
105
- name: 'lib',
106
- type: 'folder',
107
- isOpen: true,
108
- children: [
109
- { id: 'main', name: 'main.dart', type: 'file', content: code }
110
- ]
111
- }
112
- ])
113
  const [activeFileId, setActiveFileId] = useState('main')
 
114
  const [lastSaved, setLastSaved] = useState<Date | null>(null)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
  // Auto-save to file every 2 seconds when code changes
117
  useEffect(() => {
 
 
118
  const saveTimer = setTimeout(async () => {
119
- // Check if we loaded this from a file
120
- const passkey = sessionStorage.getItem('currentPasskey')
121
- const fileName = sessionStorage.getItem('currentFileName')
122
-
123
- if (passkey && fileName && initialCode) {
124
- try {
125
- // Save to secure data
126
- await fetch('/api/data', {
127
- method: 'POST',
128
- headers: { 'Content-Type': 'application/json' },
129
- body: JSON.stringify({
130
- key: passkey,
131
- action: 'save',
132
- fileName: fileName,
133
- content: code
134
- })
135
  })
136
- setLastSaved(new Date())
137
- } catch (error) {
138
- console.error('Auto-save failed:', error)
139
- }
140
  }
141
  }, 2000)
142
 
143
  return () => clearTimeout(saveTimer)
144
- }, [code, initialCode])
145
 
146
  const handleRun = () => {
147
  setKey(prev => prev + 1)
@@ -152,7 +244,7 @@ class _MyHomePageState extends State<MyHomePage> {
152
  const url = URL.createObjectURL(blob)
153
  const a = document.createElement('a')
154
  a.href = url
155
- a.download = 'main.dart'
156
  a.click()
157
  URL.revokeObjectURL(url)
158
  }
@@ -171,6 +263,30 @@ class _MyHomePageState extends State<MyHomePage> {
171
  y={40}
172
  className="flutter-ide-window"
173
  >
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  <div className="flex h-full bg-[#1e1e1e] text-gray-300 font-sans">
175
  {/* Code Editor - Left Side */}
176
  <div className="w-[600px] flex flex-col border-r border-[#333]">
@@ -187,7 +303,7 @@ class _MyHomePageState extends State<MyHomePage> {
187
  <div className="h-4 w-[1px] bg-gray-600 mx-2" />
188
  <div className="flex items-center gap-2 px-3 py-1 bg-[#1e1e1e] rounded text-xs text-gray-400 border border-[#333]">
189
  <FileCode size={14} className="text-blue-400" />
190
- main.dart
191
  </div>
192
  {lastSaved && (
193
  <span className="text-xs text-green-400">
@@ -278,7 +394,7 @@ class _MyHomePageState extends State<MyHomePage> {
278
  {file.children?.map(child => (
279
  <div
280
  key={child.id}
281
- onClick={() => setActiveFileId(child.id)}
282
  className={`flex items-center gap-2 py-1 px-2 ml-4 text-sm rounded cursor-pointer ${activeFileId === child.id ? 'bg-[#37373d] text-white' : 'text-gray-400 hover:bg-[#2a2d2e]'
283
  }`}
284
  >
@@ -294,6 +410,7 @@ class _MyHomePageState extends State<MyHomePage> {
294
  </div>
295
  )}
296
  </div>
 
297
  </Window>
298
  )
299
  }
 
27
  isOpen?: boolean
28
  }
29
 
30
+ const DEFAULT_FLUTTER_CODE = `import 'package:flutter/material.dart';
 
31
 
32
  void main() {
33
  runApp(const MyApp());
 
94
  ),
95
  );
96
  }
97
+ }`
98
 
99
+ export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }: FlutterRunnerProps) {
100
+ const [code, setCode] = useState(initialCode || DEFAULT_FLUTTER_CODE)
101
  const [key, setKey] = useState(0)
102
  const [showFiles, setShowFiles] = useState(true)
103
+ const [files, setFiles] = useState<FileNode[]>([])
 
 
 
 
 
 
 
 
 
 
104
  const [activeFileId, setActiveFileId] = useState('main')
105
+ const [activeFileName, setActiveFileName] = useState('main.dart')
106
  const [lastSaved, setLastSaved] = useState<Date | null>(null)
107
+ const [passkey, setPasskey] = useState('')
108
+ const [isUnlocked, setIsUnlocked] = useState(false)
109
+ const [tempPasskey, setTempPasskey] = useState('')
110
+ const [loading, setLoading] = useState(false)
111
+
112
+ // Load files from secure storage
113
+ const loadFiles = async (key: string) => {
114
+ setLoading(true)
115
+ try {
116
+ const response = await fetch(`/api/data?key=${encodeURIComponent(key)}&folder=`)
117
+ const data = await response.json()
118
+
119
+ if (data.error) {
120
+ throw new Error(data.error)
121
+ }
122
+
123
+ // Filter for .dart files
124
+ const dartFiles = data.files?.filter((f: any) => f.name.endsWith('.dart')) || []
125
+
126
+ if (dartFiles.length > 0) {
127
+ const fileNodes: FileNode[] = dartFiles.map((file: any, index: number) => ({
128
+ id: `file_${index}`,
129
+ name: file.name,
130
+ type: 'file' as const,
131
+ content: file.content
132
+ }))
133
+
134
+ setFiles([{
135
+ id: 'root',
136
+ name: 'Dart Files',
137
+ type: 'folder',
138
+ isOpen: true,
139
+ children: fileNodes
140
+ }])
141
+
142
+ // Load first file
143
+ if (fileNodes.length > 0) {
144
+ setActiveFileId(fileNodes[0].id)
145
+ setActiveFileName(fileNodes[0].name)
146
+ setCode(fileNodes[0].content || DEFAULT_FLUTTER_CODE)
147
+ }
148
+ } else {
149
+ // No files found, create default
150
+ setFiles([{
151
+ id: 'root',
152
+ name: 'lib',
153
+ type: 'folder',
154
+ isOpen: true,
155
+ children: [
156
+ { id: 'main', name: 'main.dart', type: 'file', content: DEFAULT_FLUTTER_CODE }
157
+ ]
158
+ }])
159
+ setCode(DEFAULT_FLUTTER_CODE)
160
+ setActiveFileName('main.dart')
161
+ }
162
+ } catch (err) {
163
+ console.error('Error loading files:', err)
164
+ // Create default on error
165
+ setFiles([{
166
+ id: 'root',
167
+ name: 'lib',
168
+ type: 'folder',
169
+ isOpen: true,
170
+ children: [
171
+ { id: 'main', name: 'main.dart', type: 'file', content: DEFAULT_FLUTTER_CODE }
172
+ ]
173
+ }])
174
+ setCode(DEFAULT_FLUTTER_CODE)
175
+ setActiveFileName('main.dart')
176
+ } finally {
177
+ setLoading(false)
178
+ }
179
+ }
180
+
181
+ const handleUnlock = async () => {
182
+ if (tempPasskey.trim().length >= 4) {
183
+ setPasskey(tempPasskey.trim())
184
+ setIsUnlocked(true)
185
+ await loadFiles(tempPasskey.trim())
186
+ } else {
187
+ alert('Passkey must be at least 4 characters')
188
+ }
189
+ }
190
+
191
+ const handleFileClick = (file: FileNode) => {
192
+ if (file.type === 'file') {
193
+ setActiveFileId(file.id)
194
+ setActiveFileName(file.name)
195
+ setCode(file.content || '')
196
+ }
197
+ }
198
+
199
+ // Check for initial passkey on mount
200
+ useEffect(() => {
201
+ const sessionPasskey = sessionStorage.getItem('currentPasskey')
202
+ if (sessionPasskey) {
203
+ setPasskey(sessionPasskey)
204
+ setIsUnlocked(true)
205
+ loadFiles(sessionPasskey)
206
+ } else if (initialCode) {
207
+ setCode(initialCode)
208
+ }
209
+ }, [initialCode])
210
 
211
  // Auto-save to file every 2 seconds when code changes
212
  useEffect(() => {
213
+ if (!passkey || !isUnlocked || !activeFileName) return
214
+
215
  const saveTimer = setTimeout(async () => {
216
+ try {
217
+ // Save to secure data
218
+ await fetch('/api/data', {
219
+ method: 'POST',
220
+ headers: { 'Content-Type': 'application/json' },
221
+ body: JSON.stringify({
222
+ key: passkey,
223
+ passkey: passkey,
224
+ action: 'save_file',
225
+ fileName: activeFileName,
226
+ content: code
 
 
 
 
 
227
  })
228
+ })
229
+ setLastSaved(new Date())
230
+ } catch (error) {
231
+ console.error('Auto-save failed:', error)
232
  }
233
  }, 2000)
234
 
235
  return () => clearTimeout(saveTimer)
236
+ }, [code, passkey, activeFileName, isUnlocked])
237
 
238
  const handleRun = () => {
239
  setKey(prev => prev + 1)
 
244
  const url = URL.createObjectURL(blob)
245
  const a = document.createElement('a')
246
  a.href = url
247
+ a.download = activeFileName || 'main.dart'
248
  a.click()
249
  URL.revokeObjectURL(url)
250
  }
 
263
  y={40}
264
  className="flutter-ide-window"
265
  >
266
+ {!isUnlocked ? (
267
+ <div className="flex h-full bg-[#1e1e1e] items-center justify-center">
268
+ <div className="bg-[#252526] p-8 rounded-lg shadow-xl border border-[#333] max-w-md w-full">
269
+ <h2 className="text-xl font-bold text-white mb-4">Enter Passkey</h2>
270
+ <p className="text-gray-400 mb-6">Enter your passkey to access Dart/Flutter files</p>
271
+ <input
272
+ type="password"
273
+ value={tempPasskey}
274
+ onChange={(e) => setTempPasskey(e.target.value)}
275
+ onKeyPress={(e) => e.key === 'Enter' && handleUnlock()}
276
+ placeholder="Your passkey"
277
+ className="w-full px-4 py-2 bg-[#1e1e1e] border border-[#333] rounded text-white focus:outline-none focus:border-blue-500"
278
+ autoFocus
279
+ />
280
+ <button
281
+ onClick={handleUnlock}
282
+ disabled={loading}
283
+ className="w-full mt-4 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded font-medium disabled:opacity-50"
284
+ >
285
+ {loading ? 'Loading...' : 'Unlock'}
286
+ </button>
287
+ </div>
288
+ </div>
289
+ ) : (
290
  <div className="flex h-full bg-[#1e1e1e] text-gray-300 font-sans">
291
  {/* Code Editor - Left Side */}
292
  <div className="w-[600px] flex flex-col border-r border-[#333]">
 
303
  <div className="h-4 w-[1px] bg-gray-600 mx-2" />
304
  <div className="flex items-center gap-2 px-3 py-1 bg-[#1e1e1e] rounded text-xs text-gray-400 border border-[#333]">
305
  <FileCode size={14} className="text-blue-400" />
306
+ {activeFileName}
307
  </div>
308
  {lastSaved && (
309
  <span className="text-xs text-green-400">
 
394
  {file.children?.map(child => (
395
  <div
396
  key={child.id}
397
+ onClick={() => handleFileClick(child)}
398
  className={`flex items-center gap-2 py-1 px-2 ml-4 text-sm rounded cursor-pointer ${activeFileId === child.id ? 'bg-[#37373d] text-white' : 'text-gray-400 hover:bg-[#2a2d2e]'
399
  }`}
400
  >
 
410
  </div>
411
  )}
412
  </div>
413
+ )}
414
  </Window>
415
  )
416
  }
app/components/LaTeXEditor.tsx CHANGED
@@ -58,60 +58,146 @@ Here is an equation:
58
  export function LaTeXEditor({ onClose, onMinimize, onMaximize, initialContent }: LaTeXEditorProps) {
59
  const [code, setCode] = useState(initialContent || DEFAULT_LATEX)
60
  const [showSidebar, setShowSidebar] = useState(true)
61
- const [files, setFiles] = useState<FileNode[]>([
62
- {
63
- id: 'root',
64
- name: 'Project',
65
- type: 'folder',
66
- isOpen: true,
67
- children: [
68
- { id: 'main', name: 'main.tex', type: 'file', content: code }
69
- ]
70
- }
71
- ])
72
  const [activeFileId, setActiveFileId] = useState('main')
 
73
  const previewRef = useRef<HTMLDivElement>(null)
74
  const [lastSaved, setLastSaved] = useState<Date | null>(null)
75
  const [isSaving, setIsSaving] = useState(false)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
  // Update code if initialContent changes (e.g. opening a new file)
78
  useEffect(() => {
79
  if (initialContent) {
80
  setCode(initialContent)
 
 
 
 
 
 
 
81
  }
82
  }, [initialContent])
83
 
84
  // Auto-save functionality
85
  useEffect(() => {
86
- const saveTimer = setTimeout(async () => {
87
- const passkey = sessionStorage.getItem('currentPasskey')
88
- const fileName = sessionStorage.getItem('currentFileName')
89
 
90
- if (passkey && fileName) {
91
- setIsSaving(true)
92
- try {
93
- await fetch('/api/data', {
94
- method: 'POST',
95
- headers: { 'Content-Type': 'application/json' },
96
- body: JSON.stringify({
97
- key: passkey, // Support both key and passkey params
98
- passkey: passkey,
99
- action: 'save_file',
100
- fileName,
101
- content: code
102
- })
103
  })
104
- setLastSaved(new Date())
105
- } catch (err) {
106
- console.error('Auto-save failed:', err)
107
- } finally {
108
- setIsSaving(false)
109
- }
110
  }
111
  }, 2000) // Debounce 2s
112
 
113
  return () => clearTimeout(saveTimer)
114
- }, [code])
115
 
116
  // Render LaTeX preview
117
  useEffect(() => {
@@ -164,7 +250,7 @@ export function LaTeXEditor({ onClose, onMinimize, onMaximize, initialContent }:
164
  const url = URL.createObjectURL(blob)
165
  const a = document.createElement('a')
166
  a.href = url
167
- a.download = 'main.tex'
168
  a.click()
169
  URL.revokeObjectURL(url)
170
  }
@@ -183,6 +269,30 @@ export function LaTeXEditor({ onClose, onMinimize, onMaximize, initialContent }:
183
  y={60}
184
  className="latex-studio-window"
185
  >
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  <div className="flex h-full bg-[#1e1e1e] text-gray-300 font-sans">
187
  {/* Sidebar */}
188
  {showSidebar && (
@@ -206,7 +316,7 @@ export function LaTeXEditor({ onClose, onMinimize, onMaximize, initialContent }:
206
  {file.children?.map(child => (
207
  <div
208
  key={child.id}
209
- onClick={() => setActiveFileId(child.id)}
210
  className={`flex items-center gap-2 py-1 px-2 ml-4 text-sm rounded cursor-pointer ${activeFileId === child.id ? 'bg-[#37373d] text-white' : 'text-gray-400 hover:bg-[#2a2d2e]'
211
  }`}
212
  >
@@ -237,7 +347,7 @@ export function LaTeXEditor({ onClose, onMinimize, onMaximize, initialContent }:
237
  <div className="h-4 w-[1px] bg-gray-600 mx-2" />
238
  <div className="flex items-center gap-2 px-3 py-1 bg-[#1e1e1e] rounded text-xs text-gray-400 border border-[#333]">
239
  <FileText size={14} className="text-green-400" />
240
- main.tex
241
  </div>
242
  {lastSaved && (
243
  <div className="flex items-center gap-1 text-xs text-gray-500 ml-2">
@@ -319,6 +429,7 @@ export function LaTeXEditor({ onClose, onMinimize, onMaximize, initialContent }:
319
  </div>
320
  </div>
321
  </div>
 
322
  </Window>
323
  )
324
  }
 
58
  export function LaTeXEditor({ onClose, onMinimize, onMaximize, initialContent }: LaTeXEditorProps) {
59
  const [code, setCode] = useState(initialContent || DEFAULT_LATEX)
60
  const [showSidebar, setShowSidebar] = useState(true)
61
+ const [files, setFiles] = useState<FileNode[]>([])
 
 
 
 
 
 
 
 
 
 
62
  const [activeFileId, setActiveFileId] = useState('main')
63
+ const [activeFileName, setActiveFileName] = useState('main.tex')
64
  const previewRef = useRef<HTMLDivElement>(null)
65
  const [lastSaved, setLastSaved] = useState<Date | null>(null)
66
  const [isSaving, setIsSaving] = useState(false)
67
+ const [passkey, setPasskey] = useState('')
68
+ const [isUnlocked, setIsUnlocked] = useState(false)
69
+ const [tempPasskey, setTempPasskey] = useState('')
70
+ const [loading, setLoading] = useState(false)
71
+
72
+ // Load files from secure storage
73
+ const loadFiles = async (key: string) => {
74
+ setLoading(true)
75
+ try {
76
+ const response = await fetch(`/api/data?key=${encodeURIComponent(key)}&folder=`)
77
+ const data = await response.json()
78
+
79
+ if (data.error) {
80
+ throw new Error(data.error)
81
+ }
82
+
83
+ // Filter for .tex files
84
+ const texFiles = data.files?.filter((f: any) => f.name.endsWith('.tex')) || []
85
+
86
+ if (texFiles.length > 0) {
87
+ const fileNodes: FileNode[] = texFiles.map((file: any, index: number) => ({
88
+ id: `file_${index}`,
89
+ name: file.name,
90
+ type: 'file' as const,
91
+ content: file.content
92
+ }))
93
+
94
+ setFiles([{
95
+ id: 'root',
96
+ name: 'LaTeX Files',
97
+ type: 'folder',
98
+ isOpen: true,
99
+ children: fileNodes
100
+ }])
101
+
102
+ // Load first file
103
+ if (fileNodes.length > 0) {
104
+ setActiveFileId(fileNodes[0].id)
105
+ setActiveFileName(fileNodes[0].name)
106
+ setCode(fileNodes[0].content || DEFAULT_LATEX)
107
+ }
108
+ } else {
109
+ // No files found, create default
110
+ setFiles([{
111
+ id: 'root',
112
+ name: 'LaTeX Files',
113
+ type: 'folder',
114
+ isOpen: true,
115
+ children: [
116
+ { id: 'main', name: 'main.tex', type: 'file', content: DEFAULT_LATEX }
117
+ ]
118
+ }])
119
+ setCode(DEFAULT_LATEX)
120
+ setActiveFileName('main.tex')
121
+ }
122
+ } catch (err) {
123
+ console.error('Error loading files:', err)
124
+ // Create default on error
125
+ setFiles([{
126
+ id: 'root',
127
+ name: 'LaTeX Files',
128
+ type: 'folder',
129
+ isOpen: true,
130
+ children: [
131
+ { id: 'main', name: 'main.tex', type: 'file', content: DEFAULT_LATEX }
132
+ ]
133
+ }])
134
+ setCode(DEFAULT_LATEX)
135
+ setActiveFileName('main.tex')
136
+ } finally {
137
+ setLoading(false)
138
+ }
139
+ }
140
+
141
+ const handleUnlock = async () => {
142
+ if (tempPasskey.trim().length >= 4) {
143
+ setPasskey(tempPasskey.trim())
144
+ setIsUnlocked(true)
145
+ await loadFiles(tempPasskey.trim())
146
+ } else {
147
+ alert('Passkey must be at least 4 characters')
148
+ }
149
+ }
150
+
151
+ const handleFileClick = (file: FileNode) => {
152
+ if (file.type === 'file') {
153
+ setActiveFileId(file.id)
154
+ setActiveFileName(file.name)
155
+ setCode(file.content || '')
156
+ }
157
+ }
158
 
159
  // Update code if initialContent changes (e.g. opening a new file)
160
  useEffect(() => {
161
  if (initialContent) {
162
  setCode(initialContent)
163
+ // Check if we have a passkey from session
164
+ const sessionPasskey = sessionStorage.getItem('currentPasskey')
165
+ if (sessionPasskey) {
166
+ setPasskey(sessionPasskey)
167
+ setIsUnlocked(true)
168
+ loadFiles(sessionPasskey)
169
+ }
170
  }
171
  }, [initialContent])
172
 
173
  // Auto-save functionality
174
  useEffect(() => {
175
+ if (!passkey || !isUnlocked || !activeFileName) return
 
 
176
 
177
+ const saveTimer = setTimeout(async () => {
178
+ setIsSaving(true)
179
+ try {
180
+ await fetch('/api/data', {
181
+ method: 'POST',
182
+ headers: { 'Content-Type': 'application/json' },
183
+ body: JSON.stringify({
184
+ key: passkey,
185
+ passkey: passkey,
186
+ action: 'save_file',
187
+ fileName: activeFileName,
188
+ content: code
 
189
  })
190
+ })
191
+ setLastSaved(new Date())
192
+ } catch (err) {
193
+ console.error('Auto-save failed:', err)
194
+ } finally {
195
+ setIsSaving(false)
196
  }
197
  }, 2000) // Debounce 2s
198
 
199
  return () => clearTimeout(saveTimer)
200
+ }, [code, passkey, activeFileName, isUnlocked])
201
 
202
  // Render LaTeX preview
203
  useEffect(() => {
 
250
  const url = URL.createObjectURL(blob)
251
  const a = document.createElement('a')
252
  a.href = url
253
+ a.download = activeFileName || 'main.tex'
254
  a.click()
255
  URL.revokeObjectURL(url)
256
  }
 
269
  y={60}
270
  className="latex-studio-window"
271
  >
272
+ {!isUnlocked ? (
273
+ <div className="flex h-full bg-[#1e1e1e] items-center justify-center">
274
+ <div className="bg-[#252526] p-8 rounded-lg shadow-xl border border-[#333] max-w-md w-full">
275
+ <h2 className="text-xl font-bold text-white mb-4">Enter Passkey</h2>
276
+ <p className="text-gray-400 mb-6">Enter your passkey to access LaTeX files</p>
277
+ <input
278
+ type="password"
279
+ value={tempPasskey}
280
+ onChange={(e) => setTempPasskey(e.target.value)}
281
+ onKeyPress={(e) => e.key === 'Enter' && handleUnlock()}
282
+ placeholder="Your passkey"
283
+ className="w-full px-4 py-2 bg-[#1e1e1e] border border-[#333] rounded text-white focus:outline-none focus:border-blue-500"
284
+ autoFocus
285
+ />
286
+ <button
287
+ onClick={handleUnlock}
288
+ disabled={loading}
289
+ className="w-full mt-4 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded font-medium disabled:opacity-50"
290
+ >
291
+ {loading ? 'Loading...' : 'Unlock'}
292
+ </button>
293
+ </div>
294
+ </div>
295
+ ) : (
296
  <div className="flex h-full bg-[#1e1e1e] text-gray-300 font-sans">
297
  {/* Sidebar */}
298
  {showSidebar && (
 
316
  {file.children?.map(child => (
317
  <div
318
  key={child.id}
319
+ onClick={() => handleFileClick(child)}
320
  className={`flex items-center gap-2 py-1 px-2 ml-4 text-sm rounded cursor-pointer ${activeFileId === child.id ? 'bg-[#37373d] text-white' : 'text-gray-400 hover:bg-[#2a2d2e]'
321
  }`}
322
  >
 
347
  <div className="h-4 w-[1px] bg-gray-600 mx-2" />
348
  <div className="flex items-center gap-2 px-3 py-1 bg-[#1e1e1e] rounded text-xs text-gray-400 border border-[#333]">
349
  <FileText size={14} className="text-green-400" />
350
+ {activeFileName}
351
  </div>
352
  {lastSaved && (
353
  <div className="flex items-center gap-1 text-xs text-gray-500 ml-2">
 
429
  </div>
430
  </div>
431
  </div>
432
+ )}
433
  </Window>
434
  )
435
  }
app/components/QuizApp.tsx CHANGED
@@ -31,6 +31,7 @@ interface QuizAppProps {
31
  }
32
 
33
  export function QuizApp({ onClose, onMinimize, zIndex }: QuizAppProps) {
 
34
  const [passkey, setPasskey] = useState('')
35
  const [tempPasskey, setTempPasskey] = useState('')
36
  const [isLocked, setIsLocked] = useState(true)
@@ -47,6 +48,19 @@ export function QuizApp({ onClose, onMinimize, zIndex }: QuizAppProps) {
47
  const [completed, setCompleted] = useState(false)
48
  const [saving, setSaving] = useState(false)
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
  const handlePasskeySubmit = () => {
52
  if (tempPasskey.trim().length >= 4) {
@@ -246,11 +260,11 @@ export function QuizApp({ onClose, onMinimize, zIndex }: QuizAppProps) {
246
  onClose={onClose}
247
  onMinimize={onMinimize}
248
  zIndex={zIndex}
249
- width={800}
250
- height={600}
251
  className="bg-[#F5F5F7] font-sans"
252
  >
253
- <div className="h-full flex flex-col">
254
  {/* Header */}
255
  <div className="bg-[#F5F5F7]/80 backdrop-blur-xl border-b border-[#000000]/10 p-4 flex items-center justify-between sticky top-0 z-10">
256
  <div className="flex items-center gap-3">
@@ -280,7 +294,7 @@ export function QuizApp({ onClose, onMinimize, zIndex }: QuizAppProps) {
280
  </div>
281
 
282
  {/* Content */}
283
- <div className="flex-1 p-8 overflow-y-auto flex flex-col items-center justify-center">
284
  {isLocked ? (
285
  <div className="flex flex-col items-center gap-6 max-w-md w-full bg-white p-8 rounded-2xl shadow-sm border border-gray-200">
286
  <div className="w-16 h-16 bg-blue-50 rounded-full flex items-center justify-center">
@@ -366,72 +380,74 @@ export function QuizApp({ onClose, onMinimize, zIndex }: QuizAppProps) {
366
  </div>
367
  </div>
368
  ) : questions.length > 0 ? (
369
- <div className="w-full max-w-2xl flex flex-col h-full justify-center">
370
  {/* Question Card */}
371
- <div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
372
- <div className="p-8 border-b border-gray-100">
373
- <h3 className="text-xl font-semibold text-gray-900 leading-relaxed">
374
  {questions[currentQuestionIndex].question}
375
  </h3>
376
  </div>
377
 
378
- <div className="p-4 bg-gray-50/50 space-y-2">
379
- {questions[currentQuestionIndex].options.map((option, idx) => {
380
- const currentQId = questions[currentQuestionIndex].id
381
- const isSelected = answers[currentQId] === option
382
- return (
383
- <button
384
- key={idx}
385
- onClick={() => handleOptionSelect(option)}
386
- className={`w-full text-left px-4 py-3.5 rounded-lg border transition-all duration-200 flex items-center gap-3 text-[15px] ${isSelected
387
- ? 'bg-blue-600 border-blue-600 text-white shadow-md shadow-blue-500/20'
388
- : 'bg-white border-gray-200 hover:border-gray-300 hover:bg-gray-50 text-gray-700'
389
- }`}
390
- >
391
- <div className={`w-5 h-5 rounded-full border flex items-center justify-center flex-shrink-0 ${isSelected ? 'border-white bg-white/20' : 'border-gray-300'
392
- }`}>
393
- {isSelected && <div className="w-2.5 h-2.5 bg-white rounded-full" />}
394
- </div>
395
- <span className="font-medium">{option}</span>
396
- </button>
397
- )
398
- })}
 
 
399
  </div>
400
 
401
- <div className="px-6 py-4 bg-white border-t border-gray-100 flex justify-between items-center">
402
  <button
403
  onClick={handlePrev}
404
  disabled={currentQuestionIndex === 0}
405
- className="flex items-center gap-1.5 px-3 py-1.5 text-gray-500 hover:text-gray-800 disabled:opacity-30 disabled:hover:text-gray-500 transition-colors text-sm font-medium"
406
  >
407
- <CaretLeft size={16} weight="bold" />
408
- Back
409
  </button>
410
 
411
  <button
412
  onClick={handleNext}
413
  disabled={!answers[questions[currentQuestionIndex]?.id]}
414
- className={`flex items-center gap-2 px-5 py-2 rounded-lg text-sm font-medium shadow-sm transition-all ${!answers[questions[currentQuestionIndex]?.id]
415
  ? 'bg-gray-100 text-gray-400 cursor-not-allowed'
416
  : 'bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800'
417
  }`}
418
  >
419
  {currentQuestionIndex === questions.length - 1 ? 'Finish' : 'Next'}
420
- {currentQuestionIndex !== questions.length - 1 && <CaretRight size={16} weight="bold" />}
421
  </button>
422
  </div>
423
  </div>
424
 
425
  {/* Progress Indicator */}
426
- <div className="mt-8 flex items-center justify-center gap-1.5">
427
  {questions.map((_, idx) => (
428
  <div
429
  key={idx}
430
- className={`h-1.5 rounded-full transition-all duration-300 ${idx === currentQuestionIndex
431
- ? 'w-8 bg-blue-600'
432
  : idx < currentQuestionIndex
433
- ? 'w-1.5 bg-gray-300'
434
- : 'w-1.5 bg-gray-200'
435
  }`}
436
  />
437
  ))}
 
31
  }
32
 
33
  export function QuizApp({ onClose, onMinimize, zIndex }: QuizAppProps) {
34
+ const [windowSize, setWindowSize] = useState({ width: 800, height: 600 })
35
  const [passkey, setPasskey] = useState('')
36
  const [tempPasskey, setTempPasskey] = useState('')
37
  const [isLocked, setIsLocked] = useState(true)
 
48
  const [completed, setCompleted] = useState(false)
49
  const [saving, setSaving] = useState(false)
50
 
51
+ // Handle window resize
52
+ useEffect(() => {
53
+ const handleResize = () => {
54
+ setWindowSize({
55
+ width: Math.min(800, window.innerWidth - 40),
56
+ height: Math.min(600, window.innerHeight - 40)
57
+ })
58
+ }
59
+
60
+ handleResize() // Set initial size
61
+ window.addEventListener('resize', handleResize)
62
+ return () => window.removeEventListener('resize', handleResize)
63
+ }, [])
64
 
65
  const handlePasskeySubmit = () => {
66
  if (tempPasskey.trim().length >= 4) {
 
260
  onClose={onClose}
261
  onMinimize={onMinimize}
262
  zIndex={zIndex}
263
+ width={windowSize.width}
264
+ height={windowSize.height}
265
  className="bg-[#F5F5F7] font-sans"
266
  >
267
+ <div className="h-full flex flex-col overflow-hidden">
268
  {/* Header */}
269
  <div className="bg-[#F5F5F7]/80 backdrop-blur-xl border-b border-[#000000]/10 p-4 flex items-center justify-between sticky top-0 z-10">
270
  <div className="flex items-center gap-3">
 
294
  </div>
295
 
296
  {/* Content */}
297
+ <div className="flex-1 p-4 sm:p-6 md:p-8 overflow-y-auto flex flex-col items-center justify-center min-h-0">
298
  {isLocked ? (
299
  <div className="flex flex-col items-center gap-6 max-w-md w-full bg-white p-8 rounded-2xl shadow-sm border border-gray-200">
300
  <div className="w-16 h-16 bg-blue-50 rounded-full flex items-center justify-center">
 
380
  </div>
381
  </div>
382
  ) : questions.length > 0 ? (
383
+ <div className="w-full max-w-2xl flex flex-col h-full justify-between py-2">
384
  {/* Question Card */}
385
+ <div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden flex-1 flex flex-col">
386
+ <div className="p-3 sm:p-4 md:p-6 border-b border-gray-100 flex-shrink-0">
387
+ <h3 className="text-base sm:text-lg md:text-xl font-semibold text-gray-900 leading-snug">
388
  {questions[currentQuestionIndex].question}
389
  </h3>
390
  </div>
391
 
392
+ <div className="p-3 sm:p-4 bg-gray-50/50 overflow-y-auto flex-1 min-h-0">
393
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-2 sm:gap-3">
394
+ {questions[currentQuestionIndex].options.map((option, idx) => {
395
+ const currentQId = questions[currentQuestionIndex].id
396
+ const isSelected = answers[currentQId] === option
397
+ return (
398
+ <button
399
+ key={idx}
400
+ onClick={() => handleOptionSelect(option)}
401
+ className={`text-left px-3 sm:px-4 py-2 sm:py-2.5 rounded-lg border transition-all duration-200 flex items-start gap-2 text-sm ${isSelected
402
+ ? 'bg-blue-600 border-blue-600 text-white shadow-md shadow-blue-500/20'
403
+ : 'bg-white border-gray-200 hover:border-gray-300 hover:bg-gray-50 text-gray-700'
404
+ }`}
405
+ >
406
+ <div className={`w-4 h-4 rounded-full border flex items-center justify-center flex-shrink-0 mt-0.5 ${isSelected ? 'border-white bg-white/20' : 'border-gray-300'
407
+ }`}>
408
+ {isSelected && <div className="w-2 h-2 bg-white rounded-full" />}
409
+ </div>
410
+ <span className="font-medium break-words flex-1 leading-tight">{option}</span>
411
+ </button>
412
+ )
413
+ })}
414
+ </div>
415
  </div>
416
 
417
+ <div className="px-3 sm:px-6 py-3 sm:py-4 bg-white border-t border-gray-100 flex justify-between items-center flex-shrink-0">
418
  <button
419
  onClick={handlePrev}
420
  disabled={currentQuestionIndex === 0}
421
+ className="flex items-center gap-1 sm:gap-1.5 px-2 sm:px-3 py-1 sm:py-1.5 text-gray-500 hover:text-gray-800 disabled:opacity-30 disabled:hover:text-gray-500 transition-colors text-xs sm:text-sm font-medium"
422
  >
423
+ <CaretLeft size={14} className="sm:w-4 sm:h-4" weight="bold" />
424
+ <span className="hidden sm:inline">Back</span>
425
  </button>
426
 
427
  <button
428
  onClick={handleNext}
429
  disabled={!answers[questions[currentQuestionIndex]?.id]}
430
+ className={`flex items-center gap-1 sm:gap-2 px-3 sm:px-5 py-1.5 sm:py-2 rounded-lg text-xs sm:text-sm font-medium shadow-sm transition-all ${!answers[questions[currentQuestionIndex]?.id]
431
  ? 'bg-gray-100 text-gray-400 cursor-not-allowed'
432
  : 'bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800'
433
  }`}
434
  >
435
  {currentQuestionIndex === questions.length - 1 ? 'Finish' : 'Next'}
436
+ {currentQuestionIndex !== questions.length - 1 && <CaretRight size={14} className="sm:w-4 sm:h-4" weight="bold" />}
437
  </button>
438
  </div>
439
  </div>
440
 
441
  {/* Progress Indicator */}
442
+ <div className="mt-2 sm:mt-3 flex items-center justify-center gap-1 flex-shrink-0">
443
  {questions.map((_, idx) => (
444
  <div
445
  key={idx}
446
+ className={`h-1 rounded-full transition-all duration-300 ${idx === currentQuestionIndex
447
+ ? 'w-6 bg-blue-600'
448
  : idx < currentQuestionIndex
449
+ ? 'w-1 bg-gray-300'
450
+ : 'w-1 bg-gray-200'
451
  }`}
452
  />
453
  ))}