@@ -18,7 +18,10 @@ export function LogScroller() {
1818 const [ isPaused , setIsPaused ] = useState ( false ) ;
1919 const [ isVisible , setIsVisible ] = useState ( true ) ;
2020 const [ isStandalone , setIsStandalone ] = useState ( false ) ;
21+ const [ isMinimized , setIsMinimized ] = useState ( false ) ;
22+ const [ isMaximized , setIsMaximized ] = useState ( false ) ;
2123 const scrollerRef = useRef < HTMLDivElement > ( null ) ;
24+ const containerRef = useRef < HTMLDivElement > ( null ) ;
2225
2326 // Check if we're in standalone mode based on window width
2427 useEffect ( ( ) => {
@@ -87,23 +90,41 @@ export function LogScroller() {
8790 setLogs ( [ generatePumaBootLogEntry ( ) ] ) ;
8891 } , [ generatePumaBootLogEntry ] ) ;
8992
90- // Add a new log entry at an interval, but not when user is hovering (isPaused)
93+ // Add a new log entry at an interval, but not when user is hovering (isPaused) unless maximized
9194 useInterval ( ( ) => {
92- if ( ! isPaused ) {
95+ if ( ! isPaused || isMaximized ) {
9396 setLogs ( ( prevLogs ) => {
9497 const newLogs = [ ...prevLogs , generateLogEntry ( ) ] ;
95- // Keep only the last 15 logs
96- return newLogs . slice ( - 15 ) ;
98+ // Keep only the last 15 logs when normal, more when maximized
99+ return newLogs . slice ( - ( isMaximized ? 30 : 15 ) ) ;
97100 } ) ;
98101 }
99102 } , 2500 ) ;
100103
101104 // Scroll to the bottom when logs change
102105 useEffect ( ( ) => {
103- if ( scrollerRef . current && ! isPaused ) {
106+ if ( scrollerRef . current && ( ! isPaused || isMaximized ) ) {
104107 scrollerRef . current . scrollTop = scrollerRef . current . scrollHeight ;
105108 }
106- } , [ logs , isPaused ] ) ;
109+ } , [ logs , isPaused , isMaximized ] ) ;
110+
111+ // Toggle the minimized state
112+ const handleMinimize = ( ) => {
113+ setIsMinimized ( true ) ;
114+ setIsMaximized ( false ) ;
115+ } ;
116+
117+ // Toggle the maximized state
118+ const handleMaximize = ( ) => {
119+ setIsMaximized ( true ) ;
120+ setIsMinimized ( false ) ;
121+ } ;
122+
123+ // Restore the window to normal state
124+ const handleRestore = ( ) => {
125+ setIsMinimized ( false ) ;
126+ setIsMaximized ( false ) ;
127+ } ;
107128
108129 // If not visible, return an empty div of the same height
109130 if ( ! isVisible ) {
@@ -117,68 +138,126 @@ export function LogScroller() {
117138 '0 10px 30px rgba(0, 0, 0, 0.2), 0 1px 3px rgba(0, 0, 0, 0.3), -5px 5px 15px rgba(0, 0, 0, 0.15)' ,
118139 } ;
119140
120- // Different styles based on layout
121- const perspectiveStyle = isStandalone
122- ? {
123- // Straight style for standalone mode
124- transform : 'scale(1)' ,
125- marginLeft : '0' ,
126- marginRight : '0' ,
127- ...baseStyle ,
128- }
129- : {
130- // 3D perspective for side-by-side mode
131- transform :
132- 'perspective(1500px) rotateX(4deg) rotateY(-8deg) rotateZ(1deg)' ,
133- transformOrigin : 'center center' ,
134- marginLeft : '-40px' ,
135- marginRight : '80px' ,
136- ...baseStyle ,
137- } ;
141+ // Different styles based on window state and layout
142+ let perspectiveStyle = { } ;
143+
144+ if ( isMinimized ) {
145+ // Minimized state - small window in bottom right
146+ perspectiveStyle = {
147+ transform : 'scale(0.25)' ,
148+ position : 'fixed' as const ,
149+ bottom : '20px' ,
150+ right : '20px' ,
151+ width : '400px' ,
152+ zIndex : 100 ,
153+ transformOrigin : 'bottom right' ,
154+ ...baseStyle ,
155+ } ;
156+ } else if ( isMaximized ) {
157+ // Maximized state - fixed position to cover the entire hero section
158+ perspectiveStyle = {
159+ transform : 'scale(1)' ,
160+ position : 'fixed' as const ,
161+ top : '0' ,
162+ left : '0' ,
163+ width : '100vw' ,
164+ height : '100vh' ,
165+ marginLeft : '0' ,
166+ marginRight : '0' ,
167+ zIndex : 999 ,
168+ ...baseStyle ,
169+ boxShadow : '0 10px 50px rgba(0, 0, 0, 0.4), 0 1px 3px rgba(0, 0, 0, 0.3)' ,
170+ borderRadius : '0' ,
171+ } ;
172+ } else {
173+ // Regular state - depends on standalone
174+ perspectiveStyle = isStandalone
175+ ? {
176+ // Straight style for standalone mode
177+ transform : 'scale(1)' ,
178+ marginLeft : '0' ,
179+ marginRight : '0' ,
180+ ...baseStyle ,
181+ }
182+ : {
183+ // 3D perspective for side-by-side mode
184+ transform :
185+ 'perspective(1500px) rotateX(4deg) rotateY(-8deg) rotateZ(1deg)' ,
186+ transformOrigin : 'center center' ,
187+ marginLeft : '-40px' ,
188+ marginRight : '80px' ,
189+ ...baseStyle ,
190+ } ;
191+ }
138192
139- const onMouseOverStyle = isStandalone
140- ? {
141- transform : 'scale(1.01)' ,
142- boxShadow :
143- '0 15px 35px rgba(0, 0, 0, 0.25), 0 3px 5px rgba(0, 0, 0, 0.35)' ,
144- }
145- : {
146- transform :
147- 'perspective(1500px) rotateX(3deg) rotateY(-6deg) rotateZ(0.5deg) scale(1.01)' ,
148- boxShadow :
149- '0 15px 35px rgba(0, 0, 0, 0.25), 0 3px 5px rgba(0, 0, 0, 0.35), -8px 8px 20px rgba(0, 0, 0, 0.15)' ,
150- } ;
193+ // Mouse over effects - only for non-minimized state
194+ const onMouseOverStyle = isMinimized
195+ ? { }
196+ : isStandalone
197+ ? {
198+ transform : 'scale(1.01)' ,
199+ boxShadow :
200+ '0 15px 35px rgba(0, 0, 0, 0.25), 0 3px 5px rgba(0, 0, 0, 0.35)' ,
201+ }
202+ : {
203+ transform :
204+ 'perspective(1500px) rotateX(3deg) rotateY(-6deg) rotateZ(0.5deg) scale(1.01)' ,
205+ boxShadow :
206+ '0 15px 35px rgba(0, 0, 0, 0.25), 0 3px 5px rgba(0, 0, 0, 0.35), -8px 8px 20px rgba(0, 0, 0, 0.15)' ,
207+ } ;
151208
152- const onMouseOutStyle = isStandalone
153- ? {
154- transform : 'scale(1)' ,
155- boxShadow : baseStyle . boxShadow ,
156- }
157- : {
158- transform :
159- 'perspective(1500px) rotateX(4deg) rotateY(-8deg) rotateZ(1deg) scale(1)' ,
160- boxShadow : baseStyle . boxShadow ,
161- } ;
209+ const onMouseOutStyle = isMinimized
210+ ? { }
211+ : isStandalone
212+ ? {
213+ transform : 'scale(1)' ,
214+ boxShadow : baseStyle . boxShadow ,
215+ }
216+ : {
217+ transform :
218+ 'perspective(1500px) rotateX(4deg) rotateY(-8deg) rotateZ(1deg) scale(1)' ,
219+ boxShadow : baseStyle . boxShadow ,
220+ } ;
221+
222+ // Adjust height based on maximized state
223+ const heightClass = isMaximized ? "h-screen" : "h-[375px]" ;
162224
163225 return (
164226 < div
165- className = "w-full h-[375px] bg-black rounded-lg overflow-hidden shadow-lg transition-all duration-300 ease-in-out"
227+ ref = { containerRef }
228+ className = { `${ isMaximized ? '' : 'w-full' } ${ heightClass } bg-black rounded-lg overflow-hidden shadow-lg transition-all duration-300 ease-in-out` }
166229 style = { perspectiveStyle }
167230 onMouseOver = { ( e ) => {
168- Object . assign ( e . currentTarget . style , onMouseOverStyle ) ;
231+ if ( ! isMinimized && ! isMaximized ) {
232+ Object . assign ( e . currentTarget . style , onMouseOverStyle ) ;
233+ }
169234 } }
170235 onMouseOut = { ( e ) => {
171- Object . assign ( e . currentTarget . style , onMouseOutStyle ) ;
236+ if ( ! isMinimized && ! isMaximized ) {
237+ Object . assign ( e . currentTarget . style , onMouseOutStyle ) ;
238+ }
239+ } }
240+ onMouseEnter = { ( ) => {
241+ if ( ! isMaximized ) {
242+ setIsPaused ( true ) ;
243+ }
172244 } }
173- onMouseEnter = { ( ) => setIsPaused ( true ) }
174245 onMouseLeave = { ( ) => {
175- setIsPaused ( false ) ;
176- // Add a small delay before resuming scrolling
177- setTimeout ( ( ) => {
178- if ( scrollerRef . current ) {
179- scrollerRef . current . scrollTop = scrollerRef . current . scrollHeight ;
180- }
181- } , 100 ) ;
246+ if ( ! isMaximized ) {
247+ setIsPaused ( false ) ;
248+ // Add a small delay before resuming scrolling
249+ setTimeout ( ( ) => {
250+ if ( scrollerRef . current ) {
251+ scrollerRef . current . scrollTop = scrollerRef . current . scrollHeight ;
252+ }
253+ } , 100 ) ;
254+ }
255+ } }
256+ onClick = { ( ) => {
257+ // Clicking on the minimized window restores it
258+ if ( isMinimized ) {
259+ handleRestore ( ) ;
260+ }
182261 } }
183262 >
184263 < div
@@ -192,18 +271,29 @@ export function LogScroller() {
192271 < div
193272 className = "w-3 h-3 rounded-full bg-red-500 cursor-pointer hover:bg-red-400 transition-colors"
194273 style = { { boxShadow : '0 1px 1px rgba(0, 0, 0, 0.2)' } }
195- onClick = { ( ) => setIsVisible ( false ) }
274+ onClick = { ( e ) => {
275+ e . stopPropagation ( ) ;
276+ setIsVisible ( false ) ;
277+ } }
196278 title = "Click to close"
197279 > </ div >
198280 < div
199281 className = "w-3 h-3 rounded-full bg-yellow-500 cursor-pointer hover:bg-yellow-400 transition-colors"
200282 style = { { boxShadow : '0 1px 1px rgba(0, 0, 0, 0.2)' } }
201- onClick = { ( ) => setIsVisible ( false ) }
283+ onClick = { ( e ) => {
284+ e . stopPropagation ( ) ;
285+ handleMinimize ( ) ;
286+ } }
202287 title = "Click to minimize"
203288 > </ div >
204289 < div
205- className = "w-3 h-3 rounded-full bg-green-500"
290+ className = "w-3 h-3 rounded-full bg-green-500 cursor-pointer hover:bg-green-400 transition-colors "
206291 style = { { boxShadow : '0 1px 1px rgba(0, 0, 0, 0.2)' } }
292+ onClick = { ( e ) => {
293+ e . stopPropagation ( ) ;
294+ isMaximized ? handleRestore ( ) : handleMaximize ( ) ;
295+ } }
296+ title = { isMaximized ? "Click to restore" : "Click to maximize" }
207297 > </ div >
208298 </ div >
209299 < div
@@ -218,7 +308,7 @@ export function LogScroller() {
218308 </ div >
219309
220310 { /* Container with relative positioning for the scrollable content and overlay */ }
221- < div className = " relative h-[333px]">
311+ < div className = { ` relative ${ isMaximized ? " h-[calc(100vh-30px)]" : "h-[ 333px]"} ` } >
222312 { /* Scrollable content */ }
223313 < div
224314 ref = { scrollerRef }
@@ -244,15 +334,15 @@ export function LogScroller() {
244334 backgroundColor : 'transparent' ,
245335 padding : '12px' ,
246336 borderRadius : '0px' ,
247- minHeight : '300px' ,
337+ minHeight : isMaximized ? 'calc(100vh - 100px)' : '300px' ,
248338 textShadow : '0 1px 0 rgba(0, 0, 0, 0.7)' ,
249339 letterSpacing : '0.2px' ,
250340 } }
251341 >
252342 { logs . join ( '\n\n' ) }
253343 </ SyntaxHighlighter >
254344 ) : (
255- < div className = " w-full h-[300px]" > </ div >
345+ < div className = { ` w-full ${ isMaximized ? ' h-[calc(100vh-100px)]' : 'h-[ 300px]' } ` } > </ div >
256346 ) }
257347 </ div >
258348 </ div >
0 commit comments