Reubencf commited on
Commit
c21bca9
·
1 Parent(s): 91918e1

some changes made

Browse files
app/api/sessions/verify/route.ts CHANGED
@@ -4,20 +4,21 @@ import { SessionManager } from '@/lib/sessionManager';
4
  export async function POST(request: NextRequest) {
5
  try {
6
  const body = await request.json();
7
- const { sessionKey } = body;
 
8
 
9
- if (!sessionKey) {
10
  return NextResponse.json(
11
- { success: false, error: 'Session key is required' },
12
  { status: 400 }
13
  );
14
  }
15
 
16
  const sessionManager = SessionManager.getInstance();
17
- const isValid = await sessionManager.validateSession(sessionKey);
18
 
19
  if (isValid) {
20
- const session = await sessionManager.getSession(sessionKey);
21
  return NextResponse.json({
22
  success: true,
23
  valid: true,
 
4
  export async function POST(request: NextRequest) {
5
  try {
6
  const body = await request.json();
7
+ const { sessionKey, sessionId } = body;
8
+ const idToVerify = sessionId || sessionKey;
9
 
10
+ if (!idToVerify) {
11
  return NextResponse.json(
12
+ { success: false, error: 'Session ID or key is required' },
13
  { status: 400 }
14
  );
15
  }
16
 
17
  const sessionManager = SessionManager.getInstance();
18
+ const isValid = await sessionManager.validateSession(idToVerify);
19
 
20
  if (isValid) {
21
+ const session = await sessionManager.getSession(idToVerify);
22
  return NextResponse.json({
23
  success: true,
24
  valid: true,
app/components/AboutModal.tsx CHANGED
@@ -1,7 +1,7 @@
1
  'use client'
2
 
3
- import React, { useEffect, useState } from 'react'
4
- import { X, Info, HardDrive, Cpu, Globe, Key } from '@phosphor-icons/react'
5
  import { motion, AnimatePresence } from 'framer-motion'
6
 
7
  interface AboutModalProps {
@@ -10,45 +10,6 @@ interface AboutModalProps {
10
  }
11
 
12
  export function AboutModal({ isOpen, onClose }: AboutModalProps) {
13
- const [systemId, setSystemId] = useState<string>('')
14
- const [publicPath, setPublicPath] = useState<string>('')
15
- const [tempPath, setTempPath] = useState<string>('')
16
-
17
- useEffect(() => {
18
- // Generate unique system ID based on browser fingerprint
19
- const generateSystemId = () => {
20
- const nav = window.navigator
21
- const screen = window.screen
22
- const fingerprint = [
23
- nav.userAgent,
24
- nav.language,
25
- screen.colorDepth,
26
- screen.width,
27
- screen.height,
28
- new Date().getTimezoneOffset(),
29
- nav.hardwareConcurrency,
30
- nav.platform
31
- ].join('|')
32
-
33
- // Create a simple hash
34
- let hash = 0
35
- for (let i = 0; i < fingerprint.length; i++) {
36
- const char = fingerprint.charCodeAt(i)
37
- hash = ((hash << 5) - hash) + char
38
- hash = hash & hash // Convert to 32-bit integer
39
- }
40
-
41
- // Convert to hex and make it look like a system ID
42
- const hexHash = Math.abs(hash).toString(16).toUpperCase()
43
- return `REUBEN-${hexHash.substring(0, 4)}-${hexHash.substring(4, 8)}-${Date.now().toString(36).toUpperCase()}`
44
- }
45
-
46
- // Set system paths
47
- setSystemId(generateSystemId())
48
- setPublicPath('E:\\mpc-hackathon\\public')
49
- setTempPath(`E:\\mpc-hackathon\\data\\temp_${Date.now()}`)
50
- }, [])
51
-
52
  if (!isOpen) return null
53
 
54
  return (
@@ -63,107 +24,22 @@ export function AboutModal({ isOpen, onClose }: AboutModalProps) {
63
  transition={{ duration: 0.2 }}
64
  className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-[101]"
65
  >
66
- <div className="bg-white rounded-2xl shadow-2xl p-8 min-w-[500px] max-w-[600px]">
67
- {/* Logo and Title */}
68
- <div className="text-center mb-6">
69
- <div className="w-24 h-24 mx-auto mb-4 bg-gradient-to-br from-purple-600 to-blue-600 rounded-2xl flex items-center justify-center">
70
- <span className="text-white font-bold text-3xl">R</span>
71
- </div>
72
- <h1 className="text-3xl font-bold text-gray-900">Reuben OS</h1>
73
- <p className="text-gray-500 mt-2">Advanced Desktop Environment</p>
74
- <p className="text-sm text-gray-400 mt-1">Version 1.0.0</p>
75
  </div>
76
 
77
- {/* System Information */}
78
- <div className="space-y-4 mb-6">
79
- <div className="bg-gray-50 rounded-lg p-4">
80
- <div className="flex items-center mb-2">
81
- <Key className="text-purple-600 mr-2" size={20} weight="fill" />
82
- <span className="font-semibold text-gray-700">System ID</span>
83
- </div>
84
- <code className="text-xs bg-white px-2 py-1 rounded border border-gray-200 text-gray-600 font-mono block">
85
- {systemId || 'Generating...'}
86
- </code>
87
- </div>
88
-
89
- <div className="bg-gray-50 rounded-lg p-4">
90
- <div className="flex items-center mb-2">
91
- <HardDrive className="text-blue-600 mr-2" size={20} weight="fill" />
92
- <span className="font-semibold text-gray-700">Storage Paths</span>
93
- </div>
94
- <div className="space-y-2">
95
- <div>
96
- <span className="text-xs text-gray-500">Public:</span>
97
- <code className="text-xs bg-white px-2 py-1 rounded border border-gray-200 text-gray-600 font-mono block mt-1">
98
- {publicPath}
99
- </code>
100
- </div>
101
- <div>
102
- <span className="text-xs text-gray-500">Temp:</span>
103
- <code className="text-xs bg-white px-2 py-1 rounded border border-gray-200 text-gray-600 font-mono block mt-1">
104
- {tempPath}
105
- </code>
106
- </div>
107
- </div>
108
- </div>
109
-
110
- <div className="bg-gray-50 rounded-lg p-4">
111
- <div className="flex items-center mb-2">
112
- <Cpu className="text-green-600 mr-2" size={20} weight="fill" />
113
- <span className="font-semibold text-gray-700">Capabilities</span>
114
- </div>
115
- <div className="text-xs text-gray-600 space-y-1">
116
- <div className="flex items-center">
117
- <span className="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
118
- Python Code Execution (via MCP)
119
- </div>
120
- <div className="flex items-center">
121
- <span className="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
122
- HTML/CSS/JS Preview
123
- </div>
124
- <div className="flex items-center">
125
- <span className="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
126
- File Management System
127
- </div>
128
- <div className="flex items-center">
129
- <span className="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
130
- Claude AI Integration
131
- </div>
132
- <div className="flex items-center">
133
- <span className="w-2 h-2 bg-yellow-500 rounded-full mr-2"></span>
134
- Flutter/Dart (Coming Soon)
135
- </div>
136
- </div>
137
- </div>
138
 
139
- <div className="bg-gray-50 rounded-lg p-4">
140
- <div className="flex items-center mb-2">
141
- <Globe className="text-indigo-600 mr-2" size={20} weight="fill" />
142
- <span className="font-semibold text-gray-700">MCP Server</span>
143
- </div>
144
- <div className="text-xs text-gray-600">
145
- <div>Status: <span className="text-green-600 font-semibold">Connected</span></div>
146
- <div>Endpoint: <code className="font-mono">reuben-os</code></div>
147
- <div>Tools: 25+ available</div>
148
- </div>
149
- </div>
150
- </div>
151
-
152
- {/* Footer */}
153
- <div className="text-center text-xs text-gray-400 mb-4">
154
- <p>© 2024 Reuben OS. All rights reserved.</p>
155
- <p className="mt-1">Built with Next.js, React, and MCP Technology</p>
156
  </div>
157
 
158
  {/* Close Button */}
159
- <button
160
- onClick={onClose}
161
- className="w-full bg-gradient-to-r from-purple-600 to-blue-600 text-white py-2 rounded-lg font-semibold hover:opacity-90 transition-opacity"
162
- >
163
- Close
164
- </button>
165
-
166
- {/* X button in corner */}
167
  <button
168
  onClick={onClose}
169
  className="absolute top-4 right-4 text-gray-400 hover:text-gray-600 transition-colors"
 
1
  'use client'
2
 
3
+ import React from 'react'
4
+ import { X } from '@phosphor-icons/react'
5
  import { motion, AnimatePresence } from 'framer-motion'
6
 
7
  interface AboutModalProps {
 
10
  }
11
 
12
  export function AboutModal({ isOpen, onClose }: AboutModalProps) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  if (!isOpen) return null
14
 
15
  return (
 
24
  transition={{ duration: 0.2 }}
25
  className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-[101]"
26
  >
27
+ <div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-2xl p-8 w-[400px] border border-white/40 text-center relative">
28
+ {/* Logo */}
29
+ <div className="w-24 h-24 mx-auto mb-6 bg-gradient-to-br from-purple-600 to-blue-600 rounded-2xl flex items-center justify-center shadow-lg">
30
+ <span className="text-white font-bold text-4xl">R</span>
 
 
 
 
 
31
  </div>
32
 
33
+ {/* Title */}
34
+ <h1 className="text-3xl font-bold text-gray-900 mb-2">Reuben OS</h1>
35
+ <p className="text-gray-500 text-sm mb-6">Version 1.0.0</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
+ {/* Creator Info */}
38
+ <div className="text-gray-600 font-medium text-sm">
39
+ Created by a huggingface user Reubencf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  </div>
41
 
42
  {/* Close Button */}
 
 
 
 
 
 
 
 
43
  <button
44
  onClick={onClose}
45
  className="absolute top-4 right-4 text-gray-400 hover:text-gray-600 transition-colors"
app/components/CalendarWidget.tsx ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import React, { useState } from 'react'
4
+ import { CaretLeft, CaretRight } from '@phosphor-icons/react'
5
+ import { motion } from 'framer-motion'
6
+
7
+ export function CalendarWidget() {
8
+ const [currentDate, setCurrentDate] = useState(new Date())
9
+ const today = new Date()
10
+
11
+ const monthNames = [
12
+ 'January', 'February', 'March', 'April', 'May', 'June',
13
+ 'July', 'August', 'September', 'October', 'November', 'December'
14
+ ]
15
+
16
+ const daysOfWeek = ['S', 'M', 'T', 'W', 'T', 'F', 'S']
17
+
18
+ const getDaysInMonth = (date: Date) => {
19
+ const year = date.getFullYear()
20
+ const month = date.getMonth()
21
+ const firstDay = new Date(year, month, 1)
22
+ const lastDay = new Date(year, month + 1, 0)
23
+ const daysInMonth = lastDay.getDate()
24
+ const startingDayOfWeek = firstDay.getDay()
25
+
26
+ const days: (number | null)[] = []
27
+ for (let i = 0; i < startingDayOfWeek; i++) {
28
+ days.push(null)
29
+ }
30
+ for (let i = 1; i <= daysInMonth; i++) {
31
+ days.push(i)
32
+ }
33
+ return days
34
+ }
35
+
36
+ const days = getDaysInMonth(currentDate)
37
+
38
+ const isToday = (day: number | null) => {
39
+ if (!day) return false
40
+ return (
41
+ day === today.getDate() &&
42
+ currentDate.getMonth() === today.getMonth() &&
43
+ currentDate.getFullYear() === today.getFullYear()
44
+ )
45
+ }
46
+
47
+ const previousMonth = () => {
48
+ setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1))
49
+ }
50
+
51
+ const nextMonth = () => {
52
+ setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1))
53
+ }
54
+
55
+ return (
56
+ <div className="w-80 bg-white/80 backdrop-blur-xl rounded-xl shadow-2xl border border-white/40 p-4 text-gray-800">
57
+ {/* Header */}
58
+ <div className="flex items-center justify-between mb-4">
59
+ <div className="font-bold text-lg">
60
+ {monthNames[currentDate.getMonth()]} {currentDate.getFullYear()}
61
+ </div>
62
+ <div className="flex space-x-1">
63
+ <button
64
+ onClick={previousMonth}
65
+ className="p-1 hover:bg-gray-200/50 rounded-full transition-colors"
66
+ >
67
+ <CaretLeft size={16} weight="bold" />
68
+ </button>
69
+ <button
70
+ onClick={nextMonth}
71
+ className="p-1 hover:bg-gray-200/50 rounded-full transition-colors"
72
+ >
73
+ <CaretRight size={16} weight="bold" />
74
+ </button>
75
+ </div>
76
+ </div>
77
+
78
+ {/* Days of Week */}
79
+ <div className="grid grid-cols-7 mb-2">
80
+ {daysOfWeek.map((day, i) => (
81
+ <div key={i} className="text-center text-xs font-semibold text-gray-500">
82
+ {day}
83
+ </div>
84
+ ))}
85
+ </div>
86
+
87
+ {/* Calendar Grid */}
88
+ <div className="grid grid-cols-7 gap-1">
89
+ {days.map((day, index) => (
90
+ <div
91
+ key={index}
92
+ className={`
93
+ aspect-square flex items-center justify-center text-sm rounded-full
94
+ ${day ? 'cursor-default' : ''}
95
+ ${isToday(day) ? 'bg-blue-500 text-white font-bold shadow-md' : 'text-gray-700'}
96
+ ${day && !isToday(day) ? 'hover:bg-gray-200/50' : ''}
97
+ `}
98
+ >
99
+ {day}
100
+ </div>
101
+ ))}
102
+ </div>
103
+
104
+ {/* Event Dots (Mock) */}
105
+ <div className="mt-4 pt-3 border-t border-gray-200/50">
106
+ <div className="text-xs font-semibold text-gray-500 mb-2">UPCOMING EVENTS</div>
107
+ <div className="space-y-2">
108
+ <div className="flex items-center gap-2">
109
+ <div className="w-1 h-8 rounded-full bg-blue-500"></div>
110
+ <div>
111
+ <div className="text-xs font-bold">Team Meeting</div>
112
+ <div className="text-[10px] text-gray-500">10:00 AM - 11:00 AM</div>
113
+ </div>
114
+ </div>
115
+ <div className="flex items-center gap-2">
116
+ <div className="w-1 h-8 rounded-full bg-purple-500"></div>
117
+ <div>
118
+ <div className="text-xs font-bold">Project Review</div>
119
+ <div className="text-[10px] text-gray-500">2:00 PM - 3:30 PM</div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ )
126
+ }
app/components/MatrixRain.tsx CHANGED
@@ -11,6 +11,7 @@ interface MatrixRainProps {
11
  export function MatrixRain({ isActive, onComplete }: MatrixRainProps) {
12
  const canvasRef = useRef<HTMLCanvasElement>(null)
13
  const [showText, setShowText] = useState(false)
 
14
 
15
  useEffect(() => {
16
  if (!isActive) return
@@ -22,214 +23,214 @@ export function MatrixRain({ isActive, onComplete }: MatrixRainProps) {
22
  if (!ctx) return
23
 
24
  // Set canvas size
25
- canvas.width = window.innerWidth
26
- canvas.height = window.innerHeight
 
 
 
 
 
 
 
 
27
 
28
- // Matrix rain configuration
29
- const fontSize = 14
30
- const columns = Math.floor(canvas.width / fontSize)
31
- const drops: number[] = []
 
 
32
 
33
- // Initialize drops
34
  for (let i = 0; i < columns; i++) {
35
- drops[i] = Math.random() * -100
 
 
 
 
36
  }
37
 
38
- // Characters to use in the rain
39
- const matrix = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%^&*()_+-=[]{}|;:<>,.?/~`'
40
- const matrixArray = matrix.split('')
 
 
41
 
42
- // Special word to display
43
- const specialWord = 'REUBENOS'
44
- let specialWordTimer: NodeJS.Timeout
45
 
46
- // Show the special text after 1.5 seconds
47
- if (isActive) {
48
- specialWordTimer = setTimeout(() => {
49
- setShowText(true)
50
- }, 1500)
51
- }
52
-
53
- // Drawing function
54
- function draw() {
55
  if (!ctx || !canvas) return
56
 
57
- // Semi-transparent black to create fade effect
58
- ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'
59
- ctx.fillRect(0, 0, canvas.width, canvas.height)
60
 
61
- // Green text
62
- ctx.fillStyle = '#0F0'
63
- ctx.font = `${fontSize}px monospace`
64
 
65
- // Draw drops
66
- for (let i = 0; i < drops.length; i++) {
67
- // Random character
68
- const text = matrixArray[Math.floor(Math.random() * matrixArray.length)]
69
 
70
- // Draw the character
71
- ctx.fillText(text, i * fontSize, drops[i] * fontSize)
72
 
73
- // Reset drop when it goes off screen
74
- if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) {
75
- drops[i] = 0
76
- }
77
 
78
- // Move drop down
79
- drops[i]++
80
- }
81
- }
82
 
83
- // Animation loop
84
- const interval = setInterval(draw, 35)
 
 
 
85
 
86
- // Cleanup
87
- return () => {
88
- clearInterval(interval)
89
- clearTimeout(specialWordTimer)
90
- setShowText(false)
91
- }
92
- }, [isActive])
93
 
94
- // Handle window resize
95
- useEffect(() => {
96
- if (!isActive) return
 
 
 
 
97
 
98
- const handleResize = () => {
99
- const canvas = canvasRef.current
100
- if (canvas) {
101
- canvas.width = window.innerWidth
102
- canvas.height = window.innerHeight
 
 
 
 
 
 
 
103
  }
 
 
104
  }
105
 
106
- window.addEventListener('resize', handleResize)
107
- return () => window.removeEventListener('resize', handleResize)
 
 
 
 
 
 
 
 
 
 
108
  }, [isActive])
109
 
110
- // Auto-complete after 5 seconds
111
  useEffect(() => {
112
  if (!isActive) return
113
-
114
  const timer = setTimeout(() => {
115
- if (onComplete) {
116
- onComplete()
117
- }
118
- }, 5000)
119
-
120
  return () => clearTimeout(timer)
121
  }, [isActive, onComplete])
122
 
123
  return (
124
  <AnimatePresence>
125
  {isActive && (
126
- <>
127
- <motion.div
128
- initial={{ opacity: 0 }}
129
- animate={{ opacity: 1 }}
130
- exit={{ opacity: 0 }}
131
- transition={{ duration: 0.5 }}
132
- className="fixed inset-0 z-[9999] bg-black"
133
- >
134
- <canvas
135
- ref={canvasRef}
136
- className="absolute inset-0"
137
- style={{ display: 'block' }}
138
- />
139
-
140
- {/* REUBENOS Text */}
141
- <AnimatePresence>
142
- {showText && (
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  <motion.div
144
- initial={{ opacity: 0, scale: 0.5 }}
145
- animate={{ opacity: 1, scale: 1 }}
146
- exit={{ opacity: 0, scale: 0.8 }}
147
- transition={{
148
- duration: 0.8,
149
- ease: 'easeOut'
150
- }}
151
- className="absolute inset-0 flex items-center justify-center pointer-events-none"
152
  >
153
- <div className="text-center">
154
- <motion.h1
155
- className="text-[#0F0] font-mono font-bold select-none"
156
- style={{
157
- fontSize: 'clamp(3rem, 10vw, 8rem)',
158
- textShadow: `
159
- 0 0 10px #0F0,
160
- 0 0 20px #0F0,
161
- 0 0 30px #0F0,
162
- 0 0 40px #0A0,
163
- 0 0 70px #0A0,
164
- 0 0 80px #0A0,
165
- 0 0 100px #0A0,
166
- 0 0 150px #0A0
167
- `,
168
- letterSpacing: '0.2em'
169
- }}
170
- initial={{ y: 20 }}
171
- animate={{
172
- y: [20, -10, 0],
173
- textShadow: [
174
- `0 0 10px #0F0, 0 0 20px #0F0, 0 0 30px #0F0, 0 0 40px #0A0, 0 0 70px #0A0, 0 0 80px #0A0, 0 0 100px #0A0, 0 0 150px #0A0`,
175
- `0 0 20px #0F0, 0 0 30px #0F0, 0 0 40px #0F0, 0 0 50px #0A0, 0 0 80px #0A0, 0 0 90px #0A0, 0 0 120px #0A0, 0 0 180px #0A0`,
176
- `0 0 10px #0F0, 0 0 20px #0F0, 0 0 30px #0F0, 0 0 40px #0A0, 0 0 70px #0A0, 0 0 80px #0A0, 0 0 100px #0A0, 0 0 150px #0A0`
177
- ]
178
- }}
179
- transition={{
180
- duration: 2,
181
- repeat: Infinity,
182
- repeatType: 'reverse',
183
- ease: 'easeInOut'
184
- }}
185
- >
186
- REUBENOS
187
- </motion.h1>
188
-
189
- <motion.div
190
- initial={{ opacity: 0 }}
191
- animate={{ opacity: [0, 1, 0] }}
192
- transition={{
193
- duration: 1.5,
194
- repeat: Infinity,
195
- repeatDelay: 0.5
196
- }}
197
- className="mt-8 text-[#0F0] font-mono text-xl"
198
- style={{
199
- textShadow: '0 0 5px #0F0, 0 0 10px #0F0'
200
- }}
201
- >
202
- SYSTEM BREACH DETECTED
203
- </motion.div>
204
-
205
- <motion.div
206
- initial={{ width: 0 }}
207
- animate={{ width: '100%' }}
208
- transition={{
209
- duration: 3,
210
- ease: 'linear'
211
- }}
212
- className="mt-4 h-1 bg-gradient-to-r from-transparent via-[#0F0] to-transparent mx-auto max-w-md"
213
- style={{
214
- boxShadow: '0 0 10px #0F0'
215
- }}
216
- />
217
  </div>
218
  </motion.div>
219
- )}
220
- </AnimatePresence>
221
-
222
- {/* Click to exit hint */}
223
- <motion.div
224
- initial={{ opacity: 0 }}
225
- animate={{ opacity: 1 }}
226
- transition={{ delay: 3, duration: 1 }}
227
- className="absolute bottom-10 left-1/2 transform -translate-x-1/2 text-[#0F0] font-mono text-sm opacity-50"
228
- >
229
- Click anywhere to exit the Matrix
230
- </motion.div>
231
- </motion.div>
232
- </>
233
  )}
234
  </AnimatePresence>
235
  )
 
11
  export function MatrixRain({ isActive, onComplete }: MatrixRainProps) {
12
  const canvasRef = useRef<HTMLCanvasElement>(null)
13
  const [showText, setShowText] = useState(false)
14
+ const requestRef = useRef<number>(0)
15
 
16
  useEffect(() => {
17
  if (!isActive) return
 
23
  if (!ctx) return
24
 
25
  // Set canvas size
26
+ const resizeCanvas = () => {
27
+ canvas.width = window.innerWidth
28
+ canvas.height = window.innerHeight
29
+ }
30
+ resizeCanvas()
31
+ window.addEventListener('resize', resizeCanvas)
32
+
33
+ // Configuration
34
+ const fontSize = 16
35
+ const columns = Math.ceil(canvas.width / fontSize)
36
 
37
+ // State for each column
38
+ // y: current vertical position
39
+ // speed: speed of the drop
40
+ // chars: array of characters in this column (for potential future complex effects, currently just generating on fly)
41
+ // lastDraw: timestamp of last update to control speed
42
+ const drops: { y: number; speed: number; lastDraw: number }[] = []
43
 
 
44
  for (let i = 0; i < columns; i++) {
45
+ drops[i] = {
46
+ y: Math.random() * -1000, // Start above screen randomly
47
+ speed: Math.random() * 0.5 + 0.5, // Random speed between 0.5 and 1.0
48
+ lastDraw: 0
49
+ }
50
  }
51
 
52
+ // Katakana + Latin + Numbers
53
+ const katakana = 'アァカサタナハマヤャラワガザダバパイィキシチニヒミリヰギジヂビピウゥクスツヌフムユュルグズブヅプエェケセテネヘメレヱゲゼデベペオォコソトノホモヨョロヲゴゾドボポヴッン'
54
+ const latin = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
55
+ const nums = '0123456789'
56
+ const alphabet = katakana + latin + nums
57
 
58
+ let lastTime = 0
59
+ const fps = 30
60
+ const frameInterval = 1000 / fps
61
 
62
+ const draw = (timestamp: number) => {
 
 
 
 
 
 
 
 
63
  if (!ctx || !canvas) return
64
 
65
+ const deltaTime = timestamp - lastTime
 
 
66
 
67
+ if (deltaTime > frameInterval) {
68
+ lastTime = timestamp - (deltaTime % frameInterval)
 
69
 
70
+ // Semi-transparent black to create fade effect
71
+ // Adjust opacity for trail length (lower = longer trails)
72
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'
73
+ ctx.fillRect(0, 0, canvas.width, canvas.height)
74
 
75
+ ctx.font = `${fontSize}px monospace`
76
+ ctx.textAlign = 'center'
77
 
78
+ for (let i = 0; i < drops.length; i++) {
79
+ const drop = drops[i]
 
 
80
 
81
+ // Draw character
82
+ const text = alphabet.charAt(Math.floor(Math.random() * alphabet.length))
83
+ const x = i * fontSize
 
84
 
85
+ // Glowing head effect
86
+ ctx.fillStyle = '#FFF' // White head
87
+ ctx.shadowBlur = 8
88
+ ctx.shadowColor = '#FFF'
89
+ ctx.fillText(text, x, drop.y * fontSize)
90
 
91
+ // Reset shadow for next characters (though we are clearing screen, this is for the trail effect if we were drawing full columns)
92
+ ctx.shadowBlur = 0
 
 
 
 
 
93
 
94
+ // Draw the trail character (slightly above the head)
95
+ // Actually, the fade effect handles the trail. We just need to draw the new head.
96
+ // To make it look "better", we can draw a green character at the same spot in the NEXT frame,
97
+ // but the fade rect handles the "turning green" part naturally if we just draw white.
98
+ // However, standard matrix is: Head is white, immediate trail is bright green, tail is dark green.
99
+ // With the fade rect method, everything drawn turns darker green over time.
100
+ // So drawing White is correct for the head.
101
 
102
+ // Let's also draw a green character strictly at the previous position to ensure it stays green and not just faded white
103
+ ctx.fillStyle = '#0F0'
104
+ ctx.fillText(text, x, (drop.y - 1) * fontSize)
105
+
106
+ // Move drop
107
+ if (drop.y * fontSize > canvas.height && Math.random() > 0.975) {
108
+ drop.y = 0
109
+ drop.speed = Math.random() * 0.5 + 0.5 // New random speed
110
+ }
111
+
112
+ drop.y += drop.speed
113
+ }
114
  }
115
+
116
+ requestRef.current = requestAnimationFrame(draw)
117
  }
118
 
119
+ requestRef.current = requestAnimationFrame(draw)
120
+
121
+ // Special text timer
122
+ const specialWordTimer = setTimeout(() => {
123
+ setShowText(true)
124
+ }, 1500)
125
+
126
+ return () => {
127
+ window.removeEventListener('resize', resizeCanvas)
128
+ if (requestRef.current) cancelAnimationFrame(requestRef.current)
129
+ clearTimeout(specialWordTimer)
130
+ }
131
  }, [isActive])
132
 
133
+ // Auto-complete
134
  useEffect(() => {
135
  if (!isActive) return
 
136
  const timer = setTimeout(() => {
137
+ onComplete?.()
138
+ }, 8000) // Extended to 8s to enjoy the view
 
 
 
139
  return () => clearTimeout(timer)
140
  }, [isActive, onComplete])
141
 
142
  return (
143
  <AnimatePresence>
144
  {isActive && (
145
+ <motion.div
146
+ initial={{ opacity: 0 }}
147
+ animate={{ opacity: 1 }}
148
+ exit={{ opacity: 0 }}
149
+ transition={{ duration: 1 }}
150
+ className="fixed inset-0 z-[9999] bg-black cursor-pointer"
151
+ onClick={onComplete}
152
+ >
153
+ <canvas
154
+ ref={canvasRef}
155
+ className="absolute inset-0 block"
156
+ />
157
+
158
+ <AnimatePresence>
159
+ {showText && (
160
+ <motion.div
161
+ initial={{ opacity: 0, scale: 0.8, filter: 'blur(10px)' }}
162
+ animate={{ opacity: 1, scale: 1, filter: 'blur(0px)' }}
163
+ exit={{ opacity: 0, scale: 1.2, filter: 'blur(20px)' }}
164
+ transition={{ duration: 1.5, ease: "easeOut" }}
165
+ className="absolute inset-0 flex flex-col items-center justify-center pointer-events-none z-10"
166
+ >
167
+ <div className="relative">
168
+ <motion.h1
169
+ className="text-transparent font-mono font-bold tracking-[0.2em] text-6xl md:text-8xl lg:text-9xl"
170
+ style={{
171
+ WebkitTextStroke: '2px #0F0',
172
+ textShadow: '0 0 20px rgba(0, 255, 0, 0.5)'
173
+ }}
174
+ animate={{
175
+ textShadow: [
176
+ '0 0 20px rgba(0, 255, 0, 0.5)',
177
+ '0 0 40px rgba(0, 255, 0, 0.8)',
178
+ '0 0 20px rgba(0, 255, 0, 0.5)'
179
+ ]
180
+ }}
181
+ transition={{ duration: 2, repeat: Infinity }}
182
+ >
183
+ REUBENOS
184
+ </motion.h1>
185
+
186
+ {/* Glitch effect layers */}
187
+ <motion.h1
188
+ className="absolute top-0 left-0 text-[#0F0] font-mono font-bold tracking-[0.2em] text-6xl md:text-8xl lg:text-9xl opacity-50 mix-blend-screen"
189
+ animate={{
190
+ x: [-2, 2, -2],
191
+ y: [1, -1, 1],
192
+ opacity: [0.5, 0.3, 0.5]
193
+ }}
194
+ transition={{ duration: 0.2, repeat: Infinity, repeatType: "mirror" }}
195
+ >
196
+ REUBENOS
197
+ </motion.h1>
198
+ <motion.h1
199
+ className="absolute top-0 left-0 text-[#F00] font-mono font-bold tracking-[0.2em] text-6xl md:text-8xl lg:text-9xl opacity-30 mix-blend-screen"
200
+ animate={{
201
+ x: [2, -2, 2],
202
+ y: [-1, 1, -1],
203
+ }}
204
+ transition={{ duration: 0.3, repeat: Infinity, repeatType: "mirror" }}
205
+ >
206
+ REUBENOS
207
+ </motion.h1>
208
+ </div>
209
+
210
  <motion.div
211
+ initial={{ opacity: 0, y: 20 }}
212
+ animate={{ opacity: 1, y: 0 }}
213
+ transition={{ delay: 0.5, duration: 0.8 }}
214
+ className="mt-8 flex flex-col items-center gap-2"
 
 
 
 
215
  >
216
+ <div className="text-[#0F0] font-mono text-xl tracking-widest uppercase">
217
+ System Breach Detected
218
+ </div>
219
+ <div className="h-px w-64 bg-gradient-to-r from-transparent via-[#0F0] to-transparent animate-pulse" />
220
+ <div className="text-[#0F0] font-mono text-xs opacity-70 mt-2">
221
+ ACCESS GRANTED_
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  </div>
223
  </motion.div>
224
+ </motion.div>
225
+ )}
226
+ </AnimatePresence>
227
+
228
+ <div className="absolute bottom-8 left-0 right-0 text-center">
229
+ <p className="text-[#0F0] font-mono text-xs opacity-40 animate-pulse">
230
+ [ CLICK TO INITIALIZE ]
231
+ </p>
232
+ </div>
233
+ </motion.div>
 
 
 
 
234
  )}
235
  </AnimatePresence>
236
  )
app/components/SessionManager.tsx CHANGED
@@ -63,10 +63,10 @@ export function SessionManager() {
63
 
64
  if (data.success) {
65
  setCurrentSession(data.session)
66
- setSessionKey(data.session.key)
67
  setSuccess('Session created successfully!')
68
- localStorage.setItem('reubenOS_sessionKey', data.session.key)
69
- await loadSessionFiles(data.session.key)
70
  } else {
71
  setError(data.error || 'Failed to create session')
72
  }
@@ -286,10 +286,10 @@ export function SessionManager() {
286
 
287
  // Load saved session on mount
288
  useEffect(() => {
289
- const savedKey = localStorage.getItem('reubenOS_sessionKey')
290
- if (savedKey) {
291
- setSessionKey(savedKey)
292
- loadSessionFiles(savedKey)
293
  }
294
  loadPublicFiles()
295
  }, [])
@@ -309,6 +309,21 @@ export function SessionManager() {
309
  }
310
  }, [error])
311
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  return (
313
  <div className="min-h-screen bg-gradient-to-br from-purple-900/20 via-black to-purple-900/20 p-8">
314
  <div className="max-w-6xl mx-auto">
@@ -445,22 +460,20 @@ export function SessionManager() {
445
  <div className="flex gap-4 mb-6">
446
  <button
447
  onClick={() => setActiveTab('session')}
448
- className={`px-4 py-2 rounded-lg transition-colors flex items-center gap-2 ${
449
- activeTab === 'session'
450
- ? 'bg-purple-600 text-white'
451
- : 'bg-gray-800 text-gray-400 hover:bg-gray-700'
452
- }`}
453
  >
454
  <Lock size={20} />
455
  Session Files ({files.length})
456
  </button>
457
  <button
458
  onClick={() => setActiveTab('public')}
459
- className={`px-4 py-2 rounded-lg transition-colors flex items-center gap-2 ${
460
- activeTab === 'public'
461
- ? 'bg-purple-600 text-white'
462
- : 'bg-gray-800 text-gray-400 hover:bg-gray-700'
463
- }`}
464
  >
465
  <Globe size={20} />
466
  Public Files ({publicFiles.length})
 
63
 
64
  if (data.success) {
65
  setCurrentSession(data.session)
66
+ setSessionKey(data.session.id) // Use ID as key for consistency
67
  setSuccess('Session created successfully!')
68
+ localStorage.setItem('reubenOS_sessionId', data.session.id)
69
+ await loadSessionFiles(data.session.id)
70
  } else {
71
  setError(data.error || 'Failed to create session')
72
  }
 
286
 
287
  // Load saved session on mount
288
  useEffect(() => {
289
+ const savedId = localStorage.getItem('reubenOS_sessionId')
290
+ if (savedId) {
291
+ setSessionKey(savedId)
292
+ loadSessionFiles(savedId)
293
  }
294
  loadPublicFiles()
295
  }, [])
 
309
  }
310
  }, [error])
311
 
312
+ // Clear messages after 3 seconds
313
+ useEffect(() => {
314
+ if (success) {
315
+ const timer = setTimeout(() => setSuccess(null), 3000)
316
+ return () => clearTimeout(timer)
317
+ }
318
+ }, [success])
319
+
320
+ useEffect(() => {
321
+ if (error) {
322
+ const timer = setTimeout(() => setError(null), 3000)
323
+ return () => clearTimeout(timer)
324
+ }
325
+ }, [error])
326
+
327
  return (
328
  <div className="min-h-screen bg-gradient-to-br from-purple-900/20 via-black to-purple-900/20 p-8">
329
  <div className="max-w-6xl mx-auto">
 
460
  <div className="flex gap-4 mb-6">
461
  <button
462
  onClick={() => setActiveTab('session')}
463
+ className={`px-4 py-2 rounded-lg transition-colors flex items-center gap-2 ${activeTab === 'session'
464
+ ? 'bg-purple-600 text-white'
465
+ : 'bg-gray-800 text-gray-400 hover:bg-gray-700'
466
+ }`}
 
467
  >
468
  <Lock size={20} />
469
  Session Files ({files.length})
470
  </button>
471
  <button
472
  onClick={() => setActiveTab('public')}
473
+ className={`px-4 py-2 rounded-lg transition-colors flex items-center gap-2 ${activeTab === 'public'
474
+ ? 'bg-purple-600 text-white'
475
+ : 'bg-gray-800 text-gray-400 hover:bg-gray-700'
476
+ }`}
 
477
  >
478
  <Globe size={20} />
479
  Public Files ({publicFiles.length})
app/components/SessionManagerWindow.tsx CHANGED
@@ -1,40 +1,12 @@
1
  'use client'
2
 
3
- import React, { useState, useEffect } from 'react'
4
- import { motion, AnimatePresence } from 'framer-motion'
5
  import {
6
- Key,
7
- Upload,
8
- Download,
9
- File,
10
- Folder,
11
- Globe,
12
- Lock,
13
  Copy,
14
  Check,
15
- X,
16
- FileText,
17
- Table as FileSpreadsheet,
18
- Presentation as FilePresentation,
19
- FilePdf,
20
- ArrowsClockwise as RefreshCw,
21
  } from '@phosphor-icons/react'
22
  import Window from './Window'
23
 
24
- interface Session {
25
- id: string
26
- key: string
27
- createdAt: string
28
- message?: string
29
- }
30
-
31
- interface FileItem {
32
- name: string
33
- size: number
34
- modified: string
35
- created: string
36
- }
37
-
38
  interface SessionManagerWindowProps {
39
  onClose: () => void
40
  sessionId: string
@@ -42,244 +14,7 @@ interface SessionManagerWindowProps {
42
  }
43
 
44
  export function SessionManagerWindow({ onClose, sessionId }: SessionManagerWindowProps) {
45
- const [files, setFiles] = useState<FileItem[]>([])
46
- const [publicFiles, setPublicFiles] = useState<FileItem[]>([])
47
- const [loading, setLoading] = useState(false)
48
- const [error, setError] = useState<string | null>(null)
49
- const [success, setSuccess] = useState<string | null>(null)
50
  const [copiedKey, setCopiedKey] = useState(false)
51
- const [copiedId, setCopiedId] = useState(false)
52
- const [activeTab, setActiveTab] = useState<'session' | 'public'>('session')
53
- const [uploadFile, setUploadFile] = useState<File | null>(null)
54
- const [isPublicUpload, setIsPublicUpload] = useState(false)
55
- const [sessionValid, setSessionValid] = useState<boolean | null>(null)
56
- const [validating, setValidating] = useState(false)
57
-
58
- // Session is automatically created - no manual creation needed!
59
-
60
- // Validate session on component mount
61
- useEffect(() => {
62
- if (sessionId) {
63
- validateSession()
64
- }
65
- // eslint-disable-next-line react-hooks/exhaustive-deps
66
- }, [])
67
-
68
- // Validate session
69
- const validateSession = async () => {
70
- setValidating(true)
71
- setError(null)
72
- try {
73
- const response = await fetch('/api/sessions/verify', {
74
- method: 'POST',
75
- headers: { 'Content-Type': 'application/json' },
76
- body: JSON.stringify({ sessionId })
77
- })
78
- const data = await response.json()
79
-
80
- if (data.success && data.valid) {
81
- setSessionValid(true)
82
- setSuccess('✅ Session is VALID! You can safely copy and use this Session ID with Claude Desktop.')
83
- setTimeout(() => setSuccess(null), 5000)
84
- } else {
85
- setSessionValid(false)
86
- setError('⚠️ Session is INVALID. The page will auto-refresh in 3 seconds to get a fresh session...')
87
- // Auto-refresh the page to get a new valid session
88
- setTimeout(() => {
89
- window.location.reload()
90
- }, 3000)
91
- }
92
- } catch (err) {
93
- setError('❌ Failed to validate session. Refreshing page...')
94
- setSessionValid(false)
95
- setTimeout(() => {
96
- window.location.reload()
97
- }, 2000)
98
- } finally {
99
- setValidating(false)
100
- }
101
- }
102
-
103
- // Load files for current session
104
- const loadSessionFiles = async (key: string) => {
105
- try {
106
- const response = await fetch('/api/sessions/files', {
107
- headers: { 'x-session-key': key }
108
- })
109
- const data = await response.json()
110
-
111
- if (data.success) {
112
- setFiles(data.files || [])
113
- }
114
- } catch (err) {
115
- console.error('Failed to load session files:', err)
116
- }
117
- }
118
-
119
- // Load public files
120
- const loadPublicFiles = async () => {
121
- try {
122
- const response = await fetch('/api/sessions/files?public=true')
123
- const data = await response.json()
124
-
125
- if (data.success) {
126
- setPublicFiles(data.files || [])
127
- }
128
- } catch (err) {
129
- console.error('Failed to load public files:', err)
130
- }
131
- }
132
-
133
- // Handle file upload
134
- const handleFileUpload = async () => {
135
- if (!uploadFile || !sessionId) {
136
- setError('No file selected or session not active')
137
- return
138
- }
139
-
140
- setLoading(true)
141
- setError(null)
142
- const formData = new FormData()
143
- formData.append('file', uploadFile)
144
- formData.append('public', isPublicUpload.toString())
145
-
146
- try {
147
- const response = await fetch('/api/sessions/upload', {
148
- method: 'POST',
149
- headers: { 'x-session-id': sessionId },
150
- body: formData
151
- })
152
- const data = await response.json()
153
-
154
- if (data.success) {
155
- setSuccess(`File uploaded successfully: ${data.fileName}`)
156
- setUploadFile(null)
157
- if (isPublicUpload) {
158
- await loadPublicFiles()
159
- } else {
160
- await loadSessionFiles(sessionId)
161
- }
162
- } else {
163
- setError(data.error || 'Failed to upload file')
164
- }
165
- } catch (err) {
166
- setError('Failed to upload file')
167
- }
168
- setLoading(false)
169
- }
170
-
171
- // Download file
172
- const downloadFile = async (fileName: string, isPublic: boolean) => {
173
- const url = `/api/sessions/download?file=${encodeURIComponent(fileName)}${isPublic ? '&public=true' : ''}`
174
- const headers: HeadersInit = isPublic ? {} : { 'x-session-id': sessionId }
175
-
176
- try {
177
- const response = await fetch(url, { headers })
178
- if (response.ok) {
179
- const blob = await response.blob()
180
- const downloadUrl = window.URL.createObjectURL(blob)
181
- const a = document.createElement('a')
182
- a.href = downloadUrl
183
- a.download = fileName
184
- document.body.appendChild(a)
185
- a.click()
186
- window.URL.revokeObjectURL(downloadUrl)
187
- document.body.removeChild(a)
188
- setSuccess(`Downloaded: ${fileName}`)
189
- } else {
190
- setError('Failed to download file')
191
- }
192
- } catch (err) {
193
- setError('Failed to download file')
194
- }
195
- }
196
-
197
- // Generate document
198
- const generateSampleDocument = async (type: string) => {
199
- if (!sessionId) {
200
- setError('No active session')
201
- return
202
- }
203
-
204
- setLoading(true)
205
- setError(null)
206
-
207
- const sampleContent: any = {
208
- docx: {
209
- type: 'docx',
210
- fileName: 'sample-document',
211
- content: {
212
- title: 'Sample Document',
213
- content: '# Introduction\n\nThis is a sample document generated by ReubenOS.\n\n## Features\n\n- Session-based isolation\n- Document generation\n- File management\n\n## Conclusion\n\nThank you for using ReubenOS!'
214
- }
215
- },
216
- pdf: {
217
- type: 'pdf',
218
- fileName: 'sample-report',
219
- content: {
220
- title: 'Sample PDF Report',
221
- content: 'This is a sample PDF report.\n\n# Section 1\n\nLorem ipsum dolor sit amet.\n\n# Section 2\n\nConclusion and summary.'
222
- }
223
- },
224
- ppt: {
225
- type: 'ppt',
226
- fileName: 'sample-presentation',
227
- content: {
228
- slides: [
229
- { title: 'Welcome', content: 'Welcome to ReubenOS', bullets: ['Feature 1', 'Feature 2'] },
230
- { title: 'Overview', content: 'System Overview', bullets: ['Session Management', 'Document Generation'] },
231
- { title: 'Thank You', content: 'Questions?' }
232
- ]
233
- }
234
- },
235
- excel: {
236
- type: 'excel',
237
- fileName: 'sample-data',
238
- content: {
239
- sheets: [{
240
- name: 'Sample Data',
241
- data: {
242
- headers: ['Name', 'Value', 'Status'],
243
- rows: [
244
- ['Item 1', '100', 'Active'],
245
- ['Item 2', '200', 'Pending'],
246
- ['Item 3', '150', 'Active']
247
- ]
248
- }
249
- }]
250
- }
251
- }
252
- }
253
-
254
- const documentData = sampleContent[type]
255
- if (!documentData) {
256
- setError('Invalid document type')
257
- setLoading(false)
258
- return
259
- }
260
-
261
- try {
262
- const response = await fetch('/api/documents/generate', {
263
- method: 'POST',
264
- headers: {
265
- 'Content-Type': 'application/json',
266
- 'x-session-id': sessionId
267
- },
268
- body: JSON.stringify({ ...documentData, isPublic: false })
269
- })
270
- const data = await response.json()
271
-
272
- if (data.success) {
273
- setSuccess(`Generated ${type.toUpperCase()}: ${data.fileName}`)
274
- await loadSessionFiles(sessionId)
275
- } else {
276
- setError(data.error || `Failed to generate ${type}`)
277
- }
278
- } catch (err) {
279
- setError(`Failed to generate ${type}`)
280
- }
281
- setLoading(false)
282
- }
283
 
284
  // Copy session ID
285
  const copySessionId = () => {
@@ -288,67 +23,14 @@ export function SessionManagerWindow({ onClose, sessionId }: SessionManagerWindo
288
  setTimeout(() => setCopiedKey(false), 2000)
289
  }
290
 
291
- // Get file icon based on extension
292
- const getFileIcon = (fileName: string) => {
293
- const ext = fileName.split('.').pop()?.toLowerCase()
294
- switch (ext) {
295
- case 'docx':
296
- case 'doc':
297
- return <FileText size={20} weight="fill" className="text-blue-500" />
298
- case 'xlsx':
299
- case 'xls':
300
- return <FileSpreadsheet size={20} weight="fill" className="text-green-500" />
301
- case 'pptx':
302
- case 'ppt':
303
- return <FilePresentation size={20} weight="fill" className="text-orange-500" />
304
- case 'pdf':
305
- return <FilePdf size={20} weight="fill" className="text-red-500" />
306
- default:
307
- return <File size={20} weight="fill" className="text-gray-500" />
308
- }
309
- }
310
-
311
- // Load files on mount and handle Escape key
312
- useEffect(() => {
313
- // Load files for the current session
314
- if (sessionId) {
315
- loadSessionFiles(sessionId)
316
- }
317
- loadPublicFiles()
318
-
319
- // Handle Escape key to close window
320
- const handleEscape = (e: KeyboardEvent) => {
321
- if (e.key === 'Escape') {
322
- onClose()
323
- }
324
- }
325
- window.addEventListener('keydown', handleEscape)
326
- return () => window.removeEventListener('keydown', handleEscape)
327
- }, [onClose, sessionId])
328
-
329
- // Clear messages after 3 seconds
330
- useEffect(() => {
331
- if (success) {
332
- const timer = setTimeout(() => setSuccess(null), 3000)
333
- return () => clearTimeout(timer)
334
- }
335
- }, [success])
336
-
337
- useEffect(() => {
338
- if (error) {
339
- const timer = setTimeout(() => setError(null), 3000)
340
- return () => clearTimeout(timer)
341
- }
342
- }, [error])
343
-
344
  return (
345
  <Window
346
  id="session-manager"
347
  title="Session Manager"
348
  isOpen={true}
349
  onClose={onClose}
350
- width={900}
351
- height={600}
352
  x={100}
353
  y={100}
354
  className="session-manager-window"
@@ -357,7 +39,7 @@ export function SessionManagerWindow({ onClose, sessionId }: SessionManagerWindo
357
  >
358
  <div className="flex flex-col h-full bg-gray-900/95 backdrop-blur-xl">
359
  {/* Window Content */}
360
- <div className="p-6 flex-1 overflow-auto">
361
  {/* Session Info */}
362
  <div className="mb-6">
363
  <div>
@@ -369,40 +51,6 @@ export function SessionManagerWindow({ onClose, sessionId }: SessionManagerWindo
369
  <div className="flex items-center justify-between mb-3">
370
  <span className="text-gray-300 text-base font-semibold">Your Session ID</span>
371
  <div className="flex gap-2">
372
- <button
373
- onClick={validateSession}
374
- disabled={validating}
375
- className={`flex items-center gap-2 px-3 py-1.5 rounded-lg transition-colors text-sm font-medium ${
376
- sessionValid === true
377
- ? 'bg-green-600/20 hover:bg-green-600/30 text-green-400'
378
- : sessionValid === false
379
- ? 'bg-red-600/20 hover:bg-red-600/30 text-red-400'
380
- : 'bg-blue-600/20 hover:bg-blue-600/30 text-blue-400'
381
- }`}
382
- title="Check if this session is valid on the server"
383
- >
384
- {validating ? (
385
- <>
386
- <RefreshCw size={16} className="animate-spin" />
387
- <span>Checking...</span>
388
- </>
389
- ) : sessionValid === true ? (
390
- <>
391
- <Check size={16} />
392
- <span>Valid ✓</span>
393
- </>
394
- ) : sessionValid === false ? (
395
- <>
396
- <X size={16} />
397
- <span>Invalid</span>
398
- </>
399
- ) : (
400
- <>
401
- <RefreshCw size={16} />
402
- <span>Validate</span>
403
- </>
404
- )}
405
- </button>
406
  <button
407
  onClick={copySessionId}
408
  className="flex items-center gap-2 px-4 py-1.5 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transition-colors text-sm font-medium shadow-lg"
@@ -431,172 +79,7 @@ export function SessionManagerWindow({ onClose, sessionId }: SessionManagerWindo
431
  </div>
432
  </div>
433
  </div>
434
-
435
- {/* File Upload */}
436
- <div className="mb-6">
437
- <h3 className="text-lg font-semibold text-white mb-4">Upload File</h3>
438
- <div className="flex gap-4">
439
- <input
440
- type="file"
441
- onChange={(e) => setUploadFile(e.target.files?.[0] || null)}
442
- className="flex-1 text-white file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:bg-purple-600 file:text-white hover:file:bg-purple-700"
443
- />
444
- <label className="flex items-center gap-2 text-white">
445
- <input
446
- type="checkbox"
447
- checked={isPublicUpload}
448
- onChange={(e) => setIsPublicUpload(e.target.checked)}
449
- className="rounded"
450
- />
451
- Public
452
- </label>
453
- <button
454
- onClick={handleFileUpload}
455
- disabled={!uploadFile || loading}
456
- className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors disabled:opacity-50"
457
- >
458
- <Upload size={20} className="inline mr-2" />
459
- Upload
460
- </button>
461
- </div>
462
- </div>
463
-
464
- {/* Document Generation */}
465
- <div className="mb-6">
466
- <h3 className="text-lg font-semibold text-white mb-4">Generate Documents</h3>
467
- <div className="grid grid-cols-4 gap-4">
468
- <button
469
- onClick={() => generateSampleDocument('docx')}
470
- disabled={loading}
471
- className="p-4 bg-blue-600/20 border border-blue-500/30 rounded-lg hover:bg-blue-600/30 transition-colors disabled:opacity-50"
472
- >
473
- <FileText size={32} className="text-blue-400 mx-auto mb-2" />
474
- <span className="text-white text-sm">Word Doc</span>
475
- </button>
476
- <button
477
- onClick={() => generateSampleDocument('pdf')}
478
- disabled={loading}
479
- className="p-4 bg-red-600/20 border border-red-500/30 rounded-lg hover:bg-red-600/30 transition-colors disabled:opacity-50"
480
- >
481
- <FilePdf size={32} className="text-red-400 mx-auto mb-2" />
482
- <span className="text-white text-sm">PDF</span>
483
- </button>
484
- <button
485
- onClick={() => generateSampleDocument('ppt')}
486
- disabled={loading}
487
- className="p-4 bg-orange-600/20 border border-orange-500/30 rounded-lg hover:bg-orange-600/30 transition-colors disabled:opacity-50"
488
- >
489
- <FilePresentation size={32} className="text-orange-400 mx-auto mb-2" />
490
- <span className="text-white text-sm">PowerPoint</span>
491
- </button>
492
- <button
493
- onClick={() => generateSampleDocument('excel')}
494
- disabled={loading}
495
- className="p-4 bg-green-600/20 border border-green-500/30 rounded-lg hover:bg-green-600/30 transition-colors disabled:opacity-50"
496
- >
497
- <FileSpreadsheet size={32} className="text-green-400 mx-auto mb-2" />
498
- <span className="text-white text-sm">Excel</span>
499
- </button>
500
- </div>
501
- </div>
502
-
503
- {/* Files List */}
504
- <div>
505
- {/* Tabs */}
506
- <div className="flex gap-4 mb-4">
507
- <button
508
- onClick={() => setActiveTab('session')}
509
- className={`px-4 py-2 rounded-lg transition-colors flex items-center gap-2 ${activeTab === 'session'
510
- ? 'bg-purple-600 text-white'
511
- : 'bg-gray-800 text-gray-400 hover:bg-gray-700'
512
- }`}
513
- >
514
- <Lock size={20} />
515
- Session Files ({files.length})
516
- </button>
517
- <button
518
- onClick={() => setActiveTab('public')}
519
- className={`px-4 py-2 rounded-lg transition-colors flex items-center gap-2 ${activeTab === 'public'
520
- ? 'bg-purple-600 text-white'
521
- : 'bg-gray-800 text-gray-400 hover:bg-gray-700'
522
- }`}
523
- >
524
- <Globe size={20} />
525
- Public Files ({publicFiles.length})
526
- </button>
527
- <button
528
- onClick={() => {
529
- if (activeTab === 'session' && sessionId) {
530
- loadSessionFiles(sessionId)
531
- } else {
532
- loadPublicFiles()
533
- }
534
- }}
535
- className="ml-auto p-2 bg-gray-800 text-gray-400 rounded-lg hover:bg-gray-700 transition-colors"
536
- >
537
- <RefreshCw size={20} />
538
- </button>
539
- </div>
540
-
541
- {/* File List */}
542
- <div className="space-y-2">
543
- {(activeTab === 'session' ? files : publicFiles).map((file) => (
544
- <div
545
- key={file.name}
546
- className="flex items-center justify-between p-3 bg-gray-800/50 rounded-lg hover:bg-gray-800/70 transition-colors"
547
- >
548
- <div className="flex items-center gap-3">
549
- {getFileIcon(file.name)}
550
- <div>
551
- <p className="text-white font-medium">{file.name}</p>
552
- <p className="text-gray-400 text-sm">
553
- {(file.size / 1024).toFixed(2)} KB • {new Date(file.modified).toLocaleDateString()}
554
- </p>
555
- </div>
556
- </div>
557
- <button
558
- onClick={() => downloadFile(file.name, activeTab === 'public')}
559
- className="p-2 bg-purple-600/20 text-purple-400 rounded-lg hover:bg-purple-600/30 transition-colors"
560
- >
561
- <Download size={20} />
562
- </button>
563
- </div>
564
- ))}
565
- {(activeTab === 'session' ? files : publicFiles).length === 0 && (
566
- <div className="text-center py-8">
567
- <Folder size={48} className="text-gray-600 mx-auto mb-4" />
568
- <p className="text-gray-400">No files in {activeTab === 'session' ? 'session' : 'public'} folder</p>
569
- </div>
570
- )}
571
- </div>
572
- </div>
573
  </div>
574
-
575
- {/* Messages */}
576
- <AnimatePresence>
577
- {success && (
578
- <motion.div
579
- initial={{ opacity: 0, y: 50 }}
580
- animate={{ opacity: 1, y: 0 }}
581
- exit={{ opacity: 0, y: 50 }}
582
- className="absolute bottom-4 right-4 px-4 py-2 bg-green-600 text-white rounded-lg shadow-lg"
583
- >
584
- <Check size={16} className="inline mr-2" />
585
- {success}
586
- </motion.div>
587
- )}
588
- {error && (
589
- <motion.div
590
- initial={{ opacity: 0, y: 50 }}
591
- animate={{ opacity: 1, y: 0 }}
592
- exit={{ opacity: 0, y: 50 }}
593
- className="absolute bottom-4 right-4 px-4 py-2 bg-red-600 text-white rounded-lg shadow-lg"
594
- >
595
- <X size={16} className="inline mr-2" />
596
- {error}
597
- </motion.div>
598
- )}
599
- </AnimatePresence>
600
  </div>
601
  </Window>
602
  )
 
1
  'use client'
2
 
3
+ import React, { useState } from 'react'
 
4
  import {
 
 
 
 
 
 
 
5
  Copy,
6
  Check,
 
 
 
 
 
 
7
  } from '@phosphor-icons/react'
8
  import Window from './Window'
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  interface SessionManagerWindowProps {
11
  onClose: () => void
12
  sessionId: string
 
14
  }
15
 
16
  export function SessionManagerWindow({ onClose, sessionId }: SessionManagerWindowProps) {
 
 
 
 
 
17
  const [copiedKey, setCopiedKey] = useState(false)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  // Copy session ID
20
  const copySessionId = () => {
 
23
  setTimeout(() => setCopiedKey(false), 2000)
24
  }
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  return (
27
  <Window
28
  id="session-manager"
29
  title="Session Manager"
30
  isOpen={true}
31
  onClose={onClose}
32
+ width={600}
33
+ height={350}
34
  x={100}
35
  y={100}
36
  className="session-manager-window"
 
39
  >
40
  <div className="flex flex-col h-full bg-gray-900/95 backdrop-blur-xl">
41
  {/* Window Content */}
42
+ <div className="p-6 flex-1 overflow-auto flex flex-col justify-center">
43
  {/* Session Info */}
44
  <div className="mb-6">
45
  <div>
 
51
  <div className="flex items-center justify-between mb-3">
52
  <span className="text-gray-300 text-base font-semibold">Your Session ID</span>
53
  <div className="flex gap-2">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  <button
55
  onClick={copySessionId}
56
  className="flex items-center gap-2 px-4 py-1.5 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transition-colors text-sm font-medium shadow-lg"
 
79
  </div>
80
  </div>
81
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  </div>
84
  </Window>
85
  )
app/components/TopBar.tsx CHANGED
@@ -1,15 +1,18 @@
1
  'use client'
2
 
3
- import React, { useState, useEffect } from 'react'
4
  import {
5
  SpeakerHigh,
6
  WifiHigh,
7
  BatteryFull,
8
  MagnifyingGlass,
9
  Desktop,
10
- CirclesFour
 
 
11
  } from '@phosphor-icons/react'
12
  import { motion, AnimatePresence } from 'framer-motion'
 
13
 
14
  interface TopBarProps {
15
  onPowerAction?: () => void
@@ -18,8 +21,9 @@ interface TopBarProps {
18
  }
19
 
20
  export function TopBar({ onPowerAction, activeAppName = 'Finder', onAboutClick }: TopBarProps) {
21
- const [appleMenuOpen, setAppleMenuOpen] = useState(false)
22
  const [currentTime, setCurrentTime] = useState('')
 
23
 
24
  useEffect(() => {
25
  const updateTime = () => {
@@ -38,87 +42,212 @@ export function TopBar({ onPowerAction, activeAppName = 'Finder', onAboutClick }
38
 
39
  updateTime()
40
  const interval = setInterval(updateTime, 1000)
41
- return () => clearInterval(interval)
 
 
 
 
 
 
 
 
 
 
 
42
  }, [])
43
 
 
 
 
 
44
  return (
45
- <div className="fixed top-0 left-0 right-0 h-8 glass flex items-center justify-between px-4 text-sm z-50 shadow-sm backdrop-blur-xl bg-white/40 border-b border-white/20">
46
- <div className="flex items-center space-x-4">
47
- <button
48
- onClick={() => setAppleMenuOpen(!appleMenuOpen)}
49
- className="font-bold text-lg hover:text-blue-600 transition-colors relative"
50
- >
51
- <CirclesFour size={20} weight="fill" className="text-blue-600" />
52
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  <span className="font-bold text-gray-800">Reuben OS</span>
54
  <span className="text-gray-600 text-xs ml-2 font-medium">{activeAppName}</span>
55
  </div>
56
 
57
- <div className="flex items-center space-x-4">
58
  <div className="flex items-center space-x-3 text-gray-700">
59
- <BatteryFull size={18} weight="fill" />
60
- <WifiHigh size={18} weight="bold" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  <MagnifyingGlass size={18} weight="bold" />
62
  </div>
63
- <div className="font-medium text-gray-800 min-w-[140px] text-right">{currentTime}</div>
64
- </div>
65
 
66
- <AnimatePresence>
67
- {appleMenuOpen && (
68
- <>
69
- <div
70
- className="fixed inset-0 z-[55]"
71
- onClick={() => setAppleMenuOpen(false)}
72
- />
73
- <motion.div
74
- initial={{ opacity: 0, y: -5 }}
75
- animate={{ opacity: 1, y: 0 }}
76
- exit={{ opacity: 0, y: -5 }}
77
- transition={{ duration: 0.15 }}
78
- className="absolute top-9 left-2 w-56 glass rounded-lg shadow-2xl flex flex-col py-1.5 z-[60] text-sm border border-white/40 backdrop-blur-2xl bg-white/80"
79
- >
80
- <button
81
- onClick={() => {
82
- if (onAboutClick) onAboutClick()
83
- setAppleMenuOpen(false)
84
- }}
85
- className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800 font-medium">
86
- About Reuben OS
87
- </button>
88
- <div className="h-px bg-gray-300/50 my-1 mx-2" />
89
- <button className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800">
90
- System Settings...
91
- </button>
92
- <div className="h-px bg-gray-300/50 my-1 mx-2" />
93
- <button className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800">
94
- Sleep
95
- </button>
96
- <button className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800">
97
- Restart...
98
- </button>
99
- <button
100
- onClick={() => {
101
- if (onPowerAction) onPowerAction()
102
- setAppleMenuOpen(false)
103
- }}
104
- className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800"
105
- >
106
- Shut Down...
107
- </button>
108
- <div className="h-px bg-gray-300/50 my-1 mx-2" />
109
- <button
110
- onClick={() => {
111
- if (onPowerAction) onPowerAction()
112
- setAppleMenuOpen(false)
113
- }}
114
- className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800"
115
  >
116
- Log Out...
117
- </button>
118
- </motion.div>
119
- </>
120
- )}
121
- </AnimatePresence>
122
  </div>
123
  )
124
  }
 
1
  'use client'
2
 
3
+ import React, { useState, useEffect, useRef } from 'react'
4
  import {
5
  SpeakerHigh,
6
  WifiHigh,
7
  BatteryFull,
8
  MagnifyingGlass,
9
  Desktop,
10
+ CirclesFour,
11
+ Check,
12
+ Warning
13
  } from '@phosphor-icons/react'
14
  import { motion, AnimatePresence } from 'framer-motion'
15
+ import { CalendarWidget } from './CalendarWidget'
16
 
17
  interface TopBarProps {
18
  onPowerAction?: () => void
 
21
  }
22
 
23
  export function TopBar({ onPowerAction, activeAppName = 'Finder', onAboutClick }: TopBarProps) {
24
+ const [activeMenu, setActiveMenu] = useState<'apple' | 'battery' | 'wifi' | 'clock' | null>(null)
25
  const [currentTime, setCurrentTime] = useState('')
26
+ const menuRef = useRef<HTMLDivElement>(null)
27
 
28
  useEffect(() => {
29
  const updateTime = () => {
 
42
 
43
  updateTime()
44
  const interval = setInterval(updateTime, 1000)
45
+
46
+ const handleClickOutside = (event: MouseEvent) => {
47
+ if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
48
+ setActiveMenu(null)
49
+ }
50
+ }
51
+
52
+ document.addEventListener('mousedown', handleClickOutside)
53
+ return () => {
54
+ clearInterval(interval)
55
+ document.removeEventListener('mousedown', handleClickOutside)
56
+ }
57
  }, [])
58
 
59
+ const toggleMenu = (menu: 'apple' | 'battery' | 'wifi' | 'clock') => {
60
+ setActiveMenu(activeMenu === menu ? null : menu)
61
+ }
62
+
63
  return (
64
+ <div className="fixed top-0 left-0 right-0 h-8 glass flex items-center justify-between px-4 text-sm z-50 shadow-sm backdrop-blur-xl bg-white/40 border-b border-white/20 select-none">
65
+ <div className="flex items-center space-x-4" ref={menuRef}>
66
+ <div className="relative">
67
+ <button
68
+ onClick={() => toggleMenu('apple')}
69
+ className={`font-bold text-lg hover:text-blue-600 transition-colors relative ${activeMenu === 'apple' ? 'text-blue-600' : ''}`}
70
+ >
71
+ <CirclesFour size={20} weight="fill" />
72
+ </button>
73
+
74
+ <AnimatePresence>
75
+ {activeMenu === 'apple' && (
76
+ <motion.div
77
+ initial={{ opacity: 0, y: 5, scale: 0.95 }}
78
+ animate={{ opacity: 1, y: 0, scale: 1 }}
79
+ exit={{ opacity: 0, y: 5, scale: 0.95 }}
80
+ transition={{ duration: 0.1 }}
81
+ className="absolute top-8 left-0 w-56 glass rounded-lg shadow-2xl flex flex-col py-1.5 z-[60] text-sm border border-white/40 backdrop-blur-2xl bg-white/80"
82
+ >
83
+ <button
84
+ onClick={() => {
85
+ if (onAboutClick) onAboutClick()
86
+ setActiveMenu(null)
87
+ }}
88
+ className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800 font-medium">
89
+ About Reuben OS
90
+ </button>
91
+ <div className="h-px bg-gray-300/50 my-1 mx-2" />
92
+ <button className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800">
93
+ System Settings...
94
+ </button>
95
+ <div className="h-px bg-gray-300/50 my-1 mx-2" />
96
+ <button className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800">
97
+ Sleep
98
+ </button>
99
+ <button className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800">
100
+ Restart...
101
+ </button>
102
+ <button
103
+ onClick={() => {
104
+ if (onPowerAction) onPowerAction()
105
+ setActiveMenu(null)
106
+ }}
107
+ className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800"
108
+ >
109
+ Shut Down...
110
+ </button>
111
+ <div className="h-px bg-gray-300/50 my-1 mx-2" />
112
+ <button
113
+ onClick={() => {
114
+ if (onPowerAction) onPowerAction()
115
+ setActiveMenu(null)
116
+ }}
117
+ className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800"
118
+ >
119
+ Log Out...
120
+ </button>
121
+ </motion.div>
122
+ )}
123
+ </AnimatePresence>
124
+ </div>
125
+
126
  <span className="font-bold text-gray-800">Reuben OS</span>
127
  <span className="text-gray-600 text-xs ml-2 font-medium">{activeAppName}</span>
128
  </div>
129
 
130
+ <div className="flex items-center space-x-4" ref={menuRef}>
131
  <div className="flex items-center space-x-3 text-gray-700">
132
+ {/* Battery */}
133
+ <div className="relative">
134
+ <button
135
+ onClick={() => toggleMenu('battery')}
136
+ className={`hover:bg-black/5 rounded px-1 py-0.5 transition-colors ${activeMenu === 'battery' ? 'bg-black/10' : ''}`}
137
+ >
138
+ <BatteryFull size={18} weight="fill" />
139
+ </button>
140
+ <AnimatePresence>
141
+ {activeMenu === 'battery' && (
142
+ <motion.div
143
+ initial={{ opacity: 0, y: 5, scale: 0.95 }}
144
+ animate={{ opacity: 1, y: 0, scale: 1 }}
145
+ exit={{ opacity: 0, y: 5, scale: 0.95 }}
146
+ transition={{ duration: 0.1 }}
147
+ className="absolute top-8 right-0 w-64 glass rounded-lg shadow-2xl flex flex-col py-2 z-[60] text-sm border border-white/40 backdrop-blur-2xl bg-white/80"
148
+ >
149
+ <div className="px-4 py-2 flex justify-between items-center border-b border-gray-200/50">
150
+ <span className="font-bold text-gray-700">Battery</span>
151
+ <span className="text-gray-500">85%</span>
152
+ </div>
153
+ <div className="px-4 py-2 text-xs text-gray-500">
154
+ Power Source: Battery
155
+ </div>
156
+ <div className="px-4 py-2 text-xs text-gray-500">
157
+ Using Significant Energy:
158
+ <div className="mt-1 flex items-center gap-2">
159
+ <div className="w-3 h-3 bg-gray-400 rounded-sm"></div>
160
+ <span>Chrome</span>
161
+ </div>
162
+ </div>
163
+ </motion.div>
164
+ )}
165
+ </AnimatePresence>
166
+ </div>
167
+
168
+ {/* WiFi */}
169
+ <div className="relative">
170
+ <button
171
+ onClick={() => toggleMenu('wifi')}
172
+ className={`hover:bg-black/5 rounded px-1 py-0.5 transition-colors ${activeMenu === 'wifi' ? 'bg-black/10' : ''}`}
173
+ >
174
+ <WifiHigh size={18} weight="bold" />
175
+ </button>
176
+ <AnimatePresence>
177
+ {activeMenu === 'wifi' && (
178
+ <motion.div
179
+ initial={{ opacity: 0, y: 5, scale: 0.95 }}
180
+ animate={{ opacity: 1, y: 0, scale: 1 }}
181
+ exit={{ opacity: 0, y: 5, scale: 0.95 }}
182
+ transition={{ duration: 0.1 }}
183
+ className="absolute top-8 right-0 w-72 glass rounded-lg shadow-2xl flex flex-col py-2 z-[60] text-sm border border-white/40 backdrop-blur-2xl bg-white/80"
184
+ >
185
+ <div className="px-4 py-2 flex justify-between items-center border-b border-gray-200/50">
186
+ <span className="font-bold text-gray-700">Wi-Fi</span>
187
+ <div className="w-8 h-4 bg-blue-500 rounded-full relative cursor-pointer">
188
+ <div className="absolute right-0.5 top-0.5 w-3 h-3 bg-white rounded-full shadow-sm"></div>
189
+ </div>
190
+ </div>
191
+ <div className="py-1">
192
+ <div className="px-4 py-1.5 bg-blue-500/10 flex justify-between items-center">
193
+ <div className="flex items-center gap-2">
194
+ <Check size={14} weight="bold" className="text-blue-600" />
195
+ <span className="font-medium text-gray-800">Reuben's WiFi</span>
196
+ </div>
197
+ <WifiHigh size={14} weight="bold" className="text-gray-600" />
198
+ </div>
199
+ <div className="h-px bg-gray-200/50 my-1 mx-4" />
200
+ <div className="px-4 py-1.5 text-gray-500 text-xs font-semibold">Personal Hotspots</div>
201
+ <div className="px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors flex justify-between items-center group cursor-pointer">
202
+ <span className="pl-6">iPhone 15 Pro</span>
203
+ <WifiHigh size={14} weight="bold" className="text-gray-400 group-hover:text-white" />
204
+ </div>
205
+ <div className="h-px bg-gray-200/50 my-1 mx-4" />
206
+ <div className="px-4 py-1.5 text-gray-500 text-xs font-semibold">Other Networks</div>
207
+ <div className="px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors flex justify-between items-center group cursor-pointer">
208
+ <span className="pl-6">Guest Network</span>
209
+ <WifiHigh size={14} weight="bold" className="text-gray-400 group-hover:text-white" />
210
+ </div>
211
+ <div className="px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors flex justify-between items-center group cursor-pointer">
212
+ <span className="pl-6">Starbucks WiFi</span>
213
+ <WifiHigh size={14} weight="bold" className="text-gray-400 group-hover:text-white" />
214
+ </div>
215
+ </div>
216
+ <div className="h-px bg-gray-200/50 my-1 mx-2" />
217
+ <button className="text-left px-4 py-1.5 hover:bg-blue-500 hover:text-white transition-colors text-gray-800">
218
+ Wi-Fi Settings...
219
+ </button>
220
+ </motion.div>
221
+ )}
222
+ </AnimatePresence>
223
+ </div>
224
+
225
  <MagnifyingGlass size={18} weight="bold" />
226
  </div>
 
 
227
 
228
+ {/* Clock */}
229
+ <div className="relative">
230
+ <button
231
+ onClick={() => toggleMenu('clock')}
232
+ className={`font-medium text-gray-800 min-w-[140px] text-right hover:bg-black/5 rounded px-2 py-0.5 transition-colors ${activeMenu === 'clock' ? 'bg-black/10' : ''}`}
233
+ >
234
+ {currentTime}
235
+ </button>
236
+ <AnimatePresence>
237
+ {activeMenu === 'clock' && (
238
+ <motion.div
239
+ initial={{ opacity: 0, y: 5, scale: 0.95 }}
240
+ animate={{ opacity: 1, y: 0, scale: 1 }}
241
+ exit={{ opacity: 0, y: 5, scale: 0.95 }}
242
+ transition={{ duration: 0.1 }}
243
+ className="absolute top-8 right-0 z-[60]"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  >
245
+ <CalendarWidget />
246
+ </motion.div>
247
+ )}
248
+ </AnimatePresence>
249
+ </div>
250
+ </div>
251
  </div>
252
  )
253
  }
lib/sessionManager.ts CHANGED
@@ -80,7 +80,7 @@ export class SessionManager {
80
  if (sessionIdOrKey.startsWith('session_')) {
81
  return this.validateSessionById(sessionIdOrKey);
82
  }
83
-
84
  // Otherwise treat as session key for backward compatibility
85
  if (this.sessions.has(sessionIdOrKey)) {
86
  const session = this.sessions.get(sessionIdOrKey)!;
@@ -126,7 +126,7 @@ export class SessionManager {
126
  const sessionPath = path.join(this.sessionDir, sessionId, 'session.json');
127
  const sessionData = await fs.readFile(sessionPath, 'utf-8');
128
  const session = JSON.parse(sessionData) as Session;
129
-
130
  // Load into memory
131
  session.lastAccessed = new Date();
132
  this.sessions.set(session.key, session);
@@ -146,7 +146,7 @@ export class SessionManager {
146
  return session;
147
  }
148
  }
149
-
150
  // Try loading from disk
151
  try {
152
  const sessionPath = path.join(this.sessionDir, sessionIdOrKey, 'session.json');
@@ -158,7 +158,7 @@ export class SessionManager {
158
  return null;
159
  }
160
  }
161
-
162
  // Otherwise treat as key
163
  if (await this.validateSession(sessionIdOrKey)) {
164
  return this.sessions.get(sessionIdOrKey) || null;
@@ -177,7 +177,7 @@ export class SessionManager {
177
  return null;
178
  }
179
  }
180
-
181
  // Otherwise get session by key
182
  const session = await this.getSession(sessionIdOrKey);
183
  if (session) {
@@ -245,7 +245,7 @@ export class SessionManager {
245
 
246
  try {
247
  await fs.rm(sessionPath, { recursive: true, force: true });
248
-
249
  // Remove from memory by finding the right key
250
  if (sessionIdOrKey.startsWith('session_')) {
251
  for (const [key, session] of this.sessions.entries()) {
@@ -257,7 +257,7 @@ export class SessionManager {
257
  } else {
258
  this.sessions.delete(sessionIdOrKey);
259
  }
260
-
261
  return true;
262
  } catch (error) {
263
  console.error('Error deleting session:', error);
 
80
  if (sessionIdOrKey.startsWith('session_')) {
81
  return this.validateSessionById(sessionIdOrKey);
82
  }
83
+
84
  // Otherwise treat as session key for backward compatibility
85
  if (this.sessions.has(sessionIdOrKey)) {
86
  const session = this.sessions.get(sessionIdOrKey)!;
 
126
  const sessionPath = path.join(this.sessionDir, sessionId, 'session.json');
127
  const sessionData = await fs.readFile(sessionPath, 'utf-8');
128
  const session = JSON.parse(sessionData) as Session;
129
+
130
  // Load into memory
131
  session.lastAccessed = new Date();
132
  this.sessions.set(session.key, session);
 
146
  return session;
147
  }
148
  }
149
+
150
  // Try loading from disk
151
  try {
152
  const sessionPath = path.join(this.sessionDir, sessionIdOrKey, 'session.json');
 
158
  return null;
159
  }
160
  }
161
+
162
  // Otherwise treat as key
163
  if (await this.validateSession(sessionIdOrKey)) {
164
  return this.sessions.get(sessionIdOrKey) || null;
 
177
  return null;
178
  }
179
  }
180
+
181
  // Otherwise get session by key
182
  const session = await this.getSession(sessionIdOrKey);
183
  if (session) {
 
245
 
246
  try {
247
  await fs.rm(sessionPath, { recursive: true, force: true });
248
+
249
  // Remove from memory by finding the right key
250
  if (sessionIdOrKey.startsWith('session_')) {
251
  for (const [key, session] of this.sessions.entries()) {
 
257
  } else {
258
  this.sessions.delete(sessionIdOrKey);
259
  }
260
+
261
  return true;
262
  } catch (error) {
263
  console.error('Error deleting session:', error);