Reuben_OS / app /components /Desktop.tsx
Reubencf's picture
Fix Desktop to not interfere with FlutterRunner file loading
4f10612
'use client'
import React, { useState, useEffect } from 'react'
import { Dock } from './Dock'
import { TopBar } from './TopBar'
import { FileManager } from './FileManager'
import { Calendar } from './Calendar'
import { DraggableDesktopIcon } from './DraggableDesktopIcon'
import { Messages } from './Messages'
import { HelpModal } from './HelpModal'
import { DesktopContextMenu } from './DesktopContextMenu'
import { BackgroundSelector } from './BackgroundSelector'
import { GeminiChat } from './GeminiChat'
import { Clock } from './Clock'
import { SpotlightSearch } from './SpotlightSearch'
import { ContextMenu } from './ContextMenu'
import { AboutModal } from './AboutModal'
import { FlutterRunner } from './FlutterRunner'
import { QuizApp } from './QuizApp'
import { TextEditor } from './TextEditor'
import { VoiceApp } from './VoiceApp'
import { motion, AnimatePresence } from 'framer-motion'
import { SystemPowerOverlay } from './SystemPowerOverlay'
import {
Folder,
Calendar as CalendarIcon,
Clock as ClockIcon,
Globe,
Sparkle,
Terminal as TerminalIcon,
Key,
Brain,
Code,
FileText,
DeviceMobile,
Lightning,
Function,
ChatCircleDots,
MusicNote
} from '@phosphor-icons/react'
export function Desktop() {
const [fileManagerOpen, setFileManagerOpen] = useState(false)
const [calendarOpen, setCalendarOpen] = useState(false)
const [clockOpen, setClockOpen] = useState(false)
const [messagesOpen, setMessagesOpen] = useState(false)
const [geminiChatOpen, setGeminiChatOpen] = useState(false)
const [spotlightOpen, setSpotlightOpen] = useState(false)
const [contextMenuVisible, setContextMenuVisible] = useState(false)
const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 })
const [currentPath, setCurrentPath] = useState('')
const [helpModalOpen, setHelpModalOpen] = useState(false)
const [contextMenuOpen, setContextMenuOpen] = useState(false)
const [contextMenuPos, setContextMenuPos] = useState({ x: 0, y: 0 })
const [backgroundSelectorOpen, setBackgroundSelectorOpen] = useState(false)
const [currentBackground, setCurrentBackground] = useState('https://images.unsplash.com/photo-1545159639-3f3534aa074e')
const [aboutModalOpen, setAboutModalOpen] = useState(false)
const [flutterRunnerOpen, setFlutterRunnerOpen] = useState(false)
const [activeFlutterApp, setActiveFlutterApp] = useState<any>(null)
const [flutterCodeEditorOpen, setFlutterCodeEditorOpen] = useState(false)
const [quizAppOpen, setQuizAppOpen] = useState(false)
const [textEditorOpen, setTextEditorOpen] = useState(false)
const [activeTextFile, setActiveTextFile] = useState<{ content: string, fileName: string, filePath: string, passkey: string } | null>(null)
const [voiceAppOpen, setVoiceAppOpen] = useState(false)
// Minimized states
const [fileManagerMinimized, setFileManagerMinimized] = useState(false)
const [calendarMinimized, setCalendarMinimized] = useState(false)
const [clockMinimized, setClockMinimized] = useState(false)
const [messagesMinimized, setMessagesMinimized] = useState(false)
const [geminiChatMinimized, setGeminiChatMinimized] = useState(false)
const [flutterRunnerMinimized, setFlutterRunnerMinimized] = useState(false)
const [flutterCodeEditorMinimized, setFlutterCodeEditorMinimized] = useState(false)
const [quizAppMinimized, setQuizAppMinimized] = useState(false)
const [textEditorMinimized, setTextEditorMinimized] = useState(false)
const [voiceAppMinimized, setVoiceAppMinimized] = useState(false)
const [powerState, setPowerState] = useState<'active' | 'sleep' | 'restart' | 'shutdown'>('active')
const [globalZIndex, setGlobalZIndex] = useState(1000)
const [windowZIndices, setWindowZIndices] = useState<{ [key: string]: number }>({})
const getNextZIndex = () => {
const nextZ = globalZIndex + 1
setGlobalZIndex(nextZ)
return nextZ
}
const bringWindowToFront = (windowId: string) => {
setWindowZIndices(prev => ({ ...prev, [windowId]: getNextZIndex() }))
}
const closeAllApps = () => {
// Close all apps
setFileManagerOpen(false)
setCalendarOpen(false)
setClockOpen(false)
setMessagesOpen(false)
setGeminiChatOpen(false)
setFlutterRunnerOpen(false)
setFlutterCodeEditorOpen(false)
setQuizAppOpen(false)
setTextEditorOpen(false)
setVoiceAppOpen(false)
// Reset all minimized states
setFileManagerMinimized(false)
setCalendarMinimized(false)
setClockMinimized(false)
setMessagesMinimized(false)
setGeminiChatMinimized(false)
setFlutterRunnerMinimized(false)
setFlutterCodeEditorMinimized(false)
setQuizAppMinimized(false)
setTextEditorMinimized(false)
setVoiceAppMinimized(false)
// Reset window z-indices
setWindowZIndices({})
setGlobalZIndex(1000)
}
const openFileManager = (path: string) => {
console.log('Opening File Manager with path:', path)
setCurrentPath(path)
setFileManagerOpen(true)
setFileManagerMinimized(false)
bringWindowToFront('fileManager')
}
const closeFileManager = () => {
setFileManagerOpen(false)
setFileManagerMinimized(false)
}
const openCalendar = () => {
console.log('Opening Calendar')
setCalendarOpen(true)
setCalendarMinimized(false)
bringWindowToFront('calendar')
}
const closeCalendar = () => {
setCalendarOpen(false)
setCalendarMinimized(false)
}
const openClock = () => {
setClockOpen(true)
setClockMinimized(false)
bringWindowToFront('clock')
}
const closeClock = () => {
setClockOpen(false)
setClockMinimized(false)
}
const openMessages = () => {
setMessagesOpen(true)
setMessagesMinimized(false)
bringWindowToFront('messages')
}
const closeMessages = () => {
setMessagesOpen(false)
setMessagesMinimized(false)
}
const openGeminiChat = () => {
console.log('Opening Gemini Chat')
setGeminiChatOpen(true)
setGeminiChatMinimized(false)
bringWindowToFront('gemini')
}
const closeGeminiChat = () => {
setGeminiChatOpen(false)
setGeminiChatMinimized(false)
}
const openFlutterRunner = (appFile: any) => {
setActiveFlutterApp(appFile)
setFlutterRunnerOpen(true)
setFlutterRunnerMinimized(false)
bringWindowToFront('flutterRunner')
}
const closeFlutterRunner = () => {
setFlutterRunnerOpen(false)
setFlutterRunnerMinimized(false)
setActiveFlutterApp(null)
}
const openFlutterCodeEditor = () => {
// Don't read/remove sessionStorage here - let FlutterRunner handle it
// This allows the polling mechanism in FlutterRunner to detect new files
setFlutterCodeEditorOpen(true)
setFlutterCodeEditorMinimized(false)
bringWindowToFront('flutterCodeEditor')
}
const closeFlutterCodeEditor = () => {
setFlutterCodeEditorOpen(false)
setFlutterCodeEditorMinimized(false)
}
const openQuizApp = () => {
setQuizAppOpen(true)
setQuizAppMinimized(false)
bringWindowToFront('quizApp')
}
const closeQuizApp = () => {
setQuizAppOpen(false)
setQuizAppMinimized(false)
}
const openTextEditor = (fileData: { content: string, fileName: string, filePath: string, passkey: string }) => {
setActiveTextFile(fileData)
setTextEditorOpen(true)
setTextEditorMinimized(false)
bringWindowToFront('textEditor')
}
const closeTextEditor = () => {
setTextEditorOpen(false)
setTextEditorMinimized(false)
setActiveTextFile(null)
}
const openVoiceApp = () => {
setVoiceAppOpen(true)
setVoiceAppMinimized(false)
bringWindowToFront('voiceApp')
}
const closeVoiceApp = () => {
setVoiceAppOpen(false)
setVoiceAppMinimized(false)
}
const handleOpenApp = (appId: string) => {
switch (appId) {
case 'files':
openFileManager('')
break
case 'calendar':
openCalendar()
break
case 'clock':
openClock()
break
case 'messages':
openMessages()
break
case 'gemini':
openGeminiChat()
break
case 'flutter-editor':
openFlutterCodeEditor()
break
case 'quiz':
openQuizApp()
break
case 'voice-app':
openVoiceApp()
break
}
}
const handleContextMenuAction = (action: string) => {
switch (action) {
case 'change-wallpaper':
setBackgroundSelectorOpen(true)
break
case 'get-info':
setAboutModalOpen(true)
break
case 'refresh':
window.location.reload()
break
}
}
const openHelpModal = () => {
setHelpModalOpen(true)
}
const closeHelpModal = () => {
setHelpModalOpen(false)
}
const handleDesktopRightClick = (e: React.MouseEvent) => {
e.preventDefault()
setContextMenuPosition({ x: e.clientX, y: e.clientY })
setContextMenuVisible(true)
}
const handleChangeBackground = (type: 'upload' | 'preset') => {
setContextMenuOpen(false)
setBackgroundSelectorOpen(true)
}
const handleSelectBackground = (background: string | File) => {
if (typeof background === 'string') {
// Handle preset backgrounds
if (background.startsWith('gradient-')) {
setCurrentBackground(background)
} else {
setCurrentBackground(background)
}
} else {
// Handle uploaded file
const url = URL.createObjectURL(background)
setCurrentBackground(url)
}
}
const handleSleep = () => setPowerState('sleep')
const handleRestart = () => setPowerState('restart')
const handleShutdown = () => setPowerState('shutdown')
const handleWake = () => setPowerState('active')
// Keyboard shortcuts
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
// Cmd/Ctrl + Space for Spotlight Search
if ((e.metaKey || e.ctrlKey) && e.key === ' ') {
e.preventDefault()
setSpotlightOpen(true)
}
// Cmd/Ctrl + K for Spotlight Search (alternative)
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault()
setSpotlightOpen(true)
}
}
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [])
const getBackgroundStyle = () => {
if (currentBackground.startsWith('gradient-')) {
const gradients: Record<string, string> = {
'gradient-sonoma': 'linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%)',
'gradient-purple': 'linear-gradient(135deg, #77216F 0%, #5E2750 50%, #2C001E 100%)',
'gradient-blue': 'linear-gradient(135deg, #1e3c72 0%, #2a5298 50%, #7e8ba3 100%)',
'gradient-green': 'linear-gradient(135deg, #134e5e 0%, #71b280 50%, #a8e063 100%)',
'gradient-orange': 'linear-gradient(135deg, #ff512f 0%, #dd2476 50%, #f09819 100%)',
'gradient-dark': 'linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 50%, #2d2d2d 100%)',
'gradient-cosmic': 'linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%)'
}
return { background: gradients[currentBackground] || gradients['gradient-sonoma'] }
} else {
return {
backgroundImage: `url('${currentBackground}')`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat'
}
}
}
// Build minimized apps list for dock
const minimizedApps = []
if (fileManagerMinimized && fileManagerOpen) {
minimizedApps.push({
id: 'files',
label: 'Files',
icon: (
<div className="bg-gradient-to-br from-blue-400 to-cyan-200 w-full h-full rounded-xl flex items-center justify-center border border-white/30">
<Folder size={20} weight="regular" className="text-blue-900" />
</div>
),
onRestore: () => {
setFileManagerMinimized(false)
bringWindowToFront('fileManager')
}
})
}
if (calendarMinimized && calendarOpen) {
minimizedApps.push({
id: 'calendar',
label: 'Calendar',
icon: (
<div className="bg-gradient-to-br from-purple-500 to-purple-600 w-full h-full rounded-xl flex items-center justify-center shadow-inner">
<CalendarIcon size={20} weight="regular" className="text-white" />
</div>
),
onRestore: () => {
setCalendarMinimized(false)
bringWindowToFront('calendar')
}
})
}
if (clockMinimized && clockOpen) {
minimizedApps.push({
id: 'clock',
label: 'Clock',
icon: (
<div className="bg-black w-full h-full rounded-full flex items-center justify-center border border-gray-600">
<ClockIcon size={20} weight="regular" className="text-white" />
</div>
),
onRestore: () => {
setClockMinimized(false)
bringWindowToFront('clock')
}
})
}
if (messagesMinimized && messagesOpen) {
minimizedApps.push({
id: 'messages',
label: 'Messages',
icon: (
<div className="bg-gradient-to-b from-[#4CD964] to-[#2E8B57] w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
<ChatCircleDots size={20} weight="fill" className="text-white relative z-10 drop-shadow-md" />
</div>
),
onRestore: () => {
setMessagesMinimized(false)
bringWindowToFront('messages')
}
})
}
if (geminiChatMinimized && geminiChatOpen) {
minimizedApps.push({
id: 'gemini',
label: 'Gemini',
icon: (
<div className="bg-white w-full h-full rounded-xl flex items-center justify-center border border-gray-200">
<Sparkle size={20} weight="fill" className="text-blue-500" />
</div>
),
onRestore: () => {
setGeminiChatMinimized(false)
bringWindowToFront('gemini')
}
})
}
if (flutterRunnerMinimized && flutterRunnerOpen) {
minimizedApps.push({
id: 'flutter-runner',
label: 'Flutter Runner',
icon: (
<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">
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
<DeviceMobile size={20} weight="fill" className="text-white relative z-10 drop-shadow-md" />
<div className="absolute top-1 right-1">
<Lightning size={8} weight="fill" className="text-yellow-300 drop-shadow-md" />
</div>
</div>
),
onRestore: () => {
setFlutterRunnerMinimized(false)
bringWindowToFront('flutterRunner')
}
})
}
if (flutterCodeEditorMinimized && flutterCodeEditorOpen) {
minimizedApps.push({
id: 'flutter-editor',
label: 'Flutter IDE',
icon: (
<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">
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
<DeviceMobile size={20} weight="fill" className="text-white relative z-10 drop-shadow-md" />
<div className="absolute top-1 right-1">
<Lightning size={8} weight="fill" className="text-yellow-300 drop-shadow-md" />
</div>
</div>
),
onRestore: () => {
setFlutterCodeEditorMinimized(false)
bringWindowToFront('flutterCodeEditor')
}
})
}
if (quizAppMinimized && quizAppOpen) {
minimizedApps.push({
id: 'quiz',
label: 'Quiz Master',
icon: (
<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">
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
<Brain size={20} weight="fill" className="text-white relative z-10 drop-shadow-md" />
</div>
),
onRestore: () => {
setQuizAppMinimized(false)
bringWindowToFront('quizApp')
}
})
}
if (textEditorMinimized && textEditorOpen) {
minimizedApps.push({
id: 'text-editor',
label: activeTextFile?.fileName || 'Text Editor',
icon: (
<div className="bg-gradient-to-br from-gray-700 to-gray-900 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-b from-white/10 to-transparent opacity-30" />
<FileText size={20} weight="fill" className="text-blue-400 relative z-10 drop-shadow-md" />
</div>
),
onRestore: () => {
setTextEditorMinimized(false)
bringWindowToFront('textEditor')
}
})
}
if (voiceAppMinimized && voiceAppOpen) {
minimizedApps.push({
id: 'voice-app',
label: 'Voice Studio',
icon: (
<div className="bg-gradient-to-br from-purple-500 to-pink-500 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
<MusicNote size={20} weight="fill" className="text-white relative z-10 drop-shadow-md" />
</div>
),
onRestore: () => {
setVoiceAppMinimized(false)
bringWindowToFront('voiceApp')
}
})
}
// Debug info
useEffect(() => {
console.log('App States:', {
fileManagerOpen,
calendarOpen,
clockOpen,
messagesOpen,
geminiChatOpen,
fileManagerMinimized,
calendarMinimized,
clockMinimized,
messagesMinimized,
geminiChatMinimized
})
}, [fileManagerOpen, calendarOpen, clockOpen, messagesOpen, geminiChatOpen, fileManagerMinimized, calendarMinimized, clockMinimized, messagesMinimized, geminiChatMinimized])
return (
<div className="relative h-screen w-screen overflow-hidden flex touch-auto" onContextMenu={handleDesktopRightClick}>
<div
className="absolute inset-0 transition-all duration-500 ease-in-out"
style={getBackgroundStyle()}
>
{/* Overlay for better text visibility */}
{!currentBackground.startsWith('gradient-') && (
<div
className="absolute inset-0 bg-black/20"
style={{
mixBlendMode: 'multiply'
}}
/>
)}
<svg className="absolute inset-0 w-full h-full opacity-10 pointer-events-none" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="ubuntu-pattern" x="0" y="0" width="100" height="100" patternUnits="userSpaceOnUse">
<circle cx="50" cy="50" r="2" fill="white" opacity="0.3" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#ubuntu-pattern)" />
</svg>
</div>
<TopBar
onAboutClick={() => setAboutModalOpen(true)}
onSleep={handleSleep}
onRestart={handleRestart}
onShutdown={handleShutdown}
/>
<Dock
onOpenFileManager={openFileManager}
onOpenCalendar={openCalendar}
onOpenClock={openClock}
onOpenMessages={openMessages}
onOpenGeminiChat={openGeminiChat}
onCloseAllApps={closeAllApps}
openApps={{
files: fileManagerOpen,
calendar: calendarOpen,
clock: clockOpen,
messages: messagesOpen,
gemini: geminiChatOpen
}}
minimizedApps={minimizedApps}
/>
<div className="flex-1 z-10 relative pointer-events-auto">
{/* Desktop Icons - Responsive grid layout, visible on all screen sizes */}
<div className="absolute top-16 left-2 right-2 sm:left-4 sm:right-4 bottom-24 grid grid-cols-3 xs:grid-cols-4 sm:grid-cols-4 md:grid-cols-5 lg:grid-flow-col lg:grid-cols-none lg:grid-rows-[repeat(auto-fill,100px)] gap-2 sm:gap-4 pointer-events-none overflow-y-auto lg:overflow-visible z-0">
<div className="pointer-events-auto w-20 h-20 sm:w-24 sm:h-24">
<DraggableDesktopIcon
id="files"
label="Files"
iconType="files"
initialPosition={{ x: 0, y: 0 }}
onClick={() => { }}
onDoubleClick={() => openFileManager('')}
/>
</div>
<div className="pointer-events-auto w-20 h-20 sm:w-24 sm:h-24">
<DraggableDesktopIcon
id="messages"
label="Messages"
iconType="messages"
initialPosition={{ x: 0, y: 0 }}
onClick={() => { }}
onDoubleClick={openMessages}
/>
</div>
<div className="pointer-events-auto w-20 h-20 sm:w-24 sm:h-24">
<DraggableDesktopIcon
id="gemini"
label="Gemini"
iconType="gemini"
initialPosition={{ x: 0, y: 0 }}
onClick={() => { }}
onDoubleClick={openGeminiChat}
/>
</div>
<div className="pointer-events-auto w-20 h-20 sm:w-24 sm:h-24">
<DraggableDesktopIcon
id="clock"
label="Clock"
iconType="clock"
initialPosition={{ x: 0, y: 0 }}
onClick={() => { }}
onDoubleClick={openClock}
/>
</div>
<div className="pointer-events-auto w-20 h-20 sm:w-24 sm:h-24">
<DraggableDesktopIcon
id="calendar"
label="Calendar"
iconType="calendar"
initialPosition={{ x: 0, y: 0 }}
onClick={() => { }}
onDoubleClick={openCalendar}
/>
</div>
<div className="pointer-events-auto w-20 h-20 sm:w-24 sm:h-24">
<DraggableDesktopIcon
id="harddrive"
label="System"
iconType="harddrive"
initialPosition={{ x: 0, y: 0 }}
onClick={() => { }}
onDoubleClick={() => openFileManager('')}
/>
</div>
<div className="pointer-events-auto w-20 h-20 sm:w-24 sm:h-24">
<DraggableDesktopIcon
id="flutter-editor"
label="Flutter IDE"
iconType="flutter"
initialPosition={{ x: 0, y: 0 }}
onClick={() => { }}
onDoubleClick={openFlutterCodeEditor}
/>
</div>
<div className="pointer-events-auto w-20 h-20 sm:w-24 sm:h-24">
<DraggableDesktopIcon
id="quiz"
label="Quiz Master"
iconType="quiz"
initialPosition={{ x: 0, y: 0 }}
onClick={() => { }}
onDoubleClick={openQuizApp}
/>
</div>
<div className="pointer-events-auto w-20 h-20 sm:w-24 sm:h-24">
<DraggableDesktopIcon
id="voice-app"
label="Voice Studio"
iconType="voice-app"
initialPosition={{ x: 0, y: 0 }}
onClick={() => { }}
onDoubleClick={openVoiceApp}
/>
</div>
</div>
{/* Windows Container - adjusted for mobile topbar height */}
<div className="fixed inset-0 pointer-events-none" style={{ position: 'fixed', top: '40px', left: 0, right: 0, bottom: 0 }}>
<AnimatePresence>
{fileManagerOpen && (
<motion.div
key="file-manager"
initial={{ opacity: 0, scale: 0.95 }}
animate={{
opacity: fileManagerMinimized ? 0 : 1,
scale: fileManagerMinimized ? 0.9 : 1,
y: fileManagerMinimized ? 100 : 0,
}}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
style={{
pointerEvents: fileManagerMinimized ? 'none' : 'auto',
display: fileManagerMinimized ? 'none' : 'block'
}}
>
<FileManager
currentPath={currentPath}
onNavigate={setCurrentPath}
onClose={closeFileManager}
onOpenFlutterApp={openFlutterRunner}
onMinimize={() => setFileManagerMinimized(true)}
onFocus={() => bringWindowToFront('fileManager')}
zIndex={windowZIndices.fileManager || 1000}
onOpenApp={handleOpenApp}
onOpenTextFile={openTextEditor}
/>
</motion.div>
)}
{calendarOpen && (
<motion.div
key="calendar"
initial={{ opacity: 0, scale: 0.95 }}
animate={{
opacity: calendarMinimized ? 0 : 1,
scale: calendarMinimized ? 0.9 : 1,
y: calendarMinimized ? 100 : 0,
}}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
style={{
pointerEvents: calendarMinimized ? 'none' : 'auto',
display: calendarMinimized ? 'none' : 'block'
}}
>
<Calendar
onClose={closeCalendar}
onMinimize={() => setCalendarMinimized(true)}
onFocus={() => bringWindowToFront('calendar')}
zIndex={windowZIndices.calendar || 1000}
/>
</motion.div>
)}
{clockOpen && (
<motion.div
key="clock"
initial={{ opacity: 0, scale: 0.95 }}
animate={{
opacity: clockMinimized ? 0 : 1,
scale: clockMinimized ? 0.9 : 1,
y: clockMinimized ? 100 : 0,
}}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
style={{
pointerEvents: clockMinimized ? 'none' : 'auto',
display: clockMinimized ? 'none' : 'block'
}}
>
<Clock
onClose={closeClock}
onMinimize={() => setClockMinimized(true)}
onFocus={() => bringWindowToFront('clock')}
zIndex={windowZIndices.clock || 1000}
/>
</motion.div>
)}
{messagesOpen && (
<motion.div
key="messages"
initial={{ opacity: 0, scale: 0.95 }}
animate={{
opacity: messagesMinimized ? 0 : 1,
scale: messagesMinimized ? 0.9 : 1,
y: messagesMinimized ? 100 : 0,
}}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
style={{
pointerEvents: messagesMinimized ? 'none' : 'auto',
display: messagesMinimized ? 'none' : 'block'
}}
>
<Messages
onClose={closeMessages}
onMinimize={() => setMessagesMinimized(true)}
onFocus={() => bringWindowToFront('messages')}
zIndex={windowZIndices.messages || 1000}
/>
</motion.div>
)}
{geminiChatOpen && (
<motion.div
key="gemini"
initial={{ opacity: 0, scale: 0.95 }}
animate={{
opacity: geminiChatMinimized ? 0 : 1,
scale: geminiChatMinimized ? 0.9 : 1,
y: geminiChatMinimized ? 100 : 0,
}}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
style={{
pointerEvents: geminiChatMinimized ? 'none' : 'auto',
display: geminiChatMinimized ? 'none' : 'block'
}}
>
<GeminiChat
onClose={closeGeminiChat}
onMinimize={() => setGeminiChatMinimized(true)}
onFocus={() => bringWindowToFront('gemini')}
zIndex={windowZIndices.gemini || 1000}
/>
</motion.div>
)}
{flutterRunnerOpen && activeFlutterApp && (
<motion.div
key="flutter-runner"
initial={{ opacity: 0, scale: 0.95 }}
animate={{
opacity: flutterRunnerMinimized ? 0 : 1,
scale: flutterRunnerMinimized ? 0.9 : 1,
y: flutterRunnerMinimized ? 100 : 0,
}}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
style={{
pointerEvents: flutterRunnerMinimized ? 'none' : 'auto',
display: flutterRunnerMinimized ? 'none' : 'block'
}}
>
<FlutterRunner
onClose={() => {
setFlutterRunnerOpen(false)
setActiveFlutterApp(null)
}}
onMinimize={() => setFlutterRunnerMinimized(true)}
onFocus={() => bringWindowToFront('flutterRunner')}
zIndex={windowZIndices.flutterRunner || 1000}
/>
</motion.div>
)}
{flutterCodeEditorOpen && (
<motion.div
key="flutter-code-editor"
initial={{ opacity: 0, scale: 0.95 }}
animate={{
opacity: flutterCodeEditorMinimized ? 0 : 1,
scale: flutterCodeEditorMinimized ? 0.9 : 1,
y: flutterCodeEditorMinimized ? 100 : 0,
}}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
style={{
pointerEvents: flutterCodeEditorMinimized ? 'none' : 'auto',
display: flutterCodeEditorMinimized ? 'none' : 'block'
}}
>
<FlutterRunner
onClose={closeFlutterCodeEditor}
onMinimize={() => setFlutterCodeEditorMinimized(true)}
onFocus={() => bringWindowToFront('flutterCodeEditor')}
zIndex={windowZIndices.flutterCodeEditor || 1000}
/>
</motion.div>
)}
{quizAppOpen && (
<motion.div
key="quiz-app"
initial={{ opacity: 0, scale: 0.95 }}
animate={{
opacity: quizAppMinimized ? 0 : 1,
scale: quizAppMinimized ? 0.9 : 1,
y: quizAppMinimized ? 100 : 0,
}}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
style={{
pointerEvents: quizAppMinimized ? 'none' : 'auto',
display: quizAppMinimized ? 'none' : 'block'
}}
>
<QuizApp
onClose={closeQuizApp}
onMinimize={() => setQuizAppMinimized(true)}
onFocus={() => bringWindowToFront('quizApp')}
zIndex={windowZIndices.quizApp || 1000}
/>
</motion.div>
)}
{textEditorOpen && activeTextFile && (
<motion.div
key="text-editor"
initial={{ opacity: 0, scale: 0.95 }}
animate={{
opacity: textEditorMinimized ? 0 : 1,
scale: textEditorMinimized ? 0.9 : 1,
y: textEditorMinimized ? 100 : 0,
}}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
style={{
pointerEvents: textEditorMinimized ? 'none' : 'auto',
display: textEditorMinimized ? 'none' : 'block'
}}
>
<TextEditor
initialContent={activeTextFile.content}
fileName={activeTextFile.fileName}
filePath={activeTextFile.filePath}
passkey={activeTextFile.passkey}
onClose={closeTextEditor}
onMinimize={() => setTextEditorMinimized(true)}
onFocus={() => bringWindowToFront('textEditor')}
zIndex={windowZIndices.textEditor || 1000}
/>
</motion.div>
)}
{voiceAppOpen && (
<motion.div
key="voice-app"
initial={{ opacity: 0, scale: 0.95 }}
animate={{
opacity: voiceAppMinimized ? 0 : 1,
scale: voiceAppMinimized ? 0.9 : 1,
y: voiceAppMinimized ? 100 : 0,
}}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
style={{
pointerEvents: voiceAppMinimized ? 'none' : 'auto',
display: voiceAppMinimized ? 'none' : 'block'
}}
>
<VoiceApp
onClose={closeVoiceApp}
onMinimize={() => setVoiceAppMinimized(true)}
onFocus={() => bringWindowToFront('voiceApp')}
zIndex={windowZIndices.voiceApp || 1000}
/>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
{/* Spotlight Search */}
<SpotlightSearch
isOpen={spotlightOpen}
onClose={() => setSpotlightOpen(false)}
onOpenApp={handleOpenApp}
/>
{/* Context Menu */}
<ContextMenu
isOpen={contextMenuVisible}
position={contextMenuPosition}
onClose={() => setContextMenuVisible(false)}
onAction={handleContextMenuAction}
/>
{/* Help Modal */}
<HelpModal isOpen={helpModalOpen} onClose={closeHelpModal} />
{/* Desktop Context Menu */}
<DesktopContextMenu
isOpen={contextMenuOpen}
position={contextMenuPos}
onClose={() => setContextMenuOpen(false)}
onChangeBackground={handleChangeBackground}
/>
{/* Background Selector Modal */}
<BackgroundSelector
isOpen={backgroundSelectorOpen}
onClose={() => setBackgroundSelectorOpen(false)}
onSelectBackground={handleSelectBackground}
currentBackground={currentBackground}
/>
{/* System Power Overlay */}
<SystemPowerOverlay
state={powerState}
onWake={handleWake}
onRestartComplete={handleWake}
/>
{/* About Modal */}
<AboutModal
isOpen={aboutModalOpen}
onClose={() => setAboutModalOpen(false)}
/>
</div>
)
}