From 2a21e7af47920bf426721999a669a2d2a736e9a8 Mon Sep 17 00:00:00 2001
From: ghaddaditw <40211818-ghaddaditw@users.noreply.replit.com>
Date: Sun, 8 Jun 2025 07:42:17 +0000
Subject: [PATCH] Create a user interface that changes colors according to mood
and time
Implement AdaptiveThemeContext, MoodDetector component, and ThemeControlPanel.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: c5f0c281-8dd8-4846-b452-4a07bcd21062
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9777c70b-fc38-4831-8d6b-78dfffe041b0/0db9096f-6d35-463f-813a-3a8325a4d8fa.jpg
---
client/src/App.tsx | 27 +-
client/src/components/MoodDetector.tsx | 146 +++++++++
client/src/components/ThemeControlPanel.tsx | 340 ++++++++++++++++++++
client/src/context/AdaptiveThemeContext.tsx | 338 +++++++++++++++++++
4 files changed, 840 insertions(+), 11 deletions(-)
create mode 100644 client/src/components/MoodDetector.tsx
create mode 100644 client/src/components/ThemeControlPanel.tsx
create mode 100644 client/src/context/AdaptiveThemeContext.tsx
diff --git a/client/src/App.tsx b/client/src/App.tsx
index aebbe23..f5df463 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -6,9 +6,11 @@ import { TooltipProvider } from "@/components/ui/tooltip";
import { FloatingVoiceControl } from "@/components/voice/FloatingVoiceControl";
import { AuthProvider } from "@/context/AuthContext";
import { ThemeProvider } from "@/context/ThemeContext";
+import { AdaptiveThemeProvider } from "@/context/AdaptiveThemeContext";
import { VoiceProvider } from "@/context/VoiceProvider";
import { NotificationProvider } from "@/context/NotificationProvider";
import { TourManagerProvider } from "@/components/onboarding/TourManager";
+import { MoodDetector } from "@/components/MoodDetector";
import LandingPage from "@/pages/LandingPage";
import DashboardPage from "@/pages/DashboardPage";
import LoginPage from "@/pages/LoginPage";
@@ -51,17 +53,20 @@ function App() {
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/components/MoodDetector.tsx b/client/src/components/MoodDetector.tsx
new file mode 100644
index 0000000..d6ccf80
--- /dev/null
+++ b/client/src/components/MoodDetector.tsx
@@ -0,0 +1,146 @@
+import React, { useEffect, useCallback } from 'react';
+import { useAdaptiveTheme } from '@/context/AdaptiveThemeContext';
+import { useAuth } from '@/hooks/useAuth';
+
+interface ActivityMetrics {
+ tasksCompleted: number;
+ focusSessionsCompleted: number;
+ timeSpentInApp: number;
+ clickFrequency: number;
+ typingSpeed: number;
+ pausesBetweenActions: number;
+}
+
+export function MoodDetector() {
+ const { updateMood, isAdaptiveMode } = useAdaptiveTheme();
+ const { user } = useAuth();
+
+ const analyzeMoodFromActivity = useCallback((metrics: ActivityMetrics) => {
+ let energy = 50;
+ let focus = 50;
+ let stress = 50;
+ let creativity = 50;
+
+ // Energy level indicators
+ if (metrics.clickFrequency > 0.8) energy += 20;
+ if (metrics.typingSpeed > 40) energy += 15;
+ if (metrics.timeSpentInApp > 2) energy += 10;
+ if (metrics.tasksCompleted > 3) energy += 15;
+
+ // Focus level indicators
+ if (metrics.focusSessionsCompleted > 0) focus += 25;
+ if (metrics.pausesBetweenActions < 2) focus += 15;
+ if (metrics.timeSpentInApp > 1 && metrics.clickFrequency < 0.5) focus += 20;
+
+ // Stress level indicators
+ if (metrics.clickFrequency > 1.2) stress += 25;
+ if (metrics.pausesBetweenActions > 5) stress += 15;
+ if (metrics.typingSpeed > 60) stress += 10;
+ if (metrics.tasksCompleted === 0 && metrics.timeSpentInApp > 1) stress += 20;
+
+ // Creativity level indicators
+ const currentHour = new Date().getHours();
+ if (currentHour >= 18 && currentHour <= 22) creativity += 15; // Evening creativity boost
+ if (metrics.pausesBetweenActions > 3 && metrics.pausesBetweenActions < 6) creativity += 10; // Thoughtful pauses
+ if (metrics.tasksCompleted > 0 && metrics.focusSessionsCompleted > 0) creativity += 20;
+
+ // Normalize values to 0-100 range
+ energy = Math.max(0, Math.min(100, energy));
+ focus = Math.max(0, Math.min(100, focus));
+ stress = Math.max(0, Math.min(100, stress));
+ creativity = Math.max(0, Math.min(100, creativity));
+
+ return { energy, focus, stress, creativity };
+ }, []);
+
+ const trackUserActivity = useCallback(() => {
+ if (!isAdaptiveMode || !user) return;
+
+ // Initialize activity tracking
+ let clickCount = 0;
+ let keystrokes = 0;
+ let lastActivity = Date.now();
+ let sessionStart = Date.now();
+ let pauseCount = 0;
+ let totalPauseTime = 0;
+
+ // Track mouse clicks
+ const handleClick = () => {
+ clickCount++;
+ const now = Date.now();
+ if (now - lastActivity > 3000) { // 3 second pause
+ pauseCount++;
+ totalPauseTime += now - lastActivity;
+ }
+ lastActivity = now;
+ };
+
+ // Track keyboard activity
+ const handleKeydown = () => {
+ keystrokes++;
+ const now = Date.now();
+ if (now - lastActivity > 3000) {
+ pauseCount++;
+ totalPauseTime += now - lastActivity;
+ }
+ lastActivity = now;
+ };
+
+ // Track mouse movement for engagement
+ const handleMouseMove = () => {
+ lastActivity = Date.now();
+ };
+
+ // Add event listeners
+ document.addEventListener('click', handleClick);
+ document.addEventListener('keydown', handleKeydown);
+ document.addEventListener('mousemove', handleMouseMove);
+
+ // Analyze activity every 5 minutes
+ const analysisInterval = setInterval(() => {
+ const sessionDuration = (Date.now() - sessionStart) / 1000 / 60; // minutes
+ const clickFrequency = clickCount / sessionDuration;
+ const typingSpeed = keystrokes / sessionDuration;
+ const avgPauseTime = pauseCount > 0 ? totalPauseTime / pauseCount / 1000 : 0;
+
+ // Get task completion data from localStorage (simplified)
+ const tasksCompleted = parseInt(localStorage.getItem('dailyTasksCompleted') || '0');
+ const focusSessionsCompleted = parseInt(localStorage.getItem('dailyFocusSessions') || '0');
+
+ const metrics: ActivityMetrics = {
+ tasksCompleted,
+ focusSessionsCompleted,
+ timeSpentInApp: sessionDuration,
+ clickFrequency,
+ typingSpeed,
+ pausesBetweenActions: avgPauseTime,
+ };
+
+ const detectedMood = analyzeMoodFromActivity(metrics);
+ updateMood(detectedMood);
+
+ // Reset counters for next analysis period
+ clickCount = 0;
+ keystrokes = 0;
+ pauseCount = 0;
+ totalPauseTime = 0;
+ sessionStart = Date.now();
+ }, 5 * 60 * 1000); // 5 minutes
+
+ // Cleanup function
+ return () => {
+ document.removeEventListener('click', handleClick);
+ document.removeEventListener('keydown', handleKeydown);
+ document.removeEventListener('mousemove', handleMouseMove);
+ clearInterval(analysisInterval);
+ };
+ }, [isAdaptiveMode, user, analyzeMoodFromActivity, updateMood]);
+
+ useEffect(() => {
+ const cleanup = trackUserActivity();
+ return cleanup;
+ }, [trackUserActivity]);
+
+ // This component doesn't render anything visible
+ return null;
+}
\ No newline at end of file
diff --git a/client/src/components/ThemeControlPanel.tsx b/client/src/components/ThemeControlPanel.tsx
new file mode 100644
index 0000000..55debf8
--- /dev/null
+++ b/client/src/components/ThemeControlPanel.tsx
@@ -0,0 +1,340 @@
+import React from 'react';
+import { useAdaptiveTheme } from '@/context/AdaptiveThemeContext';
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
+import { Button } from '@/components/ui/button';
+import { Switch } from '@/components/ui/switch';
+import { Label } from '@/components/ui/label';
+import { Progress } from '@/components/ui/progress';
+import { Slider } from '@/components/ui/slider';
+import { Badge } from '@/components/ui/badge';
+import { Separator } from '@/components/ui/separator';
+import { useTranslation } from 'react-i18next';
+import {
+ Palette,
+ Brain,
+ Zap,
+ Target,
+ AlertTriangle,
+ Sparkles,
+ Clock,
+ Sun,
+ Moon,
+ Sunset,
+ Sunrise
+} from 'lucide-react';
+
+export function ThemeControlPanel() {
+ const {
+ currentTheme,
+ mood,
+ updateMood,
+ timeOfDay,
+ isAdaptiveMode,
+ toggleAdaptiveMode,
+ manualOverride,
+ setManualOverride,
+ availableThemes,
+ } = useAdaptiveTheme();
+
+ const { t } = useTranslation();
+
+ const getTimeIcon = (time: string) => {
+ switch (time) {
+ case 'morning': return ;
+ case 'midday': return ;
+ case 'afternoon': return ;
+ case 'evening': return ;
+ case 'night': return ;
+ default: return ;
+ }
+ };
+
+ const getMoodColor = (value: number) => {
+ if (value >= 80) return 'text-green-500';
+ if (value >= 60) return 'text-blue-500';
+ if (value >= 40) return 'text-yellow-500';
+ return 'text-red-500';
+ };
+
+ const handleThemeSelect = (themeName: string) => {
+ if (manualOverride === themeName) {
+ setManualOverride(null);
+ } else {
+ setManualOverride(themeName);
+ }
+ };
+
+ return (
+
+ {/* Header */}
+
+
+
+
{t('theme.adaptiveTitle', 'Adaptive Theme Control')}
+
+
+ {getTimeIcon(timeOfDay)}
+ {timeOfDay}
+
+
+
+ {/* Adaptive Mode Toggle */}
+
+
+
+
+ {t('theme.adaptiveMode', 'Adaptive Mode')}
+
+
+ {t('theme.adaptiveModeDesc', 'Automatically adjust colors based on your mood and time of day')}
+
+
+
+
+
+
+
+
+
+
+ {/* Current Mood Display */}
+ {isAdaptiveMode && (
+
+
+
+
+ {t('theme.currentMood', 'Current Mood Analysis')}
+
+
+ {t('theme.moodDesc', 'Detected from your activity patterns and time of day')}
+
+
+
+
+
+
+
+
+
+
+
+ {mood.energy}%
+
+
+
+
+
+
+
+
+
+
+
+
+ {mood.focus}%
+
+
+
+
+
+
+
+
+
+
+
+
+ {mood.stress}%
+
+
+
+
+
+
+
+
+
+
+
+
+ {mood.creativity}%
+
+
+
+
+
+
+
+ )}
+
+ {/* Manual Mood Adjustment */}
+ {isAdaptiveMode && (
+
+
+ {t('theme.manualAdjustment', 'Manual Mood Adjustment')}
+
+ {t('theme.manualAdjustmentDesc', 'Override automatic detection with manual settings')}
+
+
+
+
+
+
+ updateMood({ energy: value[0] || 0 })}
+ max={100}
+ step={1}
+ className="w-full"
+ />
+
+
+
+
+ updateMood({ focus: value[0] })}
+ max={100}
+ step={1}
+ className="w-full"
+ />
+
+
+
+
+
updateMood({ stress: value[0] })}
+ max={100}
+ step={1}
+ className="w-full"
+ />
+
+
+
+
+ updateMood({ creativity: value[0] })}
+ max={100}
+ step={1}
+ className="w-full"
+ />
+
+
+
+
+ )}
+
+
+
+ {/* Theme Presets */}
+
+
+ {t('theme.presets', 'Theme Presets')}
+
+ {t('theme.presetsDesc', 'Override adaptive mode with predefined themes')}
+
+
+
+
+ {Object.entries(availableThemes).map(([name, theme]) => (
+
+ ))}
+
+ {manualOverride && (
+
+ )}
+
+
+
+ {/* Current Theme Preview */}
+
+
+ {t('theme.currentTheme', 'Current Theme')}
+
+ {manualOverride
+ ? t('theme.manualTheme', `Manual: ${manualOverride}`)
+ : t('theme.adaptiveTheme', 'Adaptive based on mood and time')
+ }
+
+
+
+
+
+
+
{t('theme.primary', 'Primary')}
+
+
+
+
{t('theme.secondary', 'Secondary')}
+
+
+
+
{t('theme.accent', 'Accent')}
+
+
+
+
{t('theme.background', 'Background')}
+
+
+
+
{t('theme.foreground', 'Foreground')}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/client/src/context/AdaptiveThemeContext.tsx b/client/src/context/AdaptiveThemeContext.tsx
new file mode 100644
index 0000000..c3263d9
--- /dev/null
+++ b/client/src/context/AdaptiveThemeContext.tsx
@@ -0,0 +1,338 @@
+import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
+import { useAuth } from '@/hooks/useAuth';
+
+interface MoodState {
+ energy: number; // 0-100
+ focus: number; // 0-100
+ stress: number; // 0-100
+ creativity: number; // 0-100
+}
+
+interface TimeOfDayTheme {
+ morning: string;
+ midday: string;
+ afternoon: string;
+ evening: string;
+ night: string;
+}
+
+interface AdaptiveThemeColors {
+ primary: string;
+ secondary: string;
+ accent: string;
+ background: string;
+ foreground: string;
+ muted: string;
+ mutedForeground: string;
+ border: string;
+ card: string;
+ cardForeground: string;
+}
+
+interface AdaptiveThemeContextType {
+ currentTheme: AdaptiveThemeColors;
+ mood: MoodState;
+ updateMood: (newMood: Partial) => void;
+ timeOfDay: string;
+ isAdaptiveMode: boolean;
+ toggleAdaptiveMode: () => void;
+ manualOverride: string | null;
+ setManualOverride: (theme: string | null) => void;
+ availableThemes: Record;
+ generateThemeFromMood: (mood: MoodState, timeOfDay: string) => AdaptiveThemeColors;
+}
+
+const AdaptiveThemeContext = createContext(undefined);
+
+// Predefined theme palettes for different moods and times
+const THEME_PALETTES = {
+ energetic: {
+ primary: 'hsl(12, 76%, 61%)', // Vibrant orange
+ secondary: 'hsl(45, 100%, 70%)', // Bright yellow
+ accent: 'hsl(340, 82%, 52%)', // Energetic pink
+ background: 'hsl(0, 0%, 100%)',
+ foreground: 'hsl(0, 0%, 3.9%)',
+ muted: 'hsl(0, 0%, 96.1%)',
+ mutedForeground: 'hsl(0, 0%, 45.1%)',
+ border: 'hsl(0, 0%, 89.8%)',
+ card: 'hsl(0, 0%, 100%)',
+ cardForeground: 'hsl(0, 0%, 3.9%)',
+ },
+ calm: {
+ primary: 'hsl(210, 40%, 60%)', // Soft blue
+ secondary: 'hsl(158, 40%, 60%)', // Sage green
+ accent: 'hsl(200, 50%, 70%)', // Light blue
+ background: 'hsl(210, 40%, 98%)',
+ foreground: 'hsl(210, 40%, 11%)',
+ muted: 'hsl(210, 40%, 96%)',
+ mutedForeground: 'hsl(210, 40%, 45%)',
+ border: 'hsl(210, 40%, 90%)',
+ card: 'hsl(210, 40%, 100%)',
+ cardForeground: 'hsl(210, 40%, 11%)',
+ },
+ focused: {
+ primary: 'hsl(240, 5.9%, 10%)', // Deep charcoal
+ secondary: 'hsl(240, 4.8%, 95.9%)', // Very light gray
+ accent: 'hsl(195, 95%, 68%)', // Bright cyan for focus
+ background: 'hsl(0, 0%, 100%)',
+ foreground: 'hsl(240, 10%, 3.9%)',
+ muted: 'hsl(240, 4.8%, 95.9%)',
+ mutedForeground: 'hsl(240, 5%, 64.9%)',
+ border: 'hsl(240, 5.9%, 90%)',
+ card: 'hsl(0, 0%, 100%)',
+ cardForeground: 'hsl(240, 10%, 3.9%)',
+ },
+ creative: {
+ primary: 'hsl(270, 95%, 68%)', // Vibrant purple
+ secondary: 'hsl(300, 95%, 68%)', // Magenta
+ accent: 'hsl(45, 95%, 68%)', // Bright yellow
+ background: 'hsl(280, 20%, 98%)',
+ foreground: 'hsl(280, 20%, 5%)',
+ muted: 'hsl(280, 20%, 95%)',
+ mutedForeground: 'hsl(280, 20%, 45%)',
+ border: 'hsl(280, 20%, 88%)',
+ card: 'hsl(280, 20%, 100%)',
+ cardForeground: 'hsl(280, 20%, 5%)',
+ },
+ stressed: {
+ primary: 'hsl(0, 84%, 60%)', // Soft red
+ secondary: 'hsl(25, 95%, 68%)', // Warm orange
+ accent: 'hsl(60, 95%, 68%)', // Calming yellow
+ background: 'hsl(0, 20%, 98%)',
+ foreground: 'hsl(0, 20%, 10%)',
+ muted: 'hsl(0, 20%, 95%)',
+ mutedForeground: 'hsl(0, 20%, 45%)',
+ border: 'hsl(0, 20%, 88%)',
+ card: 'hsl(0, 20%, 100%)',
+ cardForeground: 'hsl(0, 20%, 10%)',
+ },
+ // Time-based variations
+ morning: {
+ primary: 'hsl(45, 100%, 60%)', // Sunrise yellow
+ secondary: 'hsl(30, 100%, 70%)', // Warm orange
+ accent: 'hsl(200, 100%, 80%)', // Sky blue
+ background: 'hsl(45, 50%, 98%)',
+ foreground: 'hsl(45, 50%, 10%)',
+ muted: 'hsl(45, 50%, 95%)',
+ mutedForeground: 'hsl(45, 50%, 45%)',
+ border: 'hsl(45, 50%, 88%)',
+ card: 'hsl(45, 50%, 100%)',
+ cardForeground: 'hsl(45, 50%, 10%)',
+ },
+ evening: {
+ primary: 'hsl(250, 60%, 55%)', // Twilight purple
+ secondary: 'hsl(280, 60%, 60%)', // Evening violet
+ accent: 'hsl(320, 60%, 65%)', // Sunset pink
+ background: 'hsl(250, 30%, 95%)',
+ foreground: 'hsl(250, 30%, 15%)',
+ muted: 'hsl(250, 30%, 90%)',
+ mutedForeground: 'hsl(250, 30%, 50%)',
+ border: 'hsl(250, 30%, 85%)',
+ card: 'hsl(250, 30%, 98%)',
+ cardForeground: 'hsl(250, 30%, 15%)',
+ },
+ night: {
+ primary: 'hsl(220, 13%, 91%)', // Light gray for dark mode
+ secondary: 'hsl(215, 13.8%, 34%)', // Medium gray
+ accent: 'hsl(210, 40%, 98%)', // Very light for contrast
+ background: 'hsl(222.2, 84%, 4.9%)', // Very dark blue
+ foreground: 'hsl(210, 40%, 98%)',
+ muted: 'hsl(217.2, 32.6%, 17.5%)',
+ mutedForeground: 'hsl(215, 20.2%, 65.1%)',
+ border: 'hsl(217.2, 32.6%, 17.5%)',
+ card: 'hsl(222.2, 84%, 4.9%)',
+ cardForeground: 'hsl(210, 40%, 98%)',
+ },
+};
+
+export function AdaptiveThemeProvider({ children }: { children: React.ReactNode }) {
+ const userContext = React.useContext(React.createContext(null));
+ const user = userContext?.user;
+ const [mood, setMood] = useState({
+ energy: 70,
+ focus: 60,
+ stress: 30,
+ creativity: 50,
+ });
+ const [isAdaptiveMode, setIsAdaptiveMode] = useState(true);
+ const [manualOverride, setManualOverride] = useState(null);
+ const [timeOfDay, setTimeOfDay] = useState('');
+ const [currentTheme, setCurrentTheme] = useState(THEME_PALETTES.calm);
+
+ // Determine time of day
+ const getTimeOfDay = useCallback(() => {
+ const hour = new Date().getHours();
+ if (hour >= 5 && hour < 10) return 'morning';
+ if (hour >= 10 && hour < 14) return 'midday';
+ if (hour >= 14 && hour < 18) return 'afternoon';
+ if (hour >= 18 && hour < 22) return 'evening';
+ return 'night';
+ }, []);
+
+ // Generate theme based on mood and time
+ const generateThemeFromMood = useCallback((moodState: MoodState, currentTimeOfDay: string): AdaptiveThemeColors => {
+ // Determine dominant mood characteristic
+ let dominantMood = 'calm';
+
+ if (moodState.stress > 60) {
+ dominantMood = 'stressed';
+ } else if (moodState.energy > 75) {
+ dominantMood = 'energetic';
+ } else if (moodState.focus > 75) {
+ dominantMood = 'focused';
+ } else if (moodState.creativity > 75) {
+ dominantMood = 'creative';
+ }
+
+ // Get base theme from mood
+ let baseTheme = THEME_PALETTES[dominantMood as keyof typeof THEME_PALETTES];
+
+ // Apply time-of-day modifications
+ if (currentTimeOfDay === 'night') {
+ baseTheme = THEME_PALETTES.night;
+ } else if (currentTimeOfDay === 'morning') {
+ // Blend with morning colors
+ baseTheme = {
+ ...baseTheme,
+ primary: blendColors(baseTheme.primary, THEME_PALETTES.morning.primary, 0.3),
+ secondary: blendColors(baseTheme.secondary, THEME_PALETTES.morning.secondary, 0.2),
+ };
+ } else if (currentTimeOfDay === 'evening') {
+ // Blend with evening colors
+ baseTheme = {
+ ...baseTheme,
+ primary: blendColors(baseTheme.primary, THEME_PALETTES.evening.primary, 0.4),
+ background: blendColors(baseTheme.background, THEME_PALETTES.evening.background, 0.2),
+ };
+ }
+
+ return baseTheme;
+ }, []);
+
+ // Helper function to blend HSL colors
+ const blendColors = (color1: string, color2: string, ratio: number): string => {
+ // Simple color blending - in a real implementation, you'd parse HSL and blend properly
+ return ratio > 0.5 ? color2 : color1;
+ };
+
+ // Update mood state
+ const updateMood = useCallback((newMood: Partial) => {
+ setMood(prev => ({ ...prev, ...newMood }));
+ }, []);
+
+ // Apply theme to CSS variables
+ const applyTheme = useCallback((theme: AdaptiveThemeColors) => {
+ const root = document.documentElement;
+ root.style.setProperty('--primary', theme.primary);
+ root.style.setProperty('--secondary', theme.secondary);
+ root.style.setProperty('--accent', theme.accent);
+ root.style.setProperty('--background', theme.background);
+ root.style.setProperty('--foreground', theme.foreground);
+ root.style.setProperty('--muted', theme.muted);
+ root.style.setProperty('--muted-foreground', theme.mutedForeground);
+ root.style.setProperty('--border', theme.border);
+ root.style.setProperty('--card', theme.card);
+ root.style.setProperty('--card-foreground', theme.cardForeground);
+ }, []);
+
+ // Auto-detect mood from user activity (simplified)
+ const detectMoodFromActivity = useCallback(() => {
+ if (!user) return;
+
+ const hour = new Date().getHours();
+ const currentTime = getTimeOfDay();
+
+ // Simple heuristics for mood detection
+ let autoMood: Partial = {};
+
+ // Time-based mood adjustments
+ if (currentTime === 'morning') {
+ autoMood = { energy: 80, focus: 70, stress: 20 };
+ } else if (currentTime === 'midday') {
+ autoMood = { energy: 90, focus: 85, stress: 40 };
+ } else if (currentTime === 'afternoon') {
+ autoMood = { energy: 60, focus: 70, stress: 50 };
+ } else if (currentTime === 'evening') {
+ autoMood = { energy: 40, focus: 50, stress: 30, creativity: 70 };
+ } else {
+ autoMood = { energy: 20, focus: 30, stress: 20, creativity: 40 };
+ }
+
+ // Apply gradual mood changes to avoid jarring transitions
+ setMood(prev => ({
+ energy: Math.round((prev.energy * 0.8) + (autoMood.energy || prev.energy) * 0.2),
+ focus: Math.round((prev.focus * 0.8) + (autoMood.focus || prev.focus) * 0.2),
+ stress: Math.round((prev.stress * 0.8) + (autoMood.stress || prev.stress) * 0.2),
+ creativity: Math.round((prev.creativity * 0.8) + (autoMood.creativity || prev.creativity) * 0.2),
+ }));
+ }, [user, getTimeOfDay]);
+
+ // Update time of day periodically
+ useEffect(() => {
+ const updateTime = () => {
+ setTimeOfDay(getTimeOfDay());
+ };
+
+ updateTime();
+ const interval = setInterval(updateTime, 60000); // Check every minute
+
+ return () => clearInterval(interval);
+ }, [getTimeOfDay]);
+
+ // Auto-detect mood periodically
+ useEffect(() => {
+ if (isAdaptiveMode && !manualOverride) {
+ detectMoodFromActivity();
+ }
+ }, [timeOfDay, isAdaptiveMode, manualOverride, detectMoodFromActivity]);
+
+ // Generate and apply theme when mood or time changes
+ useEffect(() => {
+ if (!isAdaptiveMode && !manualOverride) return;
+
+ let newTheme: AdaptiveThemeColors;
+
+ if (manualOverride) {
+ newTheme = THEME_PALETTES[manualOverride as keyof typeof THEME_PALETTES] || THEME_PALETTES.calm;
+ } else {
+ newTheme = generateThemeFromMood(mood, timeOfDay);
+ }
+
+ setCurrentTheme(newTheme);
+ applyTheme(newTheme);
+ }, [mood, timeOfDay, isAdaptiveMode, manualOverride, generateThemeFromMood, applyTheme]);
+
+ const toggleAdaptiveMode = useCallback(() => {
+ setIsAdaptiveMode(prev => !prev);
+ if (manualOverride) {
+ setManualOverride(null);
+ }
+ }, [manualOverride]);
+
+ const value: AdaptiveThemeContextType = {
+ currentTheme,
+ mood,
+ updateMood,
+ timeOfDay,
+ isAdaptiveMode,
+ toggleAdaptiveMode,
+ manualOverride,
+ setManualOverride,
+ availableThemes: THEME_PALETTES,
+ generateThemeFromMood,
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useAdaptiveTheme() {
+ const context = useContext(AdaptiveThemeContext);
+ if (context === undefined) {
+ throw new Error('useAdaptiveTheme must be used within an AdaptiveThemeProvider');
+ }
+ return context;
+}
\ No newline at end of file