Reubencf commited on
Commit
467789d
·
1 Parent(s): 166641c

quiz master

Browse files
app/components/Desktop.tsx CHANGED
@@ -19,9 +19,10 @@ import { ContextMenu } from './ContextMenu'
19
  import { AboutModal } from './AboutModal'
20
  import { SessionManagerWindow } from './SessionManagerWindow'
21
  import { FlutterRunner } from './FlutterRunner'
22
- import { ResearchBrowser } from './ResearchBrowser'
23
  import { FlutterCodeEditor } from './FlutterCodeEditor'
24
  import { LaTeXEditor } from './LaTeXEditor'
 
25
  import { motion, AnimatePresence } from 'framer-motion'
26
  import { SystemPowerOverlay } from './SystemPowerOverlay'
27
  import {
@@ -64,9 +65,10 @@ export function Desktop() {
64
  const [sessionManagerOpen, setSessionManagerOpen] = useState(false)
65
  const [flutterRunnerOpen, setFlutterRunnerOpen] = useState(false)
66
  const [activeFlutterApp, setActiveFlutterApp] = useState<any>(null)
67
- const [researchBrowserOpen, setResearchBrowserOpen] = useState(false)
68
  const [flutterCodeEditorOpen, setFlutterCodeEditorOpen] = useState(false)
69
  const [latexEditorOpen, setLaTeXEditorOpen] = useState(false)
 
70
 
71
  // Minimized states
72
  const [fileManagerMinimized, setFileManagerMinimized] = useState(false)
@@ -77,13 +79,14 @@ export function Desktop() {
77
 
78
  const [sessionManagerMinimized, setSessionManagerMinimized] = useState(false)
79
  const [flutterRunnerMinimized, setFlutterRunnerMinimized] = useState(false)
80
- const [researchBrowserMinimized, setResearchBrowserMinimized] = useState(false)
81
  const [flutterCodeEditorMinimized, setFlutterCodeEditorMinimized] = useState(false)
82
  const [latexEditorMinimized, setLaTeXEditorMinimized] = useState(false)
 
83
 
84
  const [powerState, setPowerState] = useState<'active' | 'sleep' | 'restart' | 'shutdown'>('active')
85
  const [globalZIndex, setGlobalZIndex] = useState(1000)
86
- const [windowZIndices, setWindowZIndices] = useState<{[key: string]: number}>({})
87
 
88
  const getNextZIndex = () => {
89
  const nextZ = globalZIndex + 1
@@ -103,9 +106,10 @@ export function Desktop() {
103
  setGeminiChatOpen(false)
104
  setSessionManagerOpen(false)
105
  setFlutterRunnerOpen(false)
106
- setResearchBrowserOpen(false)
107
  setFlutterCodeEditorOpen(false)
108
  setLaTeXEditorOpen(false)
 
109
 
110
  // Reset all minimized states
111
  setFileManagerMinimized(false)
@@ -114,9 +118,10 @@ export function Desktop() {
114
  setGeminiChatMinimized(false)
115
  setSessionManagerMinimized(false)
116
  setFlutterRunnerMinimized(false)
117
- setResearchBrowserMinimized(false)
118
  setFlutterCodeEditorMinimized(false)
119
  setLaTeXEditorMinimized(false)
 
120
 
121
  // Reset window z-indices
122
  setWindowZIndices({})
@@ -199,16 +204,7 @@ export function Desktop() {
199
  setActiveFlutterApp(null)
200
  }
201
 
202
- const openResearchBrowser = () => {
203
- setResearchBrowserOpen(true)
204
- setResearchBrowserMinimized(false)
205
- setWindowZIndices(prev => ({ ...prev, researchBrowser: getNextZIndex() }))
206
- }
207
 
208
- const closeResearchBrowser = () => {
209
- setResearchBrowserOpen(false)
210
- setResearchBrowserMinimized(false)
211
- }
212
 
213
  const openFlutterCodeEditor = () => {
214
  setFlutterCodeEditorOpen(true)
@@ -232,6 +228,17 @@ export function Desktop() {
232
  setLaTeXEditorMinimized(false)
233
  }
234
 
 
 
 
 
 
 
 
 
 
 
 
235
  const handleOpenApp = (appId: string) => {
236
  switch (appId) {
237
  case 'files':
@@ -251,15 +258,16 @@ export function Desktop() {
251
  case 'sessions':
252
  openSessionManager()
253
  break
254
- case 'research':
255
- openResearchBrowser()
256
- break
257
  case 'flutter-editor':
258
  openFlutterCodeEditor()
259
  break
260
  case 'latex-editor':
261
  openLaTeXEditor()
262
  break
 
 
 
263
  }
264
  }
265
 
@@ -513,22 +521,7 @@ export function Desktop() {
513
  })
514
  }
515
 
516
- if (researchBrowserMinimized && researchBrowserOpen) {
517
- minimizedApps.push({
518
- id: 'research',
519
- label: 'Research',
520
- icon: (
521
- <div className="bg-gradient-to-b from-purple-500 to-indigo-600 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
522
- <div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
523
- <Globe size={20} weight="duotone" className="text-white relative z-10 drop-shadow-md" />
524
- <div className="absolute bottom-1 right-1 bg-white/20 p-0.5 rounded-full backdrop-blur-md shadow-sm">
525
- <Sparkle size={6} weight="fill" className="text-yellow-300" />
526
- </div>
527
- </div>
528
- ),
529
- onRestore: () => setResearchBrowserMinimized(false)
530
- })
531
- }
532
 
533
  if (flutterCodeEditorMinimized && flutterCodeEditorOpen) {
534
  minimizedApps.push({
@@ -564,6 +557,20 @@ export function Desktop() {
564
  })
565
  }
566
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  // Debug info
568
  useEffect(() => {
569
  console.log('App States:', {
@@ -693,16 +700,7 @@ export function Desktop() {
693
  />
694
  </div>
695
 
696
- <div className="pointer-events-auto w-24 h-24">
697
- <DraggableDesktopIcon
698
- id="research"
699
- label="Research"
700
- iconType="research"
701
- initialPosition={{ x: 0, y: 0 }}
702
- onClick={() => { }}
703
- onDoubleClick={openResearchBrowser}
704
- />
705
- </div>
706
 
707
  <div className="pointer-events-auto w-24 h-24">
708
  <DraggableDesktopIcon
@@ -725,6 +723,17 @@ export function Desktop() {
725
  onDoubleClick={openLaTeXEditor}
726
  />
727
  </div>
 
 
 
 
 
 
 
 
 
 
 
728
  </div>
729
 
730
  {/* Windows Container */}
@@ -747,10 +756,10 @@ export function Desktop() {
747
  }}
748
  >
749
  <FileManager
750
- currentPath={currentPath}
751
- onNavigate={setCurrentPath}
752
- onClose={closeFileManager}
753
- onOpenFlutterApp={openFlutterRunner}
754
  onMinimize={() => setFileManagerMinimized(true)}
755
  onFocus={() => bringWindowToFront('fileManager')}
756
  zIndex={windowZIndices.fileManager || 1000}
@@ -853,14 +862,14 @@ export function Desktop() {
853
  display: sessionManagerMinimized ? 'none' : 'block'
854
  }}
855
  >
856
- <SessionManagerWindow
857
- onClose={closeSessionManager}
858
- sessionId={userSession}
859
- sessionKey={sessionKey}
860
- onMinimize={() => setSessionManagerMinimized(true)}
861
- />
862
- </motion.div>
863
- )}
864
 
865
  {flutterRunnerOpen && activeFlutterApp && (
866
  <motion.div
@@ -878,81 +887,84 @@ export function Desktop() {
878
  display: flutterRunnerMinimized ? 'none' : 'block'
879
  }}
880
  >
881
- <FlutterRunner
882
- initialCode={activeFlutterApp?.dartCode}
883
- sessionId={userSession}
884
- onClose={() => {
885
- setFlutterRunnerOpen(false)
886
- setActiveFlutterApp(null)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
887
  }}
888
- onMinimize={() => setFlutterRunnerMinimized(true)}
889
- />
890
- </motion.div>
891
- )}
892
-
893
- {researchBrowserOpen && (
894
- <motion.div
895
- key="research-browser"
896
- initial={{ opacity: 0, scale: 0.95 }}
897
- animate={{
898
- opacity: researchBrowserMinimized ? 0 : 1,
899
- scale: researchBrowserMinimized ? 0.9 : 1,
900
- y: researchBrowserMinimized ? 100 : 0,
901
- }}
902
- exit={{ opacity: 0, scale: 0.95 }}
903
- transition={{ duration: 0.2 }}
904
- style={{
905
- pointerEvents: researchBrowserMinimized ? 'none' : 'auto',
906
- display: researchBrowserMinimized ? 'none' : 'block'
907
- }}
908
- >
909
- <ResearchBrowser
910
- onClose={closeResearchBrowser}
911
- onMinimize={() => setResearchBrowserMinimized(true)}
912
- />
913
- </motion.div>
914
- )}
915
-
916
- {flutterCodeEditorOpen && (
917
- <motion.div
918
- key="flutter-code-editor"
919
- initial={{ opacity: 0, scale: 0.95 }}
920
- animate={{
921
- opacity: flutterCodeEditorMinimized ? 0 : 1,
922
- scale: flutterCodeEditorMinimized ? 0.9 : 1,
923
- y: flutterCodeEditorMinimized ? 100 : 0,
924
- }}
925
- exit={{ opacity: 0, scale: 0.95 }}
926
- transition={{ duration: 0.2 }}
927
- style={{
928
- pointerEvents: flutterCodeEditorMinimized ? 'none' : 'auto',
929
- display: flutterCodeEditorMinimized ? 'none' : 'block'
930
- }}
931
- >
932
- <FlutterCodeEditor onClose={closeFlutterCodeEditor} onMinimize={() => setFlutterCodeEditorMinimized(true)} />
933
- </motion.div>
934
- )}
935
-
936
- {latexEditorOpen && (
937
- <motion.div
938
- key="latex-editor"
939
- initial={{ opacity: 0, scale: 0.95 }}
940
- animate={{
941
- opacity: latexEditorMinimized ? 0 : 1,
942
- scale: latexEditorMinimized ? 0.9 : 1,
943
- y: latexEditorMinimized ? 100 : 0,
944
- }}
945
- exit={{ opacity: 0, scale: 0.95 }}
946
- transition={{ duration: 0.2 }}
947
- style={{
948
- pointerEvents: latexEditorMinimized ? 'none' : 'auto',
949
- display: latexEditorMinimized ? 'none' : 'block'
950
- }}
951
- >
952
- <LaTeXEditor onClose={closeLaTeXEditor} sessionId={userSession} onMinimize={() => setLaTeXEditorMinimized(true)} />
953
- </motion.div>
954
- )}
955
- </AnimatePresence>
956
  </div>
957
  </div>
958
 
 
19
  import { AboutModal } from './AboutModal'
20
  import { SessionManagerWindow } from './SessionManagerWindow'
21
  import { FlutterRunner } from './FlutterRunner'
22
+
23
  import { FlutterCodeEditor } from './FlutterCodeEditor'
24
  import { LaTeXEditor } from './LaTeXEditor'
25
+ import { QuizApp } from './QuizApp'
26
  import { motion, AnimatePresence } from 'framer-motion'
27
  import { SystemPowerOverlay } from './SystemPowerOverlay'
28
  import {
 
65
  const [sessionManagerOpen, setSessionManagerOpen] = useState(false)
66
  const [flutterRunnerOpen, setFlutterRunnerOpen] = useState(false)
67
  const [activeFlutterApp, setActiveFlutterApp] = useState<any>(null)
68
+
69
  const [flutterCodeEditorOpen, setFlutterCodeEditorOpen] = useState(false)
70
  const [latexEditorOpen, setLaTeXEditorOpen] = useState(false)
71
+ const [quizAppOpen, setQuizAppOpen] = useState(false)
72
 
73
  // Minimized states
74
  const [fileManagerMinimized, setFileManagerMinimized] = useState(false)
 
79
 
80
  const [sessionManagerMinimized, setSessionManagerMinimized] = useState(false)
81
  const [flutterRunnerMinimized, setFlutterRunnerMinimized] = useState(false)
82
+
83
  const [flutterCodeEditorMinimized, setFlutterCodeEditorMinimized] = useState(false)
84
  const [latexEditorMinimized, setLaTeXEditorMinimized] = useState(false)
85
+ const [quizAppMinimized, setQuizAppMinimized] = useState(false)
86
 
87
  const [powerState, setPowerState] = useState<'active' | 'sleep' | 'restart' | 'shutdown'>('active')
88
  const [globalZIndex, setGlobalZIndex] = useState(1000)
89
+ const [windowZIndices, setWindowZIndices] = useState<{ [key: string]: number }>({})
90
 
91
  const getNextZIndex = () => {
92
  const nextZ = globalZIndex + 1
 
106
  setGeminiChatOpen(false)
107
  setSessionManagerOpen(false)
108
  setFlutterRunnerOpen(false)
109
+
110
  setFlutterCodeEditorOpen(false)
111
  setLaTeXEditorOpen(false)
112
+ setQuizAppOpen(false)
113
 
114
  // Reset all minimized states
115
  setFileManagerMinimized(false)
 
118
  setGeminiChatMinimized(false)
119
  setSessionManagerMinimized(false)
120
  setFlutterRunnerMinimized(false)
121
+
122
  setFlutterCodeEditorMinimized(false)
123
  setLaTeXEditorMinimized(false)
124
+ setQuizAppMinimized(false)
125
 
126
  // Reset window z-indices
127
  setWindowZIndices({})
 
204
  setActiveFlutterApp(null)
205
  }
206
 
 
 
 
 
 
207
 
 
 
 
 
208
 
209
  const openFlutterCodeEditor = () => {
210
  setFlutterCodeEditorOpen(true)
 
228
  setLaTeXEditorMinimized(false)
229
  }
230
 
231
+ const openQuizApp = () => {
232
+ setQuizAppOpen(true)
233
+ setQuizAppMinimized(false)
234
+ setWindowZIndices(prev => ({ ...prev, quizApp: getNextZIndex() }))
235
+ }
236
+
237
+ const closeQuizApp = () => {
238
+ setQuizAppOpen(false)
239
+ setQuizAppMinimized(false)
240
+ }
241
+
242
  const handleOpenApp = (appId: string) => {
243
  switch (appId) {
244
  case 'files':
 
258
  case 'sessions':
259
  openSessionManager()
260
  break
261
+
 
 
262
  case 'flutter-editor':
263
  openFlutterCodeEditor()
264
  break
265
  case 'latex-editor':
266
  openLaTeXEditor()
267
  break
268
+ case 'quiz':
269
+ openQuizApp()
270
+ break
271
  }
272
  }
273
 
 
521
  })
522
  }
523
 
524
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525
 
526
  if (flutterCodeEditorMinimized && flutterCodeEditorOpen) {
527
  minimizedApps.push({
 
557
  })
558
  }
559
 
560
+ if (quizAppMinimized && quizAppOpen) {
561
+ minimizedApps.push({
562
+ id: 'quiz',
563
+ label: 'Quiz Master',
564
+ icon: (
565
+ <div className="bg-gradient-to-br from-teal-400 to-emerald-500 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
566
+ <div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
567
+ <Brain size={20} weight="fill" className="text-white relative z-10 drop-shadow-md" />
568
+ </div>
569
+ ),
570
+ onRestore: () => setQuizAppMinimized(false)
571
+ })
572
+ }
573
+
574
  // Debug info
575
  useEffect(() => {
576
  console.log('App States:', {
 
700
  />
701
  </div>
702
 
703
+
 
 
 
 
 
 
 
 
 
704
 
705
  <div className="pointer-events-auto w-24 h-24">
706
  <DraggableDesktopIcon
 
723
  onDoubleClick={openLaTeXEditor}
724
  />
725
  </div>
726
+
727
+ <div className="pointer-events-auto w-24 h-24">
728
+ <DraggableDesktopIcon
729
+ id="quiz"
730
+ label="Quiz Master"
731
+ iconType="quiz"
732
+ initialPosition={{ x: 0, y: 0 }}
733
+ onClick={() => { }}
734
+ onDoubleClick={openQuizApp}
735
+ />
736
+ </div>
737
  </div>
738
 
739
  {/* Windows Container */}
 
756
  }}
757
  >
758
  <FileManager
759
+ currentPath={currentPath}
760
+ onNavigate={setCurrentPath}
761
+ onClose={closeFileManager}
762
+ onOpenFlutterApp={openFlutterRunner}
763
  onMinimize={() => setFileManagerMinimized(true)}
764
  onFocus={() => bringWindowToFront('fileManager')}
765
  zIndex={windowZIndices.fileManager || 1000}
 
862
  display: sessionManagerMinimized ? 'none' : 'block'
863
  }}
864
  >
865
+ <SessionManagerWindow
866
+ onClose={closeSessionManager}
867
+ sessionId={userSession}
868
+ sessionKey={sessionKey}
869
+ onMinimize={() => setSessionManagerMinimized(true)}
870
+ />
871
+ </motion.div>
872
+ )}
873
 
874
  {flutterRunnerOpen && activeFlutterApp && (
875
  <motion.div
 
887
  display: flutterRunnerMinimized ? 'none' : 'block'
888
  }}
889
  >
890
+ <FlutterRunner
891
+ initialCode={activeFlutterApp?.dartCode}
892
+ sessionId={userSession}
893
+ onClose={() => {
894
+ setFlutterRunnerOpen(false)
895
+ setActiveFlutterApp(null)
896
+ }}
897
+ onMinimize={() => setFlutterRunnerMinimized(true)}
898
+ />
899
+ </motion.div>
900
+ )}
901
+
902
+
903
+
904
+ {flutterCodeEditorOpen && (
905
+ <motion.div
906
+ key="flutter-code-editor"
907
+ initial={{ opacity: 0, scale: 0.95 }}
908
+ animate={{
909
+ opacity: flutterCodeEditorMinimized ? 0 : 1,
910
+ scale: flutterCodeEditorMinimized ? 0.9 : 1,
911
+ y: flutterCodeEditorMinimized ? 100 : 0,
912
+ }}
913
+ exit={{ opacity: 0, scale: 0.95 }}
914
+ transition={{ duration: 0.2 }}
915
+ style={{
916
+ pointerEvents: flutterCodeEditorMinimized ? 'none' : 'auto',
917
+ display: flutterCodeEditorMinimized ? 'none' : 'block'
918
  }}
919
+ >
920
+ <FlutterCodeEditor onClose={closeFlutterCodeEditor} onMinimize={() => setFlutterCodeEditorMinimized(true)} />
921
+ </motion.div>
922
+ )}
923
+
924
+ {latexEditorOpen && (
925
+ <motion.div
926
+ key="latex-editor"
927
+ initial={{ opacity: 0, scale: 0.95 }}
928
+ animate={{
929
+ opacity: latexEditorMinimized ? 0 : 1,
930
+ scale: latexEditorMinimized ? 0.9 : 1,
931
+ y: latexEditorMinimized ? 100 : 0,
932
+ }}
933
+ exit={{ opacity: 0, scale: 0.95 }}
934
+ transition={{ duration: 0.2 }}
935
+ style={{
936
+ pointerEvents: latexEditorMinimized ? 'none' : 'auto',
937
+ display: latexEditorMinimized ? 'none' : 'block'
938
+ }}
939
+ >
940
+ <LaTeXEditor onClose={closeLaTeXEditor} sessionId={userSession} onMinimize={() => setLaTeXEditorMinimized(true)} />
941
+ </motion.div>
942
+ )}
943
+
944
+ {quizAppOpen && (
945
+ <motion.div
946
+ key="quiz-app"
947
+ initial={{ opacity: 0, scale: 0.95 }}
948
+ animate={{
949
+ opacity: quizAppMinimized ? 0 : 1,
950
+ scale: quizAppMinimized ? 0.9 : 1,
951
+ y: quizAppMinimized ? 100 : 0,
952
+ }}
953
+ exit={{ opacity: 0, scale: 0.95 }}
954
+ transition={{ duration: 0.2 }}
955
+ style={{
956
+ pointerEvents: quizAppMinimized ? 'none' : 'auto',
957
+ display: quizAppMinimized ? 'none' : 'block'
958
+ }}
959
+ >
960
+ <QuizApp
961
+ onClose={closeQuizApp}
962
+ onMinimize={() => setQuizAppMinimized(true)}
963
+ zIndex={windowZIndices.quizApp || 1000}
964
+ />
965
+ </motion.div>
966
+ )}
967
+ </AnimatePresence>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
968
  </div>
969
  </div>
970
 
app/components/DraggableDesktopIcon.tsx CHANGED
@@ -14,7 +14,8 @@ import {
14
  HardDrives,
15
  Code,
16
  Lightning,
17
- Key
 
18
  } from '@phosphor-icons/react'
19
  import { DynamicClockIcon } from './DynamicClockIcon'
20
  import { DynamicCalendarIcon } from './DynamicCalendarIcon'
@@ -66,16 +67,7 @@ export function DraggableDesktopIcon({
66
  <DynamicClockIcon />
67
  </div>
68
  )
69
- case 'research':
70
- return (
71
- <div className="bg-gradient-to-b from-purple-500 to-indigo-600 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300">
72
- <div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
73
- <Globe size={36} weight="duotone" className="text-white relative z-10 drop-shadow-md" />
74
- <div className="absolute bottom-1.5 right-1.5 bg-white/20 p-1 rounded-full backdrop-blur-md shadow-sm">
75
- <Sparkle size={10} weight="fill" className="text-yellow-300" />
76
- </div>
77
- </div>
78
- )
79
  case 'flutter':
80
  return (
81
  <div className="bg-gradient-to-b from-[#54C5F8] to-[#29B6F6] w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300">
@@ -128,6 +120,13 @@ export function DraggableDesktopIcon({
128
  <Key size={32} weight="bold" className="text-white" />
129
  </div>
130
  )
 
 
 
 
 
 
 
131
  default:
132
  return (
133
  <div className="bg-gray-400 w-full h-full rounded-xl flex items-center justify-center">
 
14
  HardDrives,
15
  Code,
16
  Lightning,
17
+ Key,
18
+ Brain
19
  } from '@phosphor-icons/react'
20
  import { DynamicClockIcon } from './DynamicClockIcon'
21
  import { DynamicCalendarIcon } from './DynamicCalendarIcon'
 
67
  <DynamicClockIcon />
68
  </div>
69
  )
70
+
 
 
 
 
 
 
 
 
 
71
  case 'flutter':
72
  return (
73
  <div className="bg-gradient-to-b from-[#54C5F8] to-[#29B6F6] w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300">
 
120
  <Key size={32} weight="bold" className="text-white" />
121
  </div>
122
  )
123
+ case 'quiz':
124
+ return (
125
+ <div className="bg-gradient-to-br from-teal-400 to-emerald-500 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300">
126
+ <div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
127
+ <Brain size={36} weight="fill" className="text-white relative z-10 drop-shadow-md" />
128
+ </div>
129
+ )
130
  default:
131
  return (
132
  <div className="bg-gray-400 w-full h-full rounded-xl flex items-center justify-center">
app/components/FileManager.tsx CHANGED
@@ -32,7 +32,8 @@ import {
32
  Calendar as CalendarIcon,
33
  Clock as ClockIcon,
34
  Sparkle,
35
- Lightning
 
36
  } from '@phosphor-icons/react'
37
  import { motion } from 'framer-motion'
38
  import { FilePreview } from './FilePreview'
@@ -351,17 +352,7 @@ export function FileManager({ currentPath, onNavigate, onClose, onOpenFlutterApp
351
  </div>
352
  )
353
  },
354
- {
355
- id: 'research', name: 'Research', icon: (
356
- <div className="bg-gradient-to-b from-purple-500 to-indigo-600 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
357
- <div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
358
- <Globe size={32} weight="duotone" className="text-white relative z-10 drop-shadow-md" />
359
- <div className="absolute bottom-1.5 right-1.5 bg-white/20 p-1 rounded-full backdrop-blur-md shadow-sm">
360
- <Sparkle size={10} weight="fill" className="text-yellow-300" />
361
- </div>
362
- </div>
363
- )
364
- },
365
  {
366
  id: 'flutter-editor', name: 'Flutter IDE', icon: (
367
  <div className="bg-gradient-to-b from-[#54C5F8] to-[#29B6F6] w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
@@ -384,6 +375,14 @@ export function FileManager({ currentPath, onNavigate, onClose, onOpenFlutterApp
384
  </div>
385
  )
386
  },
 
 
 
 
 
 
 
 
387
  ]
388
 
389
  return (
 
32
  Calendar as CalendarIcon,
33
  Clock as ClockIcon,
34
  Sparkle,
35
+ Lightning,
36
+ Brain
37
  } from '@phosphor-icons/react'
38
  import { motion } from 'framer-motion'
39
  import { FilePreview } from './FilePreview'
 
352
  </div>
353
  )
354
  },
355
+
 
 
 
 
 
 
 
 
 
 
356
  {
357
  id: 'flutter-editor', name: 'Flutter IDE', icon: (
358
  <div className="bg-gradient-to-b from-[#54C5F8] to-[#29B6F6] w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
 
375
  </div>
376
  )
377
  },
378
+ {
379
+ id: 'quiz', name: 'Quiz Master', icon: (
380
+ <div className="bg-gradient-to-br from-teal-400 to-emerald-500 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
381
+ <div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
382
+ <Brain size={32} weight="fill" className="text-white relative z-10 drop-shadow-md" />
383
+ </div>
384
+ )
385
+ },
386
  ]
387
 
388
  return (
app/components/QuizApp.tsx ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import React, { useState, useEffect } from 'react'
4
+ import { motion, AnimatePresence } from 'framer-motion'
5
+ import {
6
+ CheckCircle,
7
+ XCircle,
8
+ CaretRight,
9
+ CaretLeft,
10
+ Brain,
11
+ Spinner,
12
+ Trophy,
13
+ ArrowClockwise
14
+ } from '@phosphor-icons/react'
15
+ import Window from './Window'
16
+
17
+ interface QuizQuestion {
18
+ id: number
19
+ question: string
20
+ options: string[]
21
+ }
22
+
23
+ interface QuizAppProps {
24
+ onClose: () => void
25
+ onMinimize: () => void
26
+ zIndex: number
27
+ }
28
+
29
+ export function QuizApp({ onClose, onMinimize, zIndex }: QuizAppProps) {
30
+ const [questions, setQuestions] = useState<QuizQuestion[]>([])
31
+ const [loading, setLoading] = useState(true)
32
+ const [error, setError] = useState<string | null>(null)
33
+ const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0)
34
+ const [answers, setAnswers] = useState<Record<number, string>>({})
35
+ const [completed, setCompleted] = useState(false)
36
+ const [saving, setSaving] = useState(false)
37
+
38
+ const loadQuiz = async () => {
39
+ setLoading(true)
40
+ setError(null)
41
+ try {
42
+ // Try to fetch from public folder first
43
+ const response = await fetch('/api/public?folder=')
44
+ const data = await response.json()
45
+
46
+ const quizFile = data.files?.find((f: any) => f.name === 'quiz.json')
47
+
48
+ if (!quizFile) {
49
+ setError('No quiz found. Ask Claude to "create a quiz" first!')
50
+ setLoading(false)
51
+ return
52
+ }
53
+
54
+ // Fetch the actual content
55
+ // Since we don't have a direct "read file content" API for public files easily exposed here without downloading,
56
+ // we might need to rely on the download endpoint or a specific content endpoint.
57
+ // However, for this hackathon context, let's assume we can fetch it via the download URL or a direct public URL if served statically.
58
+ // If it's in public/, it might be served statically by Next.js if in the public folder of the project.
59
+ // But the 'public' folder in FileManager is virtual or mapped.
60
+ // Let's try to fetch via the download API which returns the file content if we use the right headers or just fetch it if it's static.
61
+
62
+ // Actually, the best way is to use the same mechanism the FileManager uses or just assume Claude writes to 'public/quiz.json' which maps to the real public folder.
63
+ // If Claude writes to the real 'public' folder, it's available at /quiz.json
64
+
65
+ const quizResponse = await fetch('/quiz.json')
66
+ if (!quizResponse.ok) {
67
+ // Fallback: try to read via the API if it was uploaded via the file manager API
68
+ // This is a bit tricky without a direct "read content" API.
69
+ // Let's assume for this demo that Claude writes to 'public/quiz.json' and it's available at root.
70
+ throw new Error('Could not load quiz.json')
71
+ }
72
+
73
+ const quizData = await quizResponse.json()
74
+ if (Array.isArray(quizData)) {
75
+ setQuestions(quizData)
76
+ } else if (quizData.questions) {
77
+ setQuestions(quizData.questions)
78
+ } else {
79
+ throw new Error('Invalid quiz format')
80
+ }
81
+ } catch (err) {
82
+ console.error('Error loading quiz:', err)
83
+ // Fallback for demo purposes if file not found
84
+ // setError('Could not load quiz.json. Please ask Claude to generate one.')
85
+
86
+ // For testing/demo, let's provide a mock if fetch fails, or just show the error.
87
+ // The user specifically wants the flow with Claude, so showing the error is better to prompt the user.
88
+ setError('Waiting for Claude to generate a quiz... (File public/quiz.json not found)')
89
+ } finally {
90
+ setLoading(false)
91
+ }
92
+ }
93
+
94
+ useEffect(() => {
95
+ loadQuiz()
96
+ }, [])
97
+
98
+ const handleOptionSelect = (option: string) => {
99
+ setAnswers(prev => ({
100
+ ...prev,
101
+ [questions[currentQuestionIndex].id]: option
102
+ }))
103
+ }
104
+
105
+ const handleNext = () => {
106
+ if (currentQuestionIndex < questions.length - 1) {
107
+ setCurrentQuestionIndex(prev => prev + 1)
108
+ } else {
109
+ finishQuiz()
110
+ }
111
+ }
112
+
113
+ const handlePrev = () => {
114
+ if (currentQuestionIndex > 0) {
115
+ setCurrentQuestionIndex(prev => prev - 1)
116
+ }
117
+ }
118
+
119
+ const finishQuiz = async () => {
120
+ setSaving(true)
121
+ try {
122
+ // Save answers to public/quiz_answers.json
123
+ // We use the upload API to save the file
124
+ const answersJson = JSON.stringify(
125
+ Object.entries(answers).map(([id, answer]) => ({
126
+ questionId: parseInt(id),
127
+ answer
128
+ })),
129
+ null,
130
+ 2
131
+ )
132
+
133
+ const blob = new Blob([answersJson], { type: 'application/json' })
134
+ const file = new File([blob], 'quiz_answers.json', { type: 'application/json' })
135
+
136
+ const formData = new FormData()
137
+ formData.append('file', file)
138
+ formData.append('folder', '') // Root of public
139
+
140
+ await fetch('/api/public', {
141
+ method: 'POST',
142
+ body: formData
143
+ })
144
+
145
+ setCompleted(true)
146
+ } catch (err) {
147
+ console.error('Error saving answers:', err)
148
+ alert('Failed to save answers. Please try again.')
149
+ } finally {
150
+ setSaving(false)
151
+ }
152
+ }
153
+
154
+ return (
155
+ <Window
156
+ id="quiz-app"
157
+ title="Quiz Master"
158
+ isOpen={true}
159
+ onClose={onClose}
160
+ onMinimize={onMinimize}
161
+ zIndex={zIndex}
162
+ width={800}
163
+ height={600}
164
+ className="bg-[#F5F5F7] font-[-apple-system,BlinkMacSystemFont,Inter,system-ui,sans-serif]"
165
+ >
166
+ <div className="h-full flex flex-col">
167
+ {/* macOS-style Header/Toolbar */}
168
+ <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">
169
+ <div className="flex items-center gap-3">
170
+ <div className="w-10 h-10 bg-gradient-to-br from-teal-400 to-emerald-500 rounded-lg shadow-sm flex items-center justify-center border border-white/10">
171
+ <Brain size={24} weight="fill" className="text-white" />
172
+ </div>
173
+ <div>
174
+ <h1 className="text-lg font-semibold text-gray-900 tracking-tight">Quiz Master</h1>
175
+ <p className="text-xs text-gray-500">Knowledge Check</p>
176
+ </div>
177
+ </div>
178
+
179
+ {!loading && !error && !completed && (
180
+ <div className="flex items-center gap-2 bg-white/50 px-3 py-1 rounded-md border border-black/5">
181
+ <span className="text-xs font-medium text-gray-500">Question {currentQuestionIndex + 1} of {questions.length}</span>
182
+ </div>
183
+ )}
184
+ </div>
185
+
186
+ {/* Content */}
187
+ <div className="flex-1 p-8 overflow-y-auto flex flex-col items-center">
188
+ {loading ? (
189
+ <div className="flex flex-col items-center justify-center h-full gap-4 text-gray-500">
190
+ <Spinner size={32} className="animate-spin text-gray-400" />
191
+ <p className="text-sm font-medium">Loading quiz...</p>
192
+ </div>
193
+ ) : error ? (
194
+ <div className="flex flex-col items-center justify-center h-full max-w-md text-center">
195
+ <div className="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
196
+ <Brain size={32} weight="duotone" className="text-gray-400" />
197
+ </div>
198
+ <h3 className="text-lg font-semibold text-gray-900 mb-2">No Quiz Found</h3>
199
+ <p className="text-sm text-gray-500 mb-6 leading-relaxed">{error}</p>
200
+
201
+ <div className="bg-white p-4 rounded-xl border border-gray-200 shadow-sm w-full text-left mb-6">
202
+ <p className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3">How to start</p>
203
+ <ol className="text-sm text-gray-600 space-y-2 list-decimal pl-4">
204
+ <li>Open <span className="font-semibold text-gray-900">Claude Desktop</span></li>
205
+ <li>Ask: "Create a quiz about [Topic]"</li>
206
+ <li>Wait for the file to be generated</li>
207
+ </ol>
208
+ </div>
209
+
210
+ <button
211
+ onClick={loadQuiz}
212
+ className="px-4 py-2 bg-white border border-gray-300 rounded-lg shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 active:bg-gray-100 transition-colors flex items-center gap-2"
213
+ >
214
+ <ArrowClockwise size={16} />
215
+ Check Again
216
+ </button>
217
+ </div>
218
+ ) : completed ? (
219
+ <div className="flex flex-col items-center justify-center h-full max-w-md text-center animate-in fade-in zoom-in duration-300">
220
+ <div className="w-20 h-20 bg-gradient-to-br from-green-400 to-emerald-500 rounded-full flex items-center justify-center mb-6 shadow-lg shadow-green-500/20">
221
+ <Trophy size={40} weight="fill" className="text-white" />
222
+ </div>
223
+ <h2 className="text-2xl font-bold text-gray-900 mb-2">Quiz Completed</h2>
224
+ <p className="text-gray-500 mb-8">Your answers have been saved successfully.</p>
225
+
226
+ <div className="bg-blue-50 p-5 rounded-xl border border-blue-100 mb-8 w-full">
227
+ <p className="font-medium text-blue-900 text-sm mb-1">Next Step</p>
228
+ <p className="text-blue-700 text-sm">
229
+ Ask Claude to <span className="font-semibold">"Grade my quiz answers"</span>
230
+ </p>
231
+ </div>
232
+
233
+ <button
234
+ onClick={onClose}
235
+ className="px-6 py-2 bg-[#007AFF] text-white rounded-lg shadow-sm hover:bg-[#0062CC] active:bg-[#0051A8] transition-colors text-sm font-medium"
236
+ >
237
+ Done
238
+ </button>
239
+ </div>
240
+ ) : questions.length > 0 ? (
241
+ <div className="w-full max-w-2xl flex flex-col h-full justify-center">
242
+ {/* Question Card */}
243
+ <div className="bg-white rounded-xl shadow-[0_2px_12px_rgba(0,0,0,0.08)] border border-gray-200/60 overflow-hidden">
244
+ <div className="p-8 border-b border-gray-100">
245
+ <h3 className="text-xl font-semibold text-gray-900 leading-relaxed">
246
+ {questions[currentQuestionIndex].question}
247
+ </h3>
248
+ </div>
249
+
250
+ <div className="p-4 bg-gray-50/50 space-y-2">
251
+ {questions[currentQuestionIndex].options.map((option, idx) => {
252
+ const isSelected = answers[questions[currentQuestionIndex].id] === option
253
+ return (
254
+ <button
255
+ key={idx}
256
+ onClick={() => handleOptionSelect(option)}
257
+ 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
258
+ ? 'bg-[#007AFF] border-[#007AFF] text-white shadow-md shadow-blue-500/20'
259
+ : 'bg-white border-gray-200 hover:border-gray-300 hover:bg-gray-50 text-gray-700'
260
+ }`}
261
+ >
262
+ <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'
263
+ }`}>
264
+ {isSelected && <div className="w-2.5 h-2.5 bg-white rounded-full" />}
265
+ </div>
266
+ <span className="font-medium">{option}</span>
267
+ </button>
268
+ )
269
+ })}
270
+ </div>
271
+
272
+ <div className="px-6 py-4 bg-white border-t border-gray-100 flex justify-between items-center">
273
+ <button
274
+ onClick={handlePrev}
275
+ disabled={currentQuestionIndex === 0}
276
+ 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"
277
+ >
278
+ <CaretLeft size={16} weight="bold" />
279
+ Back
280
+ </button>
281
+
282
+ <button
283
+ onClick={handleNext}
284
+ disabled={!answers[questions[currentQuestionIndex].id]}
285
+ className={`flex items-center gap-2 px-5 py-2 rounded-lg text-sm font-medium shadow-sm transition-all ${!answers[questions[currentQuestionIndex].id]
286
+ ? 'bg-gray-100 text-gray-400 cursor-not-allowed'
287
+ : 'bg-[#007AFF] text-white hover:bg-[#0062CC] active:bg-[#0051A8]'
288
+ }`}
289
+ >
290
+ {currentQuestionIndex === questions.length - 1 ? 'Finish' : 'Next'}
291
+ {currentQuestionIndex !== questions.length - 1 && <CaretRight size={16} weight="bold" />}
292
+ </button>
293
+ </div>
294
+ </div>
295
+
296
+ {/* Progress Indicator */}
297
+ <div className="mt-8 flex items-center justify-center gap-1.5">
298
+ {questions.map((_, idx) => (
299
+ <div
300
+ key={idx}
301
+ className={`h-1.5 rounded-full transition-all duration-300 ${idx === currentQuestionIndex
302
+ ? 'w-8 bg-[#007AFF]'
303
+ : idx < currentQuestionIndex
304
+ ? 'w-1.5 bg-gray-300'
305
+ : 'w-1.5 bg-gray-200'
306
+ }`}
307
+ />
308
+ ))}
309
+ </div>
310
+ </div>
311
+ ) : (
312
+ <div className="text-center text-gray-500">
313
+ <p>No questions available.</p>
314
+ </div>
315
+ )}
316
+ </div>
317
+ </div>
318
+ </Window>
319
+ )
320
+ }
app/components/ResearchBrowser.tsx DELETED
@@ -1,657 +0,0 @@
1
- import React, { useState, useRef, useEffect } from 'react'
2
- import Window from './Window'
3
- import { Search, Globe, Brain, Loader2, X, ChevronDown, ChevronUp, Sparkles, AlertCircle, Mic, Scan, ArrowLeft, Bot } from 'lucide-react'
4
-
5
- interface ResearchAgent {
6
- id: string
7
- name: string
8
- status: 'idle' | 'searching' | 'analyzing' | 'complete' | 'error'
9
- query: string
10
- results: string[]
11
- progress: number
12
- currentUrl?: string
13
- }
14
-
15
- interface ResearchResult {
16
- id: string
17
- title: string
18
- summary: string
19
- source: string
20
- url: string
21
- relevance: number
22
- timestamp: Date
23
- agentId: string
24
- }
25
-
26
- interface ResearchBrowserProps {
27
- onClose?: () => void
28
- onMinimize?: () => void
29
- onMaximize?: () => void
30
- }
31
-
32
- export function ResearchBrowser({ onClose, onMinimize, onMaximize }: ResearchBrowserProps) {
33
- const [prompt, setPrompt] = useState('')
34
- const [isResearching, setIsResearching] = useState(false)
35
- const [hasSearched, setHasSearched] = useState(false)
36
- const [agents, setAgents] = useState<ResearchAgent[]>([])
37
- const [results, setResults] = useState<ResearchResult[]>([])
38
- const [expandedAgents, setExpandedAgents] = useState<Set<string>>(new Set())
39
- const [selectedResult, setSelectedResult] = useState<ResearchResult | null>(null)
40
- const resultsRef = useRef<HTMLDivElement>(null)
41
-
42
- // Initialize research agents
43
- const initializeAgents = (query: string) => {
44
- const newAgents: ResearchAgent[] = [
45
- {
46
- id: 'web-search',
47
- name: 'Web Search Agent',
48
- status: 'idle',
49
- query: query,
50
- results: [],
51
- progress: 0
52
- },
53
- {
54
- id: 'academic',
55
- name: 'Academic Research Agent',
56
- status: 'idle',
57
- query: query,
58
- results: [],
59
- progress: 0
60
- },
61
- {
62
- id: 'news',
63
- name: 'News & Current Events Agent',
64
- status: 'idle',
65
- query: query,
66
- results: [],
67
- progress: 0
68
- },
69
- {
70
- id: 'deep-analysis',
71
- name: 'Deep Analysis Agent',
72
- status: 'idle',
73
- query: query,
74
- results: [],
75
- progress: 0
76
- },
77
- {
78
- id: 'fact-check',
79
- name: 'Fact Checking Agent',
80
- status: 'idle',
81
- query: query,
82
- results: [],
83
- progress: 0
84
- }
85
- ]
86
- setAgents(newAgents)
87
- return newAgents
88
- }
89
-
90
- // Simulate agent research process
91
- const runAgent = async (agent: ResearchAgent) => {
92
- // Update agent status to searching
93
- setAgents(prev => prev.map(a =>
94
- a.id === agent.id ? { ...a, status: 'searching', progress: 10 } : a
95
- ))
96
-
97
- // Simulate search phase
98
- await new Promise(resolve => setTimeout(resolve, 1000 + Math.random() * 2000))
99
-
100
- setAgents(prev => prev.map(a =>
101
- a.id === agent.id ? { ...a, status: 'analyzing', progress: 50 } : a
102
- ))
103
-
104
- // Simulate analysis phase
105
- await new Promise(resolve => setTimeout(resolve, 1500 + Math.random() * 2000))
106
-
107
- // Generate mock results based on agent type
108
- const mockResults = generateMockResults(agent)
109
-
110
- setAgents(prev => prev.map(a =>
111
- a.id === agent.id ? {
112
- ...a,
113
- status: 'complete',
114
- progress: 100,
115
- results: mockResults.map(r => r.title)
116
- } : a
117
- ))
118
-
119
- // Add results to global results
120
- setResults(prev => [...prev, ...mockResults])
121
- }
122
-
123
- // Generate mock research results
124
- const generateMockResults = (agent: ResearchAgent): ResearchResult[] => {
125
- const baseResults = {
126
- 'web-search': [
127
- {
128
- title: `Understanding ${agent.query}: A Comprehensive Overview`,
129
- summary: `Detailed analysis of ${agent.query} including recent developments, key concepts, and practical applications in various fields.`,
130
- source: 'Wikipedia',
131
- url: `https://en.wikipedia.org/wiki/${agent.query.replace(' ', '_')}`
132
- },
133
- {
134
- title: `${agent.query} - Latest Research and Insights`,
135
- summary: `Recent studies show significant advancements in ${agent.query}. Experts predict continued growth and innovation in this area.`,
136
- source: 'Research Gate',
137
- url: `https://www.researchgate.net/search/${agent.query}`
138
- }
139
- ],
140
- 'academic': [
141
- {
142
- title: `Peer-Reviewed Study on ${agent.query}`,
143
- summary: `A comprehensive academic analysis examining the theoretical foundations and empirical evidence related to ${agent.query}.`,
144
- source: 'Google Scholar',
145
- url: `https://scholar.google.com/search?q=${agent.query}`
146
- },
147
- {
148
- title: `${agent.query}: A Systematic Review`,
149
- summary: `Meta-analysis of 50+ studies on ${agent.query}, revealing key patterns and future research directions.`,
150
- source: 'PubMed',
151
- url: `https://pubmed.ncbi.nlm.nih.gov/?term=${agent.query}`
152
- }
153
- ],
154
- 'news': [
155
- {
156
- title: `Breaking: New Developments in ${agent.query}`,
157
- summary: `Recent news coverage highlights important changes and updates in the field of ${agent.query}.`,
158
- source: 'Reuters',
159
- url: `https://www.reuters.com/search/news?q=${agent.query}`
160
- },
161
- {
162
- title: `${agent.query} Industry Report 2024`,
163
- summary: `Market analysis shows growing interest in ${agent.query} with implications for various sectors.`,
164
- source: 'Bloomberg',
165
- url: `https://www.bloomberg.com/search?query=${agent.query}`
166
- }
167
- ],
168
- 'deep-analysis': [
169
- {
170
- title: `Deep Dive: The Complexity of ${agent.query}`,
171
- summary: `An in-depth exploration of ${agent.query}, examining multiple perspectives, historical context, and future implications.`,
172
- source: 'Analysis Engine',
173
- url: '#'
174
- },
175
- {
176
- title: `${agent.query}: Patterns and Connections`,
177
- summary: `AI-powered analysis reveals hidden patterns and connections in ${agent.query} across multiple domains.`,
178
- source: 'Deep Learning Analysis',
179
- url: '#'
180
- }
181
- ],
182
- 'fact-check': [
183
- {
184
- title: `Fact Check: Common Myths about ${agent.query}`,
185
- summary: `Verification of common claims and misconceptions about ${agent.query} using authoritative sources.`,
186
- source: 'FactCheck.org',
187
- url: `https://www.factcheck.org/search/${agent.query}`
188
- },
189
- {
190
- title: `Verified Information on ${agent.query}`,
191
- summary: `Cross-referenced facts and verified data about ${agent.query} from multiple trusted sources.`,
192
- source: 'Snopes',
193
- url: `https://www.snopes.com/search/?q=${agent.query}`
194
- }
195
- ]
196
- }
197
-
198
- return (baseResults[agent.id as keyof typeof baseResults] || []).map((result, index) => ({
199
- ...result,
200
- id: `${agent.id}-${index}`,
201
- relevance: 0.9 - (index * 0.1),
202
- timestamp: new Date(),
203
- agentId: agent.id
204
- }))
205
- }
206
-
207
- // Handle research submission
208
- const handleResearch = async () => {
209
- if (!prompt.trim()) return
210
-
211
- setIsResearching(true)
212
- setHasSearched(true)
213
- setResults([])
214
-
215
- // Initialize agents
216
- const newAgents = initializeAgents(prompt)
217
-
218
- try {
219
- // Call Gemini to perform "Deep Research"
220
- const response = await fetch('/api/gemini/chat', {
221
- method: 'POST',
222
- headers: { 'Content-Type': 'application/json' },
223
- body: JSON.stringify({
224
- message: `Perform a deep research on: "${prompt}".
225
- Return a JSON object with:
226
- 1. "crawledUrls": a list of 5-10 plausible URLs that were "crawled".
227
- 2. "results": a list of 5-8 research findings. Each finding has:
228
- - title
229
- - summary (2-3 sentences)
230
- - source (domain name)
231
- - url
232
- - relevance (0.8 to 1.0)
233
- - agentId (assign one of: 'web-search', 'academic', 'news', 'deep-analysis', 'fact-check')
234
- Return ONLY raw JSON, no markdown formatting.`
235
- })
236
- })
237
-
238
- const data = await response.json()
239
- let parsedData
240
-
241
- try {
242
- // Clean up markdown code blocks if present
243
- const cleanText = data.response.replace(/```json/g, '').replace(/```/g, '').trim()
244
- parsedData = JSON.parse(cleanText)
245
- } catch (e) {
246
- console.error('Failed to parse Gemini response, falling back to mocks', e)
247
- parsedData = null
248
- }
249
-
250
- if (parsedData && parsedData.results) {
251
- // Simulate crawling and processing
252
- const totalDuration = 4000 // 4 seconds total simulation
253
- const interval = totalDuration / newAgents.length
254
-
255
- // Update agents with crawling status
256
- newAgents.forEach((agent, index) => {
257
- setTimeout(() => {
258
- setAgents(prev => prev.map(a => {
259
- if (a.id === agent.id) {
260
- // Assign random crawled URLs to this agent
261
- const agentUrls = parsedData.crawledUrls
262
- ? parsedData.crawledUrls.slice(index * 2, (index * 2) + 2)
263
- : [`https://example.com/search?q=${encodeURIComponent(prompt)}`]
264
-
265
- return {
266
- ...a,
267
- status: 'searching',
268
- progress: 30,
269
- currentUrl: agentUrls[0] || 'Scanning web...'
270
- // We could store these URLs to show what's being crawled if we added a UI for it
271
- }
272
- }
273
- return a
274
- }))
275
- }, index * interval)
276
-
277
- setTimeout(() => {
278
- setAgents(prev => prev.map(a =>
279
- a.id === agent.id ? { ...a, status: 'analyzing', progress: 70, currentUrl: 'Analyzing content...' } : a
280
- ))
281
- }, (index * interval) + (interval / 2))
282
-
283
- setTimeout(() => {
284
- setAgents(prev => prev.map(a => {
285
- if (a.id === agent.id) {
286
- const agentResults = parsedData.results.filter((r: any) => r.agentId === agent.id)
287
- return {
288
- ...a,
289
- status: 'complete',
290
- progress: 100,
291
- results: agentResults.map((r: any) => r.title)
292
- }
293
- }
294
- return a
295
- }))
296
- }, totalDuration + (index * 200))
297
- })
298
-
299
- // Set final results after simulation
300
- setTimeout(() => {
301
- const formattedResults = parsedData.results.map((r: any, i: number) => ({
302
- ...r,
303
- id: `res-${i}`,
304
- timestamp: new Date(),
305
- relevance: r.relevance || 0.9
306
- }))
307
- setResults(formattedResults)
308
- setIsResearching(false)
309
- }, totalDuration + 1000)
310
-
311
- } else {
312
- // Fallback to mocks if Gemini fails or returns invalid data
313
- runMockSimulation(newAgents)
314
- }
315
-
316
- } catch (error) {
317
- console.error('Research failed', error)
318
- runMockSimulation(newAgents)
319
- }
320
- }
321
-
322
- const runMockSimulation = async (currentAgents: ResearchAgent[]) => {
323
- const agentPromises = currentAgents.map((agent, index) => {
324
- return new Promise(resolve => {
325
- setTimeout(() => {
326
- runAgent(agent).then(resolve)
327
- }, index * 500)
328
- })
329
- })
330
- await Promise.all(agentPromises)
331
- setIsResearching(false)
332
- }
333
-
334
- // Toggle agent expansion
335
- const toggleAgentExpansion = (agentId: string) => {
336
- setExpandedAgents(prev => {
337
- const newSet = new Set(prev)
338
- if (newSet.has(agentId)) {
339
- newSet.delete(agentId)
340
- } else {
341
- newSet.add(agentId)
342
- }
343
- return newSet
344
- })
345
- }
346
-
347
- // Get agent status icon
348
- const getAgentStatusIcon = (status: ResearchAgent['status']) => {
349
- switch (status) {
350
- case 'searching':
351
- return <Loader2 className="animate-spin" size={16} />
352
- case 'analyzing':
353
- return <Brain className="animate-pulse" size={16} />
354
- case 'complete':
355
- return <Sparkles className="text-green-400" size={16} />
356
- case 'error':
357
- return <AlertCircle className="text-red-400" size={16} />
358
- default:
359
- return <Globe size={16} />
360
- }
361
- }
362
-
363
- const resetSearch = () => {
364
- setHasSearched(false)
365
- setPrompt('')
366
- setResults([])
367
- setAgents([])
368
- }
369
-
370
- return (
371
- <Window
372
- id="research-browser"
373
- title="AI Research Browser"
374
- isOpen={true}
375
- onClose={onClose || (() => { })}
376
- onMinimize={onMinimize}
377
- onMaximize={onMaximize}
378
- width={1200}
379
- height={800}
380
- x={100}
381
- y={100}
382
- darkMode={true}
383
- className="research-browser-window"
384
- contentClassName="bg-[#050505] text-white"
385
- >
386
- <div className="flex flex-col h-full relative overflow-hidden">
387
- {/* Background Effects */}
388
- <div className="absolute top-0 left-0 w-full h-full overflow-hidden pointer-events-none">
389
- <div className="absolute top-[-20%] left-[20%] w-[600px] h-[600px] bg-blue-600/10 rounded-full blur-[120px]" />
390
- <div className="absolute bottom-[-10%] right-[-10%] w-[500px] h-[500px] bg-purple-600/10 rounded-full blur-[100px]" />
391
- </div>
392
-
393
- {!hasSearched ? (
394
- // Initial Home View
395
- <div className="flex-1 flex flex-col items-center justify-center p-8 relative z-10 animate-in fade-in duration-700">
396
- {/* Logo */}
397
- <div className="mb-8 relative group">
398
- <div className="absolute inset-0 bg-blue-500/30 rounded-2xl blur-xl group-hover:blur-2xl transition-all duration-500" />
399
- <div className="relative bg-gradient-to-br from-blue-500 to-cyan-400 p-4 rounded-2xl shadow-lg shadow-blue-500/20 border border-white/10">
400
- <Bot size={48} className="text-white" />
401
- </div>
402
- </div>
403
-
404
- {/* Greeting */}
405
- <h1 className="text-5xl font-bold text-white mb-12 tracking-tight">Welcome to DeepResearch</h1>
406
-
407
- {/* Search Bar */}
408
- <div className="w-full max-w-2xl relative group">
409
- <div className="absolute inset-0 bg-gradient-to-r from-blue-500/20 to-purple-500/20 rounded-full blur-md group-hover:blur-lg transition-all duration-500 opacity-0 group-hover:opacity-100" />
410
- <div className="relative bg-[#1a1a1a]/80 backdrop-blur-xl border border-white/10 rounded-full p-2 flex items-center shadow-2xl">
411
- <div className="pl-4 pr-2">
412
- <div className="w-6 h-6 border-l-2 border-blue-500/50 h-4 mx-2" />
413
- </div>
414
- <input
415
- type="text"
416
- value={prompt}
417
- onChange={(e) => setPrompt(e.target.value)}
418
- onKeyPress={(e) => e.key === 'Enter' && handleResearch()}
419
- placeholder="Search now"
420
- className="flex-1 bg-transparent border-none outline-none text-white placeholder-gray-500 text-lg px-2 h-12"
421
- autoFocus
422
- />
423
- <div className="flex items-center gap-2 pr-2">
424
- <button className="p-2 text-gray-400 hover:text-white transition-colors rounded-full hover:bg-white/5">
425
- <Scan size={20} />
426
- </button>
427
- <button
428
- onClick={handleResearch}
429
- className="p-3 bg-white text-black rounded-full hover:bg-gray-200 transition-colors"
430
- >
431
- {isResearching ? <Loader2 size={20} className="animate-spin" /> : <Mic size={20} />}
432
- </button>
433
- </div>
434
- </div>
435
- </div>
436
- </div>
437
- ) : (
438
- // Results View
439
- <div className="flex flex-col h-full relative z-10">
440
- {/* Top Bar */}
441
- <div className="flex items-center gap-4 p-4 border-b border-white/10 bg-[#0a0a0a]/50 backdrop-blur-md">
442
- <button
443
- onClick={resetSearch}
444
- className="p-2 hover:bg-white/10 rounded-lg transition-colors"
445
- >
446
- <ArrowLeft size={20} className="text-gray-400" />
447
- </button>
448
- <div className="flex-1 relative">
449
- <Search size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500" />
450
- <input
451
- type="text"
452
- value={prompt}
453
- onChange={(e) => setPrompt(e.target.value)}
454
- onKeyPress={(e) => e.key === 'Enter' && !isResearching && handleResearch()}
455
- className="w-full bg-[#1a1a1a] border border-white/10 rounded-lg pl-10 pr-4 py-2 text-sm text-white focus:outline-none focus:border-blue-500/50 transition-colors"
456
- />
457
- </div>
458
- </div>
459
-
460
- <div className="flex-1 flex overflow-hidden">
461
- {/* Left Panel - Agents */}
462
- <div className="w-80 border-r border-white/10 bg-[#0a0a0a]/30 overflow-y-auto p-4">
463
- <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-4">Research Agents</h3>
464
- <div className="space-y-3">
465
- {agents.map(agent => (
466
- <div
467
- key={agent.id}
468
- className="bg-[#1a1a1a] rounded-lg border border-white/5 overflow-hidden"
469
- >
470
- <button
471
- onClick={() => toggleAgentExpansion(agent.id)}
472
- className="w-full px-3 py-3 flex items-center justify-between hover:bg-white/5 transition-colors"
473
- >
474
- <div className="flex items-center gap-3">
475
- <div className={`p-1.5 rounded-md ${agent.status === 'complete' ? 'bg-green-500/10 text-green-400' :
476
- agent.status === 'error' ? 'bg-red-500/10 text-red-400' :
477
- agent.status === 'searching' || agent.status === 'analyzing' ? 'bg-blue-500/10 text-blue-400' :
478
- 'bg-gray-500/10 text-gray-400'
479
- }`}>
480
- {getAgentStatusIcon(agent.status)}
481
- </div>
482
- <span className="text-sm font-medium text-gray-200">{agent.name}</span>
483
- </div>
484
- {expandedAgents.has(agent.id) ? <ChevronUp size={14} className="text-gray-500" /> : <ChevronDown size={14} className="text-gray-500" />}
485
- </button>
486
-
487
- {expandedAgents.has(agent.id) && (
488
- <div className="px-3 py-3 border-t border-white/5 bg-black/20">
489
- <div className="text-xs text-gray-600 mb-2">
490
- <div className="flex justify-between items-center mb-1">
491
- <span>Status: <span className="font-medium capitalize text-gray-300">{agent.status}</span></span>
492
- </div>
493
- {agent.currentUrl && (agent.status === 'searching' || agent.status === 'analyzing') && (
494
- <div className="text-blue-400 truncate max-w-full mb-2 font-mono text-[10px]">
495
- Crawling: {agent.currentUrl}
496
- </div>
497
- )}
498
- </div>
499
- {agent.progress > 0 && agent.progress < 100 && (
500
- <div className="mb-2">
501
- <div className="w-full bg-gray-800 rounded-full h-1">
502
- <div
503
- className="bg-blue-500 h-1 rounded-full transition-all duration-500"
504
- style={{ width: `${agent.progress}%` }}
505
- />
506
- </div>
507
- </div>
508
- )}
509
- {agent.results.length > 0 && (
510
- <div className="text-xs text-gray-500">
511
- Found <span className="text-white">{agent.results.length}</span> results
512
- </div>
513
- )}
514
- </div>
515
- )}
516
- </div>
517
- ))}
518
- </div>
519
- </div>
520
-
521
- {/* Right Panel - Results */}
522
- <div className="flex-1 overflow-y-auto p-6" ref={resultsRef}>
523
- <h3 className="text-lg font-semibold text-white mb-6 flex items-center gap-2">
524
- <Sparkles size={20} className="text-blue-400" />
525
- Research Results
526
- </h3>
527
-
528
- <div className="space-y-4">
529
- {results
530
- .sort((a, b) => b.relevance - a.relevance)
531
- .map(result => (
532
- <div
533
- key={result.id}
534
- className="group bg-[#1a1a1a] border border-white/5 rounded-xl p-5 hover:border-blue-500/30 hover:bg-[#202020] transition-all cursor-pointer"
535
- onClick={() => setSelectedResult(result)}
536
- >
537
- <div className="flex items-start justify-between mb-3">
538
- <h4 className="text-lg font-medium text-blue-400 group-hover:text-blue-300 transition-colors flex-1">
539
- {result.title}
540
- </h4>
541
- <span className="text-xs bg-blue-500/10 text-blue-400 px-2 py-1 rounded ml-4 border border-blue-500/20">
542
- {Math.round(result.relevance * 100)}% match
543
- </span>
544
- </div>
545
-
546
- <p className="text-sm text-gray-400 mb-4 line-clamp-2 leading-relaxed">
547
- {result.summary}
548
- </p>
549
-
550
- <div className="flex items-center justify-between text-xs text-gray-500">
551
- <div className="flex items-center gap-4">
552
- <span className="flex items-center gap-1.5">
553
- <Globe size={12} />
554
- {result.source}
555
- </span>
556
- <span className="flex items-center gap-1.5">
557
- <Bot size={12} />
558
- {agents.find(a => a.id === result.agentId)?.name}
559
- </span>
560
- </div>
561
- {result.url !== '#' && (
562
- <span className="text-blue-500/50 group-hover:text-blue-400 flex items-center gap-1 transition-colors">
563
- View Source <ArrowLeft size={12} className="rotate-180" />
564
- </span>
565
- )}
566
- </div>
567
- </div>
568
- ))}
569
- </div>
570
- </div>
571
- </div>
572
- </div>
573
- )}
574
-
575
- {/* Selected Result Modal */}
576
- {selectedResult && (
577
- <div
578
- className="absolute inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center z-50 p-8"
579
- onClick={() => setSelectedResult(null)}
580
- >
581
- <div
582
- className="bg-[#1a1a1a] border border-white/10 rounded-2xl w-full max-w-3xl max-h-full overflow-hidden flex flex-col shadow-2xl"
583
- onClick={(e) => e.stopPropagation()}
584
- >
585
- <div className="p-6 border-b border-white/10 flex items-center justify-between bg-[#202020]">
586
- <h3 className="text-xl font-semibold text-white pr-8">{selectedResult.title}</h3>
587
- <button
588
- onClick={() => setSelectedResult(null)}
589
- className="p-2 hover:bg-white/10 rounded-lg text-gray-400 hover:text-white transition-colors"
590
- >
591
- <X size={20} />
592
- </button>
593
- </div>
594
-
595
- <div className="p-8 overflow-y-auto">
596
- <div className="flex items-center gap-4 mb-6 text-sm text-gray-500">
597
- <span className="flex items-center gap-2 px-3 py-1.5 bg-white/5 rounded-full">
598
- <Globe size={14} />
599
- {selectedResult.source}
600
- </span>
601
- <span className="flex items-center gap-2 px-3 py-1.5 bg-blue-500/10 text-blue-400 rounded-full border border-blue-500/20">
602
- <Sparkles size={14} />
603
- {Math.round(selectedResult.relevance * 100)}% relevance
604
- </span>
605
- </div>
606
-
607
- <div className="prose prose-invert max-w-none">
608
- <p className="text-gray-300 text-lg leading-relaxed">{selectedResult.summary}</p>
609
-
610
- <div className="mt-8 p-6 bg-black/30 rounded-xl border border-white/5">
611
- <h4 className="font-semibold text-white mb-4 flex items-center gap-2">
612
- <Scan size={18} className="text-purple-400" />
613
- Research Details
614
- </h4>
615
- <ul className="space-y-3 text-sm text-gray-400">
616
- <li className="flex items-center gap-2">
617
- <span className="w-1.5 h-1.5 bg-gray-600 rounded-full" />
618
- Agent: <span className="text-gray-300">{agents.find(a => a.id === selectedResult.agentId)?.name}</span>
619
- </li>
620
- <li className="flex items-center gap-2">
621
- <span className="w-1.5 h-1.5 bg-gray-600 rounded-full" />
622
- Timestamp: <span className="text-gray-300">{selectedResult.timestamp.toLocaleString()}</span>
623
- </li>
624
- <li className="flex items-center gap-2">
625
- <span className="w-1.5 h-1.5 bg-gray-600 rounded-full" />
626
- Source URL:
627
- {selectedResult.url !== '#' ? (
628
- <a href={selectedResult.url} target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:text-blue-300 ml-1">
629
- {selectedResult.url}
630
- </a>
631
- ) : <span className="text-gray-300 ml-1">Internal Analysis</span>}
632
- </li>
633
- </ul>
634
- </div>
635
-
636
- {selectedResult.url !== '#' && (
637
- <div className="mt-8 flex justify-end">
638
- <a
639
- href={selectedResult.url}
640
- target="_blank"
641
- rel="noopener noreferrer"
642
- className="inline-flex items-center gap-2 px-6 py-3 bg-blue-600 text-white rounded-xl hover:bg-blue-500 transition-all shadow-lg shadow-blue-600/20 font-medium"
643
- >
644
- View Full Source
645
- <Globe size={18} />
646
- </a>
647
- </div>
648
- )}
649
- </div>
650
- </div>
651
- </div>
652
- </div>
653
- )}
654
- </div>
655
- </Window>
656
- )
657
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/components/SpotlightSearch.tsx CHANGED
@@ -10,7 +10,8 @@ import {
10
  Sparkle,
11
  Globe,
12
  DeviceMobile,
13
- Function as FunctionIcon
 
14
  } from '@phosphor-icons/react'
15
  import { motion, AnimatePresence } from 'framer-motion'
16
 
@@ -37,9 +38,10 @@ export function SpotlightSearch({ isOpen, onClose, onOpenApp }: SpotlightSearchP
37
  { id: 'calendar', name: 'Calendar', type: 'app', icon: <div className="w-6 h-6 bg-white rounded-md flex items-center justify-center border border-gray-200"><Calendar size={16} weight="regular" className="text-red-500" /></div> },
38
  { id: 'clock', name: 'Clock', type: 'app', icon: <div className="w-6 h-6 bg-white rounded-full flex items-center justify-center border border-gray-200"><Clock size={16} weight="regular" className="text-black" /></div> },
39
  { id: 'gemini', name: 'Gemini Chat', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-b from-white to-blue-50 rounded-md flex items-center justify-center border border-white/50"><Sparkle size={16} weight="fill" className="text-blue-500" /></div> },
40
- { id: 'research', name: 'Research', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-b from-purple-500 to-indigo-600 rounded-md flex items-center justify-center border border-white/20"><Globe size={16} weight="duotone" className="text-white" /></div> },
41
  { id: 'flutter-editor', name: 'Flutter IDE', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-b from-[#54C5F8] to-[#29B6F6] rounded-md flex items-center justify-center border border-white/20"><DeviceMobile size={16} weight="fill" className="text-white" /></div> },
42
  { id: 'latex-editor', name: 'LaTeX Studio', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-b from-slate-700 to-slate-900 rounded-md flex items-center justify-center border border-white/20"><FunctionIcon size={16} weight="bold" className="text-green-400" /></div> },
 
43
  ]
44
 
45
  const files: SearchResult[] = [
@@ -102,7 +104,7 @@ export function SpotlightSearch({ isOpen, onClose, onOpenApp }: SpotlightSearchP
102
  initial={{ opacity: 0 }}
103
  animate={{ opacity: 1 }}
104
  exit={{ opacity: 0 }}
105
- className="fixed inset-0 bg-black/20 z-[60]"
106
  onClick={onClose}
107
  />
108
 
@@ -112,7 +114,7 @@ export function SpotlightSearch({ isOpen, onClose, onOpenApp }: SpotlightSearchP
112
  animate={{ opacity: 1, scale: 1, y: 0 }}
113
  exit={{ opacity: 0, scale: 0.95, y: -20 }}
114
  transition={{ duration: 0.15, ease: 'easeOut' }}
115
- className="fixed top-[20%] left-1/2 -translate-x-1/2 w-[600px] max-w-[90%] glass rounded-xl shadow-2xl z-[70]"
116
  >
117
  <div className="flex items-center px-4 py-3 gap-3 border-b border-gray-200/20">
118
  <MagnifyingGlass size={24} weight="regular" className="text-gray-600" />
 
10
  Sparkle,
11
  Globe,
12
  DeviceMobile,
13
+ Function as FunctionIcon,
14
+ Brain
15
  } from '@phosphor-icons/react'
16
  import { motion, AnimatePresence } from 'framer-motion'
17
 
 
38
  { id: 'calendar', name: 'Calendar', type: 'app', icon: <div className="w-6 h-6 bg-white rounded-md flex items-center justify-center border border-gray-200"><Calendar size={16} weight="regular" className="text-red-500" /></div> },
39
  { id: 'clock', name: 'Clock', type: 'app', icon: <div className="w-6 h-6 bg-white rounded-full flex items-center justify-center border border-gray-200"><Clock size={16} weight="regular" className="text-black" /></div> },
40
  { id: 'gemini', name: 'Gemini Chat', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-b from-white to-blue-50 rounded-md flex items-center justify-center border border-white/50"><Sparkle size={16} weight="fill" className="text-blue-500" /></div> },
41
+
42
  { id: 'flutter-editor', name: 'Flutter IDE', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-b from-[#54C5F8] to-[#29B6F6] rounded-md flex items-center justify-center border border-white/20"><DeviceMobile size={16} weight="fill" className="text-white" /></div> },
43
  { id: 'latex-editor', name: 'LaTeX Studio', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-b from-slate-700 to-slate-900 rounded-md flex items-center justify-center border border-white/20"><FunctionIcon size={16} weight="bold" className="text-green-400" /></div> },
44
+ { id: 'quiz', name: 'Quiz Master', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-br from-teal-400 to-emerald-500 rounded-md flex items-center justify-center border border-white/20"><Brain size={16} weight="fill" className="text-white" /></div> },
45
  ]
46
 
47
  const files: SearchResult[] = [
 
104
  initial={{ opacity: 0 }}
105
  animate={{ opacity: 1 }}
106
  exit={{ opacity: 0 }}
107
+ className="fixed inset-0 bg-black/20 z-[9998]"
108
  onClick={onClose}
109
  />
110
 
 
114
  animate={{ opacity: 1, scale: 1, y: 0 }}
115
  exit={{ opacity: 0, scale: 0.95, y: -20 }}
116
  transition={{ duration: 0.15, ease: 'easeOut' }}
117
+ className="fixed top-[20%] left-1/2 -translate-x-1/2 w-[600px] max-w-[90%] glass rounded-xl shadow-2xl z-[9999]"
118
  >
119
  <div className="flex items-center px-4 py-3 gap-3 border-b border-gray-200/20">
120
  <MagnifyingGlass size={24} weight="regular" className="text-gray-600" />