mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-11-01 01:21:18 +01:00 
			
		
		
		
	RAINBOW MODE!!!!
This commit is contained in:
		
							parent
							
								
									342345cbb9
								
							
						
					
					
						commit
						41efd80dd5
					
				@ -1 +0,0 @@
 | 
				
			|||||||
{}
 | 
					 | 
				
			||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import { MantineProvider } from '@mantine/core';
 | 
					import { RainbowThemeProvider } from './components/RainbowThemeProvider';
 | 
				
			||||||
import { mantineTheme } from './theme/mantineTheme';
 | 
					 | 
				
			||||||
import HomePage from './pages/HomePage';
 | 
					import HomePage from './pages/HomePage';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Import global styles
 | 
					// Import global styles
 | 
				
			||||||
@ -9,8 +8,8 @@ import './index.css';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default function App() {
 | 
					export default function App() {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <MantineProvider theme={mantineTheme}>
 | 
					    <RainbowThemeProvider>
 | 
				
			||||||
      <HomePage />
 | 
					      <HomePage />
 | 
				
			||||||
    </MantineProvider>
 | 
					    </RainbowThemeProvider>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										55
									
								
								frontend/src/components/RainbowThemeProvider.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								frontend/src/components/RainbowThemeProvider.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					import React, { createContext, useContext, ReactNode } from 'react';
 | 
				
			||||||
 | 
					import { MantineProvider, ColorSchemeScript } from '@mantine/core';
 | 
				
			||||||
 | 
					import { useRainbowTheme } from '../hooks/useRainbowTheme';
 | 
				
			||||||
 | 
					import { mantineTheme } from '../theme/mantineTheme';
 | 
				
			||||||
 | 
					import rainbowStyles from '../styles/rainbow.module.css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface RainbowThemeContextType {
 | 
				
			||||||
 | 
					  themeMode: 'light' | 'dark' | 'rainbow';
 | 
				
			||||||
 | 
					  isRainbowMode: boolean;
 | 
				
			||||||
 | 
					  isToggleDisabled: boolean;
 | 
				
			||||||
 | 
					  toggleTheme: () => void;
 | 
				
			||||||
 | 
					  activateRainbow: () => void;
 | 
				
			||||||
 | 
					  deactivateRainbow: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const RainbowThemeContext = createContext<RainbowThemeContextType | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useRainbowThemeContext() {
 | 
				
			||||||
 | 
					  const context = useContext(RainbowThemeContext);
 | 
				
			||||||
 | 
					  if (!context) {
 | 
				
			||||||
 | 
					    throw new Error('useRainbowThemeContext must be used within RainbowThemeProvider');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return context;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface RainbowThemeProviderProps {
 | 
				
			||||||
 | 
					  children: ReactNode;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function RainbowThemeProvider({ children }: RainbowThemeProviderProps) {
 | 
				
			||||||
 | 
					  const rainbowTheme = useRainbowTheme();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Determine the Mantine color scheme
 | 
				
			||||||
 | 
					  const mantineColorScheme = rainbowTheme.themeMode === 'rainbow' ? 'dark' : rainbowTheme.themeMode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <ColorSchemeScript defaultColorScheme={mantineColorScheme} />
 | 
				
			||||||
 | 
					      <RainbowThemeContext.Provider value={rainbowTheme}>
 | 
				
			||||||
 | 
					        <MantineProvider
 | 
				
			||||||
 | 
					          theme={mantineTheme}
 | 
				
			||||||
 | 
					          defaultColorScheme={mantineColorScheme}
 | 
				
			||||||
 | 
					          forceColorScheme={mantineColorScheme}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <div
 | 
				
			||||||
 | 
					            className={rainbowTheme.isRainbowMode ? rainbowStyles.rainbowMode : ''}
 | 
				
			||||||
 | 
					            style={{ minHeight: '100vh' }}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            {children}
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </MantineProvider>
 | 
				
			||||||
 | 
					      </RainbowThemeContext.Provider>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,9 +1,11 @@
 | 
				
			|||||||
import React from "react";
 | 
					import React from "react";
 | 
				
			||||||
import { Button, SegmentedControl } from "@mantine/core";
 | 
					import { Button, SegmentedControl } from "@mantine/core";
 | 
				
			||||||
import { useMantineColorScheme } from "@mantine/core";
 | 
					import { useRainbowThemeContext } from "./RainbowThemeProvider";
 | 
				
			||||||
import LanguageSelector from "./LanguageSelector";
 | 
					import LanguageSelector from "./LanguageSelector";
 | 
				
			||||||
 | 
					import rainbowStyles from '../styles/rainbow.module.css';
 | 
				
			||||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
 | 
					import DarkModeIcon from '@mui/icons-material/DarkMode';
 | 
				
			||||||
import LightModeIcon from '@mui/icons-material/LightMode';
 | 
					import LightModeIcon from '@mui/icons-material/LightMode';
 | 
				
			||||||
 | 
					import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
 | 
				
			||||||
import VisibilityIcon from "@mui/icons-material/Visibility";
 | 
					import VisibilityIcon from "@mui/icons-material/Visibility";
 | 
				
			||||||
import EditNoteIcon from "@mui/icons-material/EditNote";
 | 
					import EditNoteIcon from "@mui/icons-material/EditNote";
 | 
				
			||||||
import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
 | 
					import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
 | 
				
			||||||
@ -45,18 +47,34 @@ const TopControls: React.FC<TopControlsProps> = ({
 | 
				
			|||||||
  currentView,
 | 
					  currentView,
 | 
				
			||||||
  setCurrentView,
 | 
					  setCurrentView,
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
  const { colorScheme, toggleColorScheme } = useMantineColorScheme();
 | 
					  const { themeMode, isRainbowMode, isToggleDisabled, toggleTheme } = useRainbowThemeContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getThemeIcon = () => {
 | 
				
			||||||
 | 
					    if (isRainbowMode) return <AutoAwesomeIcon className={rainbowStyles.rainbowText} />;
 | 
				
			||||||
 | 
					    if (themeMode === "dark") return <LightModeIcon />;
 | 
				
			||||||
 | 
					    return <DarkModeIcon />;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className="absolute left-0 w-full top-0 z-[9999] pointer-events-none">
 | 
					    <div className="absolute left-0 w-full top-0 z-[100] pointer-events-none">
 | 
				
			||||||
      <div className="absolute left-4 top-1/2 -translate-y-1/2 pointer-events-auto flex gap-2 items-center">
 | 
					      <div className="absolute left-4 top-1/2 -translate-y-1/2 pointer-events-auto flex gap-2 items-center">
 | 
				
			||||||
        <Button
 | 
					        <Button
 | 
				
			||||||
          onClick={toggleColorScheme}
 | 
					          onClick={toggleTheme}
 | 
				
			||||||
          variant="subtle"
 | 
					          variant="subtle"
 | 
				
			||||||
          size="md"
 | 
					          size="md"
 | 
				
			||||||
          aria-label="Toggle theme"
 | 
					          aria-label="Toggle theme"
 | 
				
			||||||
 | 
					          disabled={isToggleDisabled}
 | 
				
			||||||
 | 
					          className={isRainbowMode ? rainbowStyles.rainbowButton : ''}
 | 
				
			||||||
 | 
					          title={
 | 
				
			||||||
 | 
					            isToggleDisabled
 | 
				
			||||||
 | 
					              ? "Button disabled for 3 seconds..."
 | 
				
			||||||
 | 
					              : isRainbowMode
 | 
				
			||||||
 | 
					                ? "Rainbow Mode Active! Click to exit"
 | 
				
			||||||
 | 
					                : "Toggle theme (click rapidly 6 times for a surprise!)"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          style={isToggleDisabled ? { opacity: 0.5, cursor: 'not-allowed' } : {}}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          {colorScheme === "dark" ? <LightModeIcon /> : <DarkModeIcon />}
 | 
					          {getThemeIcon()}
 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
        <LanguageSelector />
 | 
					        <LanguageSelector />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
@ -69,6 +87,7 @@ const TopControls: React.FC<TopControlsProps> = ({
 | 
				
			|||||||
            radius="xl"
 | 
					            radius="xl"
 | 
				
			||||||
            size="md"
 | 
					            size="md"
 | 
				
			||||||
            fullWidth
 | 
					            fullWidth
 | 
				
			||||||
 | 
					            className={isRainbowMode ? rainbowStyles.rainbowSegmentedControl : ''}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										200
									
								
								frontend/src/hooks/useRainbowTheme.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								frontend/src/hooks/useRainbowTheme.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,200 @@
 | 
				
			|||||||
 | 
					import { useState, useCallback, useRef, useEffect } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ThemeMode = 'light' | 'dark' | 'rainbow';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface RainbowThemeHook {
 | 
				
			||||||
 | 
					  themeMode: ThemeMode;
 | 
				
			||||||
 | 
					  isRainbowMode: boolean;
 | 
				
			||||||
 | 
					  isToggleDisabled: boolean;
 | 
				
			||||||
 | 
					  toggleTheme: () => void;
 | 
				
			||||||
 | 
					  activateRainbow: () => void;
 | 
				
			||||||
 | 
					  deactivateRainbow: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useRainbowTheme(initialTheme: 'light' | 'dark' = 'light'): RainbowThemeHook {
 | 
				
			||||||
 | 
					  // Get theme from localStorage or use initial
 | 
				
			||||||
 | 
					  const [themeMode, setThemeMode] = useState<ThemeMode>(() => {
 | 
				
			||||||
 | 
					    const stored = localStorage.getItem('stirling-theme');
 | 
				
			||||||
 | 
					    if (stored && ['light', 'dark', 'rainbow'].includes(stored)) {
 | 
				
			||||||
 | 
					      return stored as ThemeMode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return initialTheme;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Track rapid toggles for easter egg
 | 
				
			||||||
 | 
					  const toggleCount = useRef(0);
 | 
				
			||||||
 | 
					  const lastToggleTime = useRef(Date.now());
 | 
				
			||||||
 | 
					  const [isToggleDisabled, setIsToggleDisabled] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Save theme to localStorage whenever it changes
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    localStorage.setItem('stirling-theme', themeMode);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Apply rainbow class to body if in rainbow mode
 | 
				
			||||||
 | 
					    if (themeMode === 'rainbow') {
 | 
				
			||||||
 | 
					      document.body.classList.add('rainbow-mode-active');
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Show easter egg notification
 | 
				
			||||||
 | 
					      showRainbowNotification();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      document.body.classList.remove('rainbow-mode-active');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [themeMode]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const showRainbowNotification = () => {
 | 
				
			||||||
 | 
					    // Remove any existing notification
 | 
				
			||||||
 | 
					    const existingNotification = document.getElementById('rainbow-notification');
 | 
				
			||||||
 | 
					    if (existingNotification) {
 | 
				
			||||||
 | 
					      existingNotification.remove();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create and show rainbow notification
 | 
				
			||||||
 | 
					    const notification = document.createElement('div');
 | 
				
			||||||
 | 
					    notification.id = 'rainbow-notification';
 | 
				
			||||||
 | 
					    notification.innerHTML = '🌈 RAINBOW MODE ACTIVATED! 🌈<br><small>Button disabled for 3 seconds, then click to exit</small>';
 | 
				
			||||||
 | 
					    notification.style.cssText = `
 | 
				
			||||||
 | 
					      position: fixed;
 | 
				
			||||||
 | 
					      top: 20px;
 | 
				
			||||||
 | 
					      right: 20px;
 | 
				
			||||||
 | 
					      background: linear-gradient(45deg, #ff0000, #ff8800, #ffff00, #88ff00, #00ff88, #00ffff, #0088ff, #8800ff);
 | 
				
			||||||
 | 
					      background-size: 300% 300%;
 | 
				
			||||||
 | 
					      animation: rainbowBackground 1s ease infinite;
 | 
				
			||||||
 | 
					      color: white;
 | 
				
			||||||
 | 
					      padding: 15px 20px;
 | 
				
			||||||
 | 
					      border-radius: 25px;
 | 
				
			||||||
 | 
					      font-weight: bold;
 | 
				
			||||||
 | 
					      font-size: 16px;
 | 
				
			||||||
 | 
					      z-index: 1000;
 | 
				
			||||||
 | 
					      border: 2px solid white;
 | 
				
			||||||
 | 
					      box-shadow: 0 0 20px rgba(255, 255, 255, 0.8);
 | 
				
			||||||
 | 
					      user-select: none;
 | 
				
			||||||
 | 
					      pointer-events: none;
 | 
				
			||||||
 | 
					      transition: opacity 0.3s ease;
 | 
				
			||||||
 | 
					    `;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    document.body.appendChild(notification);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Auto-remove notification after 3 seconds
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					      if (notification) {
 | 
				
			||||||
 | 
					        notification.style.opacity = '0';
 | 
				
			||||||
 | 
					        setTimeout(() => {
 | 
				
			||||||
 | 
					          if (notification.parentNode) {
 | 
				
			||||||
 | 
					            notification.parentNode.removeChild(notification);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }, 300);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }, 3000);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const showExitNotification = () => {
 | 
				
			||||||
 | 
					    // Remove any existing notification
 | 
				
			||||||
 | 
					    const existingNotification = document.getElementById('rainbow-exit-notification');
 | 
				
			||||||
 | 
					    if (existingNotification) {
 | 
				
			||||||
 | 
					      existingNotification.remove();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create and show exit notification
 | 
				
			||||||
 | 
					    const notification = document.createElement('div');
 | 
				
			||||||
 | 
					    notification.id = 'rainbow-exit-notification';
 | 
				
			||||||
 | 
					    notification.innerHTML = '🌙 Rainbow mode deactivated';
 | 
				
			||||||
 | 
					    notification.style.cssText = `
 | 
				
			||||||
 | 
					      position: fixed;
 | 
				
			||||||
 | 
					      top: 20px;
 | 
				
			||||||
 | 
					      right: 20px;
 | 
				
			||||||
 | 
					      background: linear-gradient(45deg, #333, #666);
 | 
				
			||||||
 | 
					      color: white;
 | 
				
			||||||
 | 
					      padding: 15px 20px;
 | 
				
			||||||
 | 
					      border-radius: 25px;
 | 
				
			||||||
 | 
					      font-weight: bold;
 | 
				
			||||||
 | 
					      font-size: 16px;
 | 
				
			||||||
 | 
					      z-index: 1000;
 | 
				
			||||||
 | 
					      border: 2px solid #999;
 | 
				
			||||||
 | 
					      box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
 | 
				
			||||||
 | 
					      user-select: none;
 | 
				
			||||||
 | 
					      pointer-events: none;
 | 
				
			||||||
 | 
					      transition: opacity 0.3s ease;
 | 
				
			||||||
 | 
					    `;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    document.body.appendChild(notification);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Auto-remove notification after 2 seconds
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					      if (notification) {
 | 
				
			||||||
 | 
					        notification.style.opacity = '0';
 | 
				
			||||||
 | 
					        setTimeout(() => {
 | 
				
			||||||
 | 
					          if (notification.parentNode) {
 | 
				
			||||||
 | 
					            notification.parentNode.removeChild(notification);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }, 300);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }, 2000);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const toggleTheme = useCallback(() => {
 | 
				
			||||||
 | 
					    // Don't allow toggle if disabled
 | 
				
			||||||
 | 
					    if (isToggleDisabled) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const currentTime = Date.now();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Simple exit from rainbow mode with single click (after cooldown period)
 | 
				
			||||||
 | 
					    if (themeMode === 'rainbow') {
 | 
				
			||||||
 | 
					      setThemeMode('light');
 | 
				
			||||||
 | 
					      console.log('🌈 Rainbow mode deactivated. Thanks for trying it!');
 | 
				
			||||||
 | 
					      showExitNotification();
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Reset counter if too much time has passed (2.5 seconds)
 | 
				
			||||||
 | 
					    if (currentTime - lastToggleTime.current > 2500) {
 | 
				
			||||||
 | 
					      toggleCount.current = 1;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      toggleCount.current++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    lastToggleTime.current = currentTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Easter egg: Activate rainbow mode after 6 rapid toggles
 | 
				
			||||||
 | 
					    if (toggleCount.current >= 6) {
 | 
				
			||||||
 | 
					      setThemeMode('rainbow');
 | 
				
			||||||
 | 
					      console.log('🌈 RAINBOW MODE ACTIVATED! 🌈 You found the secret easter egg!');
 | 
				
			||||||
 | 
					      console.log('🌈 Button will be disabled for 3 seconds, then click once to exit!');
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Disable toggle for 3 seconds
 | 
				
			||||||
 | 
					      setIsToggleDisabled(true);
 | 
				
			||||||
 | 
					      setTimeout(() => {
 | 
				
			||||||
 | 
					        setIsToggleDisabled(false);
 | 
				
			||||||
 | 
					        console.log('🌈 Theme toggle re-enabled! Click once to exit rainbow mode.');
 | 
				
			||||||
 | 
					      }, 3000);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Reset counter
 | 
				
			||||||
 | 
					      toggleCount.current = 0;
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Normal theme switching
 | 
				
			||||||
 | 
					    setThemeMode(prevMode => prevMode === 'light' ? 'dark' : 'light');
 | 
				
			||||||
 | 
					  }, [themeMode, isToggleDisabled]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const activateRainbow = useCallback(() => {
 | 
				
			||||||
 | 
					    setThemeMode('rainbow');
 | 
				
			||||||
 | 
					    console.log('🌈 Rainbow mode manually activated!');
 | 
				
			||||||
 | 
					  }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const deactivateRainbow = useCallback(() => {
 | 
				
			||||||
 | 
					    if (themeMode === 'rainbow') {
 | 
				
			||||||
 | 
					      setThemeMode('light');
 | 
				
			||||||
 | 
					      console.log('🌈 Rainbow mode manually deactivated.');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [themeMode]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    themeMode,
 | 
				
			||||||
 | 
					    isRainbowMode: themeMode === 'rainbow',
 | 
				
			||||||
 | 
					    isToggleDisabled,
 | 
				
			||||||
 | 
					    toggleTheme,
 | 
				
			||||||
 | 
					    activateRainbow,
 | 
				
			||||||
 | 
					    deactivateRainbow,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -2,6 +2,7 @@
 | 
				
			|||||||
@tailwind components;
 | 
					@tailwind components;
 | 
				
			||||||
@tailwind utilities;
 | 
					@tailwind utilities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
body {
 | 
					body {
 | 
				
			||||||
  margin: 0;
 | 
					  margin: 0;
 | 
				
			||||||
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
 | 
					  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
 | 
				
			||||||
@ -11,6 +12,7 @@ body {
 | 
				
			|||||||
  -moz-osx-font-smoothing: grayscale;
 | 
					  -moz-osx-font-smoothing: grayscale;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
code {
 | 
					code {
 | 
				
			||||||
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
 | 
					  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
 | 
				
			||||||
    monospace;
 | 
					    monospace;
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,9 @@ import { useToolParams } from "../hooks/useToolParams";
 | 
				
			|||||||
import AddToPhotosIcon from "@mui/icons-material/AddToPhotos";
 | 
					import AddToPhotosIcon from "@mui/icons-material/AddToPhotos";
 | 
				
			||||||
import ContentCutIcon from "@mui/icons-material/ContentCut";
 | 
					import ContentCutIcon from "@mui/icons-material/ContentCut";
 | 
				
			||||||
import ZoomInMapIcon from "@mui/icons-material/ZoomInMap";
 | 
					import ZoomInMapIcon from "@mui/icons-material/ZoomInMap";
 | 
				
			||||||
import { Group, Paper, Box, Button, useMantineTheme, useMantineColorScheme } from "@mantine/core";
 | 
					import { Group, Paper, Box, Button, useMantineTheme } from "@mantine/core";
 | 
				
			||||||
 | 
					import { useRainbowThemeContext } from "../components/RainbowThemeProvider";
 | 
				
			||||||
 | 
					import rainbowStyles from '../styles/rainbow.module.css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ToolPicker from "../components/ToolPicker";
 | 
					import ToolPicker from "../components/ToolPicker";
 | 
				
			||||||
import FileManager from "../components/FileManager";
 | 
					import FileManager from "../components/FileManager";
 | 
				
			||||||
@ -41,7 +43,7 @@ export default function HomePage() {
 | 
				
			|||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
  const [searchParams] = useSearchParams();
 | 
					  const [searchParams] = useSearchParams();
 | 
				
			||||||
  const theme = useMantineTheme();
 | 
					  const theme = useMantineTheme();
 | 
				
			||||||
  const { colorScheme, toggleColorScheme } = useMantineColorScheme();
 | 
					  const { isRainbowMode } = useRainbowThemeContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Core app state
 | 
					  // Core app state
 | 
				
			||||||
  const [selectedToolKey, setSelectedToolKey] = useState<string>(searchParams.get("t") || "split");
 | 
					  const [selectedToolKey, setSelectedToolKey] = useState<string>(searchParams.get("t") || "split");
 | 
				
			||||||
@ -81,7 +83,7 @@ export default function HomePage() {
 | 
				
			|||||||
      {/* Left: Tool Picker */}
 | 
					      {/* Left: Tool Picker */}
 | 
				
			||||||
      {sidebarsVisible && (
 | 
					      {sidebarsVisible && (
 | 
				
			||||||
        <div
 | 
					        <div
 | 
				
			||||||
          className="h-screen z-sticky flex flex-col bg-surface border-r border-border min-w-[180px] max-w-[240px] w-[16vw]"
 | 
					          className={`h-screen z-sticky flex flex-col bg-surface border-r border-border min-w-[180px] max-w-[240px] w-[16vw] ${isRainbowMode ? rainbowStyles.rainbowPaper : ''}`}
 | 
				
			||||||
          style={{ padding: '1rem' }}
 | 
					          style={{ padding: '1rem' }}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <ToolPicker
 | 
					          <ToolPicker
 | 
				
			||||||
@ -136,7 +138,7 @@ export default function HomePage() {
 | 
				
			|||||||
      {/* Right: Tool Interaction */}
 | 
					      {/* Right: Tool Interaction */}
 | 
				
			||||||
      {sidebarsVisible && (
 | 
					      {sidebarsVisible && (
 | 
				
			||||||
        <div
 | 
					        <div
 | 
				
			||||||
          className="h-screen bg-surface border-l border-border gap-6 z-sticky flex flex-col min-w-[260px] max-w-[400px] w-[22vw]"
 | 
					          className={`h-screen bg-surface border-l border-border gap-6 z-sticky flex flex-col min-w-[260px] max-w-[400px] w-[22vw] ${isRainbowMode ? rainbowStyles.rainbowPaper : ''}`}
 | 
				
			||||||
          style={{ padding: '1.5rem' }}
 | 
					          style={{ padding: '1.5rem' }}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <ToolRenderer
 | 
					          <ToolRenderer
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										202
									
								
								frontend/src/styles/rainbow.module.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								frontend/src/styles/rainbow.module.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,202 @@
 | 
				
			|||||||
 | 
					/* Rainbow Mode Styles - Easter Egg! */
 | 
				
			||||||
 | 
					@keyframes rainbowBackground {
 | 
				
			||||||
 | 
					  0% { background-position: 0% 50%; }
 | 
				
			||||||
 | 
					  50% { background-position: 100% 50%; }
 | 
				
			||||||
 | 
					  100% { background-position: 0% 50%; }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes rainbowBorder {
 | 
				
			||||||
 | 
					  0% { border-color: #ff0000; box-shadow: 0 0 15px #ff0000; }
 | 
				
			||||||
 | 
					  14% { border-color: #ff8800; box-shadow: 0 0 15px #ff8800; }
 | 
				
			||||||
 | 
					  28% { border-color: #ffff00; box-shadow: 0 0 15px #ffff00; }
 | 
				
			||||||
 | 
					  42% { border-color: #88ff00; box-shadow: 0 0 15px #88ff00; }
 | 
				
			||||||
 | 
					  57% { border-color: #00ff88; box-shadow: 0 0 15px #00ff88; }
 | 
				
			||||||
 | 
					  71% { border-color: #0088ff; box-shadow: 0 0 15px #0088ff; }
 | 
				
			||||||
 | 
					  85% { border-color: #8800ff; box-shadow: 0 0 15px #8800ff; }
 | 
				
			||||||
 | 
					  100% { border-color: #ff0000; box-shadow: 0 0 15px #ff0000; }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes rainbowText {
 | 
				
			||||||
 | 
					  0% { color: #ff0000; text-shadow: 0 0 10px #ff0000; }
 | 
				
			||||||
 | 
					  14% { color: #ff8800; text-shadow: 0 0 10px #ff8800; }
 | 
				
			||||||
 | 
					  28% { color: #ffff00; text-shadow: 0 0 10px #ffff00; }
 | 
				
			||||||
 | 
					  42% { color: #88ff00; text-shadow: 0 0 10px #88ff00; }
 | 
				
			||||||
 | 
					  57% { color: #00ff88; text-shadow: 0 0 10px #00ff88; }
 | 
				
			||||||
 | 
					  71% { color: #0088ff; text-shadow: 0 0 10px #0088ff; }
 | 
				
			||||||
 | 
					  85% { color: #8800ff; text-shadow: 0 0 10px #8800ff; }
 | 
				
			||||||
 | 
					  100% { color: #ff0000; text-shadow: 0 0 10px #ff0000; }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes rainbowPulse {
 | 
				
			||||||
 | 
					  0%, 100% { transform: scale(1); }
 | 
				
			||||||
 | 
					  50% { transform: scale(1.05); }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Main rainbow theme class */
 | 
				
			||||||
 | 
					.rainbowMode {
 | 
				
			||||||
 | 
					  background: linear-gradient(
 | 
				
			||||||
 | 
					    -45deg,
 | 
				
			||||||
 | 
					    #ff0000, #ff8800, #ffff00, #88ff00, #00ff88, #00ffff, #0088ff, #8800ff, #ff0088, #ff0000
 | 
				
			||||||
 | 
					  ) !important;
 | 
				
			||||||
 | 
					  background-size: 400% 400% !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 3s ease infinite !important;
 | 
				
			||||||
 | 
					  color: white !important;
 | 
				
			||||||
 | 
					  overflow-x: hidden;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Rainbow components */
 | 
				
			||||||
 | 
					.rainbowCard {
 | 
				
			||||||
 | 
					  background: linear-gradient(
 | 
				
			||||||
 | 
					    45deg,
 | 
				
			||||||
 | 
					    #ff0000, #ff8800, #ffff00, #88ff00, #00ff88, #00ffff, #0088ff, #8800ff, #ff0088
 | 
				
			||||||
 | 
					  ) !important;
 | 
				
			||||||
 | 
					  background-size: 400% 400% !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 4s ease infinite, rainbowBorder 2s linear infinite !important;
 | 
				
			||||||
 | 
					  color: white !important;
 | 
				
			||||||
 | 
					  border: 2px solid !important;
 | 
				
			||||||
 | 
					  border-radius: 15px !important;
 | 
				
			||||||
 | 
					  box-shadow: 0 0 20px rgba(255, 255, 255, 0.3) !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.rainbowButton {
 | 
				
			||||||
 | 
					  background: linear-gradient(
 | 
				
			||||||
 | 
					    45deg,
 | 
				
			||||||
 | 
					    #ff0000, #ff8800, #ffff00, #88ff00, #00ff88, #00ffff, #0088ff, #8800ff, #ff0088
 | 
				
			||||||
 | 
					  ) !important;
 | 
				
			||||||
 | 
					  background-size: 300% 300% !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 2s ease infinite, rainbowBorder 1s linear infinite !important;
 | 
				
			||||||
 | 
					  border: 2px solid !important;
 | 
				
			||||||
 | 
					  color: white !important;
 | 
				
			||||||
 | 
					  border-radius: 8px !important;
 | 
				
			||||||
 | 
					  transition: all 0.3s ease !important;
 | 
				
			||||||
 | 
					  font-weight: bold !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.rainbowButton:hover {
 | 
				
			||||||
 | 
					  transform: scale(1.05) !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 1s ease infinite, rainbowBorder 0.5s linear infinite, rainbowPulse 0.5s ease infinite !important;
 | 
				
			||||||
 | 
					  box-shadow: 0 0 25px rgba(255, 255, 255, 0.6) !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.rainbowInput {
 | 
				
			||||||
 | 
					  background: linear-gradient(
 | 
				
			||||||
 | 
					    90deg,
 | 
				
			||||||
 | 
					    #ff0000, #ff8800, #ffff00, #88ff00, #00ff88, #00ffff, #0088ff, #8800ff, #ff0088
 | 
				
			||||||
 | 
					  ) !important;
 | 
				
			||||||
 | 
					  background-size: 300% 300% !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 2.5s ease infinite, rainbowBorder 1.5s linear infinite !important;
 | 
				
			||||||
 | 
					  border: 2px solid !important;
 | 
				
			||||||
 | 
					  color: white !important;
 | 
				
			||||||
 | 
					  border-radius: 8px !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.rainbowInput::placeholder {
 | 
				
			||||||
 | 
					  color: rgba(255, 255, 255, 0.8) !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.rainbowText {
 | 
				
			||||||
 | 
					  animation: rainbowText 3s linear infinite !important;
 | 
				
			||||||
 | 
					  font-weight: bold !important;
 | 
				
			||||||
 | 
					  text-shadow: 0 0 10px currentColor !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.rainbowSegmentedControl {
 | 
				
			||||||
 | 
					  background: linear-gradient(
 | 
				
			||||||
 | 
					    45deg,
 | 
				
			||||||
 | 
					    #ff0000, #ff8800, #ffff00, #88ff00, #00ff88, #00ffff, #0088ff, #8800ff, #ff0088
 | 
				
			||||||
 | 
					  ) !important;
 | 
				
			||||||
 | 
					  background-size: 400% 400% !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 3s ease infinite, rainbowBorder 2s linear infinite !important;
 | 
				
			||||||
 | 
					  border: 2px solid !important;
 | 
				
			||||||
 | 
					  border-radius: 12px !important;
 | 
				
			||||||
 | 
					  padding: 4px !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.rainbowPaper {
 | 
				
			||||||
 | 
					  background: linear-gradient(
 | 
				
			||||||
 | 
					    90deg,
 | 
				
			||||||
 | 
					  #00ffff, #0088ff, #8800ff, #ff0088
 | 
				
			||||||
 | 
					  ) !important;
 | 
				
			||||||
 | 
					  background-size: 100% 100% !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 3.5s ease infinite, rainbowBorder 2s linear infinite !important;
 | 
				
			||||||
 | 
					  border: 2px solid !important;
 | 
				
			||||||
 | 
					  color: white !important;
 | 
				
			||||||
 | 
					  border-radius: 12px !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Easter egg notification */
 | 
				
			||||||
 | 
					.rainbowNotification {
 | 
				
			||||||
 | 
					  position: fixed !important;
 | 
				
			||||||
 | 
					  top: 20px !important;
 | 
				
			||||||
 | 
					  right: 20px !important;
 | 
				
			||||||
 | 
					  background: linear-gradient(45deg,#ffff00, #88ff00, #00ff88, #00ffff) !important;
 | 
				
			||||||
 | 
					  background-size: 300% 300% !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 1s ease infinite, rainbowBorder 0.5s linear infinite !important;
 | 
				
			||||||
 | 
					  color: white !important;
 | 
				
			||||||
 | 
					  padding: 15px 20px !important;
 | 
				
			||||||
 | 
					  border-radius: 25px !important;
 | 
				
			||||||
 | 
					  font-weight: bold !important;
 | 
				
			||||||
 | 
					  font-size: 16px !important;
 | 
				
			||||||
 | 
					  z-index: 10000 !important;
 | 
				
			||||||
 | 
					  border: 2px solid white !important;
 | 
				
			||||||
 | 
					  box-shadow: 0 0 20px rgba(255, 255, 255, 0.8) !important;
 | 
				
			||||||
 | 
					  user-select: none !important;
 | 
				
			||||||
 | 
					  pointer-events: none !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Specific component overrides */
 | 
				
			||||||
 | 
					.rainbowMode [data-mantine-color-scheme] {
 | 
				
			||||||
 | 
					  background: linear-gradient(
 | 
				
			||||||
 | 
					    -45deg,
 | 
				
			||||||
 | 
					    #ff0000, #ff8800, #ffff00, #88ff00, #00ff88, #00ffff, #0088ff, #8800ff, #ff0088, #ff0000
 | 
				
			||||||
 | 
					  ) !important;
 | 
				
			||||||
 | 
					  background-size: 400% 400% !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 3s ease infinite !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Make all buttons rainbow in rainbow mode */
 | 
				
			||||||
 | 
					.rainbowMode button {
 | 
				
			||||||
 | 
					  background: linear-gradient(
 | 
				
			||||||
 | 
					    45deg,
 | 
				
			||||||
 | 
					     #ffff00, #88ff00, #00ff88, #00ffff
 | 
				
			||||||
 | 
					  ) !important;
 | 
				
			||||||
 | 
					  background-size: 100% 100% !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 2s ease infinite !important;
 | 
				
			||||||
 | 
					  border: 2px solid !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 2s ease infinite, rainbowBorder 1.5s linear infinite !important;
 | 
				
			||||||
 | 
					  color: white !important;
 | 
				
			||||||
 | 
					  font-weight: bold !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Make all inputs rainbow in rainbow mode */
 | 
				
			||||||
 | 
					.rainbowMode input,
 | 
				
			||||||
 | 
					.rainbowMode select,
 | 
				
			||||||
 | 
					.rainbowMode textarea {
 | 
				
			||||||
 | 
					  background: linear-gradient(
 | 
				
			||||||
 | 
					    90deg,
 | 
				
			||||||
 | 
					    #ffff00, #88ff00, #00ff88, #00ffff
 | 
				
			||||||
 | 
					  ) !important;
 | 
				
			||||||
 | 
					  background-size: 100% 100% !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 2.5s ease infinite !important;
 | 
				
			||||||
 | 
					  border: 2px solid !important;
 | 
				
			||||||
 | 
					  animation: rainbowBackground 2.5s ease infinite, rainbowBorder 1.5s linear infinite !important;
 | 
				
			||||||
 | 
					  color: white !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Rainbow text class */
 | 
				
			||||||
 | 
					.rainbowText {
 | 
				
			||||||
 | 
					  animation: rainbowText 3s linear infinite !important;
 | 
				
			||||||
 | 
					  font-weight: bold !important;
 | 
				
			||||||
 | 
					  text-shadow: 0 0 10px currentColor !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Make all text rainbow */
 | 
				
			||||||
 | 
					.rainbowMode h1,
 | 
				
			||||||
 | 
					.rainbowMode h2,
 | 
				
			||||||
 | 
					.rainbowMode h3,
 | 
				
			||||||
 | 
					.rainbowMode h4,
 | 
				
			||||||
 | 
					.rainbowMode h5,
 | 
				
			||||||
 | 
					.rainbowMode h6 {
 | 
				
			||||||
 | 
					  animation: rainbowText 3s linear infinite !important;
 | 
				
			||||||
 | 
					  font-weight: bold !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user