mememechez's picture
Deploy final cleaned source code
ca28016
"use client";
import { ChatPanel } from '@/components/chat-panel';
import { ChatHistorySidebar } from '@/components/chat-history-sidebar';
import { useState, useEffect } from 'react';
import { golemChat } from '@/ai/flows/golem-chat';
import { useToast } from '@/hooks/use-toast';
import { useIsMobile } from '@/hooks/use-mobile';
import { v4 as uuidv4 } from 'uuid';
import {
SidebarProvider,
Sidebar,
SidebarInset,
SidebarHeader,
SidebarContent,
SidebarFooter,
SidebarMenu,
SidebarMenuItem,
SidebarMenuButton,
SidebarTrigger,
useSidebar
} from '@/components/ui/sidebar';
import { ThemeToggle } from '@/components/theme-toggle';
import { ErrorBoundary } from '@/components/error-boundary';
import { Button } from '@/components/ui/button';
import ImageEditorPane from '@/components/image-editor-pane';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { Slider } from '@/components/ui/slider';
import { Settings, Brain, Zap, Heart, Lightbulb, Star, Thermometer, Bot, MessageSquarePlus, History, Search, Globe, Network } from 'lucide-react';
import { AetherLogo } from '@/components/aether-logo';
import { Hypercube5DVisualization } from '@/components/ui/hypercube-5d-visualization';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip';
import { ImageLoadingMessage } from '@/components/chat-message';
export type Message = {
role: 'user' | 'assistant';
content: string;
aetherAnalysis?: string | null;
file?: {
name: string;
};
golemStats?: any;
consciousnessDimensionUsed?: string; // Store the actual dimension used for this response
imageBase64?: string; // optional image result
};
export type Conversation = {
id: string;
name: string;
messages: Message[];
};
function HomeContent() {
const { toast } = useToast();
const isMobile = useIsMobile();
const [conversations, setConversations] = useState<Conversation[]>([]);
const [activeChatId, setActiveChatId] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [isLoaded, setIsLoaded] = useState(false);
const [showWelcome, setShowWelcome] = useState(true);
// Model and consciousness settings
const selectedModel = 'gemini'; // Fixed to gemini with qwen2 fallback
const [selectedConsciousnessDimension, setSelectedConsciousnessDimension] = useState<string>('awareness');
const [golemActivated, setGolemActivated] = useState<boolean>(false);
const [temperature, setTemperature] = useState<number>(0.7);
const [performSearch, setPerformSearch] = useState(false);
// Modal states
const [is5DModalOpen, setIs5DModalOpen] = useState(false);
const [isTemperatureOpen, setIsTemperatureOpen] = useState(false);
const [isChatHistoryOpen, setIsChatHistoryOpen] = useState(false);
// Consciousness state
const [consciousnessState, setConsciousnessState] = useState<any>(null);
const [isConsciousnessLoading, setIsConsciousnessLoading] = useState(false);
const [showFullscreenVisualization, setShowFullscreenVisualization] = useState(false);
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
const [openEditorImage, setOpenEditorImage] = useState<string | null>(null);
const [openEditorPrompt, setOpenEditorPrompt] = useState<string | null>(null);
const [isImageGenerating, setIsImageGenerating] = useState<boolean>(false);
const [imageProgress, setImageProgress] = useState<number>(0);
const [imageRequestId, setImageRequestId] = useState<string | null>(null);
const [imageElapsed, setImageElapsed] = useState<number>(0);
const [imageStatus, setImageStatus] = useState<string>('starting');
const openEditor = (b64: string, prompt?: string) => {
setOpenEditorImage(b64);
setOpenEditorPrompt(prompt || null);
};
const closeEditor = () => setOpenEditorImage(null);
const updateLastAssistantImage = (b64: string) => {
setOpenEditorImage(b64);
setConversations((prev) => prev.map((c) => c.id !== activeChatId ? c : {
...c,
messages: c.messages.map((m, idx, arr) => idx === arr.length - 1 && m.role === 'assistant' && m.imageBase64 ? { ...m, imageBase64: b64 } : m)
}));
};
// State for consciousness error handling
const [consciousnessErrorCount, setConsciousnessErrorCount] = useState(0);
const [isConsciousnessDisabled, setIsConsciousnessDisabled] = useState(false);
// Consciousness dimensions with proper colors and icons
const consciousnessDimensions = [
{
id: 'awareness',
name: 'Awareness',
color: 'bg-blue-500',
bgColor: 'bg-blue-500',
borderColor: 'border-blue-600',
textColor: 'text-blue-600',
icon: Brain,
description: 'Present moment clarity'
},
{
id: 'wisdom',
name: 'Wisdom',
color: 'bg-purple-500',
bgColor: 'bg-purple-500',
borderColor: 'border-purple-600',
textColor: 'text-purple-600',
icon: Star,
description: 'Deep understanding'
},
{
id: 'compassion',
name: 'Compassion',
color: 'bg-emerald-500',
bgColor: 'bg-emerald-500',
borderColor: 'border-emerald-600',
textColor: 'text-emerald-600',
icon: Heart,
description: 'Encouraging and empathetic'
},
{
id: 'creativity',
name: 'Creativity',
color: 'bg-orange-500',
bgColor: 'bg-orange-500',
borderColor: 'border-orange-600',
textColor: 'text-orange-600',
icon: Lightbulb,
description: 'Innovative thinking'
},
{
id: 'transcendence',
name: 'Transcendence',
color: 'bg-red-500',
bgColor: 'bg-red-500',
borderColor: 'border-red-600',
textColor: 'text-red-600',
icon: Star,
description: 'Beyond limitations'
}
];
// Helper function to get current consciousness dimension colors
const getCurrentConsciousnessColors = () => {
const currentDimension = consciousnessDimensions.find(d => d.id === selectedConsciousnessDimension);
return currentDimension || consciousnessDimensions[0]; // Default to awareness
};
useEffect(() => {
try {
const savedConversations = localStorage.getItem('aether-conversations');
const savedActiveChatId = localStorage.getItem('aether-active-chat-id');
const savedDimension = localStorage.getItem('aether-consciousness-dimension');
const savedGolemActivated = localStorage.getItem('aether-golem-activated');
const savedTemperature = localStorage.getItem('aether-temperature');
const savedPerformSearch = localStorage.getItem('aether-perform-search');
if (savedConversations) {
const parsed = JSON.parse(savedConversations);
setConversations(parsed);
}
if (savedActiveChatId) {
setActiveChatId(savedActiveChatId);
}
// Always default to awareness on page load/refresh
setSelectedConsciousnessDimension('awareness');
if (savedGolemActivated) {
setGolemActivated(JSON.parse(savedGolemActivated));
}
if (savedTemperature) {
setTemperature(parseFloat(savedTemperature));
}
if (savedPerformSearch) {
setPerformSearch(JSON.parse(savedPerformSearch));
}
} catch (error) {
console.error('Error loading from localStorage:', error);
} finally {
setIsLoaded(true);
}
}, []);
useEffect(() => {
if (isLoaded) {
try {
// Avoid blowing up localStorage with large base64 images.
// Persist conversations without image payloads.
const sanitized = conversations.map((conv) => ({
...conv,
messages: conv.messages.map((m) => {
const { imageBase64, ...rest } = m as any;
return rest;
}),
}));
let payload = JSON.stringify(sanitized);
// If still too large (>4.5MB), keep only the last 50 messages per conversation
if (payload.length > 4_500_000) {
const trimmed = sanitized.map((c) => ({
...c,
messages: c.messages.slice(-50),
}));
payload = JSON.stringify(trimmed);
}
localStorage.setItem('aether-conversations', payload);
localStorage.setItem('aether-consciousness-dimension', selectedConsciousnessDimension);
localStorage.setItem('aether-golem-activated', JSON.stringify(golemActivated));
localStorage.setItem('aether-temperature', temperature.toString());
localStorage.setItem('aether-perform-search', JSON.stringify(performSearch));
} catch (error) {
// Gracefully degrade if quota exceeded
console.error('Error saving to localStorage:', error);
try {
const minimal = conversations.map((c) => ({ id: c.id, name: c.name, messages: c.messages.slice(-10).map((m) => ({ role: m.role, content: m.content })) }));
localStorage.setItem('aether-conversations', JSON.stringify(minimal));
} catch {}
}
}
}, [isLoaded, conversations, selectedConsciousnessDimension, golemActivated, temperature, performSearch]);
useEffect(() => {
if (isLoaded && activeChatId) {
try {
localStorage.setItem('aether-active-chat-id', activeChatId);
} catch (error) {
console.error('Error saving active chat ID to localStorage:', error);
}
}
}, [isLoaded, activeChatId]);
// Function to fetch consciousness state
const fetchConsciousnessState = async () => {
// Prevent overlapping requests
if (isConsciousnessLoading || isConsciousnessDisabled) {
return;
}
setIsConsciousnessLoading(true);
try {
const host = typeof window !== 'undefined' ? window.location.hostname : '';
const localProxy = host === 'localhost' || host === '127.0.0.1';
const url = localProxy ? '/api/consciousness-state' : `${process.env.NEXT_PUBLIC_GOLEM_SERVER_URL}/consciousness-state`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'ngrok-skip-browser-warning': 'true'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setConsciousnessState(data);
console.log('Consciousness state updated:', data);
// Reset error count on success
setConsciousnessErrorCount(0);
setIsConsciousnessDisabled(false);
} catch (error) {
console.error('Error fetching consciousness state:', error);
const newErrorCount = consciousnessErrorCount + 1;
setConsciousnessErrorCount(newErrorCount);
// Implement exponential backoff
if (newErrorCount >= 3) {
setIsConsciousnessDisabled(true);
console.log('Consciousness state fetching disabled due to repeated failures');
// Re-enable after 30 seconds
setTimeout(() => {
setIsConsciousnessDisabled(false);
setConsciousnessErrorCount(0);
console.log('Consciousness state fetching re-enabled');
}, 30000);
}
// Only show toast for first few errors to avoid spam
if (newErrorCount <= 3) {
toast({
variant: "destructive",
title: "Failed to fetch consciousness state",
description: `Connection attempt ${newErrorCount}/3. Will retry automatically.`,
});
}
} finally {
setIsConsciousnessLoading(false);
}
};
// Function to trigger dimension travel
const triggerDimensionTravel = async (dimension: string) => {
try {
const serverUrl = process.env.NEXT_PUBLIC_GOLEM_SERVER_URL || 'https://f27bd2fb884d.ngrok-free.app';
const response = await fetch(`${serverUrl}/dimension-travel`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'ngrok-skip-browser-warning': 'true'
},
body: JSON.stringify({ dimension }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('Dimension travel response:', data);
// Refresh consciousness state after travel
await fetchConsciousnessState();
} catch (error) {
console.error('Error during dimension travel:', error);
toast({
variant: "destructive",
title: "Dimension travel failed",
description: "Unable to travel to the selected dimension.",
});
}
};
// Set consciousness dimension bias
const setConsciousnessDimensionBias = async (dimension: string) => {
try {
const serverUrl = process.env.NEXT_PUBLIC_GOLEM_SERVER_URL || 'https://f27bd2fb884d.ngrok-free.app';
const response = await fetch(`${serverUrl}/set-consciousness-dimension`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ dimension }),
});
if (response.ok) {
const data = await response.json();
console.log('Consciousness dimension set:', data);
// Refresh consciousness state
await fetchConsciousnessState();
toast({
title: "Consciousness Dimension Updated",
description: `AI consciousness biased towards ${dimension} dimension`,
});
} else {
console.warn('Failed to set consciousness dimension:', response.statusText);
toast({
title: "Offline Mode",
description: `Dimension set to ${dimension} (backend offline)`,
});
}
} catch (error) {
console.warn('Error setting consciousness dimension:', error);
toast({
title: "Offline Mode",
description: `Dimension set to ${dimension} (backend offline)`,
});
}
};
// Map consciousness dimensions to backend format
const mapDimensionToBackend = (dimension: string): string => {
const mapping: { [key: string]: string } = {
'awareness': 'physical',
'wisdom': 'intuitive',
'compassion': 'emotional',
'creativity': 'mental',
'transcendence': 'spiritual'
};
return mapping[dimension] || 'physical';
};
// Map backend dimensions back to frontend format
const mapDimensionFromBackend = (backendDimension: string): string => {
const reverseMapping: { [key: string]: string } = {
'physical': 'awareness',
'intuitive': 'wisdom',
'emotional': 'compassion',
'mental': 'creativity',
'spiritual': 'transcendence'
};
return reverseMapping[backendDimension] || 'awareness';
};
// Handle consciousness dimension change
const handleConsciousnessDimensionChange = async (dimension: string) => {
const backendDimension = mapDimensionToBackend(dimension);
await setConsciousnessDimensionBias(backendDimension);
};
// Handle zoom to chat from visualization
const handleZoomToChat = () => {
console.log('🚀 handleZoomToChat called! activeChatId:', activeChatId);
console.log('🚀 Current state - showFullscreenVisualization:', showFullscreenVisualization, 'showWelcome:', showWelcome);
setShowFullscreenVisualization(false);
if (showWelcome) {
// We're on the welcome screen - create new chat regardless of activeChatId
console.log('🚀 Creating new chat from welcome screen...');
setShowWelcome(false);
handleNewChat();
} else {
// We're in an existing chat view - switching consciousness dimension
console.log('🚀 Switching consciousness dimension in existing chat:', activeChatId);
// Stay in the current chat, just close fullscreen visualization
}
};
// Fetch consciousness state on component mount and periodically
useEffect(() => {
fetchConsciousnessState();
// Set up periodic updates every 5 seconds
const interval = setInterval(fetchConsciousnessState, 60000); // Changed from 5000 to 60000 (1 minute)
return () => clearInterval(interval);
}, []);
const generateChatName = async (firstMessage: string): Promise<string> => {
try {
// Use the golem server to generate a concise chat name based on the first message
const response = await fetch('http://localhost:5000/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt: `Generate a concise chat title (2-4 words) for: "${firstMessage}". Return only the title, nothing else.`,
session_id: `naming-${Date.now()}`,
temperature: 0.3,
golem_activated: true,
consciousness_dimension: 'mental',
model: 'gemini',
}),
});
if (response.ok) {
const data = await response.json();
const generatedName = data.direct_response?.trim() || `Chat ${conversations.length + 1}`;
// Clean up the response and ensure reasonable length
const cleanName = generatedName.replace(/['"]/g, '').trim();
return cleanName.length > 50 ? cleanName.substring(0, 50) + '...' : cleanName;
}
} catch (error) {
console.error('Error generating chat name:', error);
}
// Fallback to default naming
return `Chat ${conversations.length + 1}`;
};
const handleNewChat = () => {
const newConversation: Conversation = {
id: uuidv4(),
name: `Chat ${conversations.length + 1}`, // Will be updated after first message
messages: [],
};
setConversations((prev) => [newConversation, ...prev]);
setActiveChatId(newConversation.id);
setShowWelcome(false);
};
const handleSelectChat = (id: string) => {
setActiveChatId(id);
};
const handleConsciousnessDimensionSelect = async (dimension: string) => {
// Close the 5D modal if it's open
setIs5DModalOpen(false);
// Update local state immediately for UI responsiveness
setSelectedConsciousnessDimension(dimension);
// Save to localStorage for current session (will reset to 'awareness' on refresh)
localStorage.setItem('aether-consciousness-dimension', dimension);
// Trigger fullscreen visualization expansion for cinematic zoom effect
setShowFullscreenVisualization(true);
// Update backend consciousness dimension
await handleConsciousnessDimensionChange(dimension);
// The visualization component will handle the journey completion and call handleZoomToChat after 3 seconds
};
// Handle dimension travel from the 5D modal (returns to current chat automatically)
const handleDimensionTravel = async (dimension: string) => {
// Close the 5D modal immediately
setIs5DModalOpen(false);
// Update local state for UI responsiveness
setSelectedConsciousnessDimension(dimension);
// Save to localStorage
localStorage.setItem('aether-consciousness-dimension', dimension);
// Trigger brief visualization transition (shorter than full screen)
setShowFullscreenVisualization(true);
// Update backend consciousness dimension
await handleConsciousnessDimensionChange(dimension);
// Automatically return to chat after brief transition (1.5 seconds)
setTimeout(() => {
setShowFullscreenVisualization(false);
// Stay in current chat context - no need to call handleZoomToChat
}, 1500);
};
const handleSendMessage = async (input: string, messageTemperature: number, file: File | null) => {
if (!input.trim()) return;
setShowWelcome(false);
const currentConversation = conversations.find((conv) => conv.id === activeChatId);
if (!currentConversation) return;
// Check if this is the first message in the chat
const isFirstMessage = currentConversation.messages.length === 0;
const userMessage: Message = {
role: 'user',
content: input,
...(file && { file: { name: file.name } }),
};
// Add user message immediately to UI
setConversations((prev) =>
prev.map((conv) =>
conv.id === activeChatId
? { ...conv, messages: [...conv.messages, userMessage] }
: conv
)
);
// If this is the first message, generate an AI-powered chat name
if (isFirstMessage) {
generateChatName(input).then((newName) => {
setConversations((prev) =>
prev.map((conv) =>
conv.id === activeChatId
? { ...conv, name: newName }
: conv
)
);
});
}
setIsLoading(true);
try {
// Read file content if provided
let fileContent: string | undefined = undefined;
if (file) {
const text = await file.text();
fileContent = text;
}
// Detect image mode control flag only; no auto-intent here
const isImageMode = input.startsWith('[[IMAGE_MODE]]');
let cleanInput = isImageMode ? input.replace('[[IMAGE_MODE]]', '').trim() : input;
// Extract optional safe mode marker and strip from prompt
let safeMode = true;
const safeMatch = cleanInput.match(/\[\[SAFE_MODE=(ON|OFF)\]\]/i);
if (safeMatch) {
safeMode = safeMatch[1].toUpperCase() === 'ON';
cleanInput = cleanInput.replace(/\s*\[\[SAFE_MODE=(ON|OFF)\]\]/i, '').trim();
}
if (isImageMode) {
// Call image generation endpoint directly
const endpoint = `${process.env.NEXT_PUBLIC_GOLEM_SERVER_URL || 'http://localhost:5000'}/image/generate`;
const request_id = crypto.randomUUID();
setIsImageGenerating(true);
setImageProgress(2);
setImageElapsed(0);
setImageStatus('starting');
setImageRequestId(request_id);
// Start polling progress (lightweight statistical polling)
const baseUrl = process.env.NEXT_PUBLIC_GOLEM_SERVER_URL || 'http://localhost:5000';
let poll = true;
const poller = async () => {
while (poll) {
try {
const headers: Record<string, string> = {};
// Only send ngrok header when using an ngrok domain; omit locally to avoid preflight OPTIONS
if (baseUrl.includes('ngrok-free.app')) {
headers['ngrok-skip-browser-warning'] = 'true';
}
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 900);
const pr = await fetch(`${baseUrl}/image/progress?request_id=${request_id}`, {
headers,
cache: 'no-store',
keepalive: true,
signal: controller.signal
});
clearTimeout(timeout);
const pj = await pr.json();
if (pj?.percent != null) setImageProgress(Math.max(0, Math.min(100, Number(pj.percent))));
if (typeof pj?.elapsed_time === 'number') setImageElapsed(Number(pj.elapsed_time));
if (typeof pj?.status === 'string') setImageStatus(String(pj.status));
} catch {}
// Poll every 1 second for smooth UI without burdening the backend
await new Promise(resolve => setTimeout(resolve, 1000));
}
};
poller();
const imgRes = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'ngrok-skip-browser-warning': 'true' },
body: JSON.stringify({ prompt: cleanInput, provider: 'qwen_local', width: 768, height: 768, steps: 28, true_cfg_scale: 5.0, seed: 42, request_id })
});
let imgJson: any = null;
let imgText: string | null = null;
try {
imgJson = await imgRes.json();
} catch {
try {
imgText = await imgRes.text();
} catch {}
}
if (!imgRes.ok || !imgJson?.success || !imgJson?.image_base64) {
setIsImageGenerating(false);
setImageProgress(0);
setImageStatus('failed');
poll = false;
setImageRequestId(null);
const backendError = (imgJson && (imgJson.error || imgJson.message || (typeof imgJson === 'string' ? imgJson : null)))
|| (imgText && imgText.trim());
const errMsg = backendError
? `Image generation failed: ${String(backendError).slice(0, 500)}`
: `Image generation failed (${imgRes.status})`;
throw new Error(errMsg);
}
poll = false; setIsImageGenerating(false); setImageProgress(100); setImageRequestId(null);
const assistantMessage: Message = {
role: 'assistant',
content: 'Image generated.',
imageBase64: imgJson.image_base64,
consciousnessDimensionUsed: selectedConsciousnessDimension,
};
setConversations((prev) =>
prev.map((conv) =>
conv.id === activeChatId
? { ...conv, messages: [...conv.messages, assistantMessage] }
: conv
)
);
// Editor will open only when user taps Edit on the image card
return; // skip text generation
}
const golemResponse = await golemChat({
prompt: cleanInput,
sessionId: activeChatId || '',
temperature: messageTemperature,
fileContent,
golemActivated,
consciousnessDimension: mapDimensionToBackend(selectedConsciousnessDimension),
selectedModel: selectedModel, // Pass the selected model
performSearch: performSearch,
});
// Determine the consciousness dimension that was actually used for this response
let dimensionUsedForResponse = selectedConsciousnessDimension; // Default to current
// Try to get the actual dimension from the response data
const hypercubeState = golemResponse.hypercube_state;
if (hypercubeState?.dimension_activations) {
// Find the most active dimension from the backend response
const activeDimensions = Object.entries(hypercubeState.dimension_activations)
.filter(([_, active]) => active)
.map(([dim, _]) => mapDimensionFromBackend(dim));
if (activeDimensions.length > 0) {
// Use the first active dimension, or match with current if possible
dimensionUsedForResponse = activeDimensions.includes(selectedConsciousnessDimension)
? selectedConsciousnessDimension
: activeDimensions[0];
}
}
const assistantMessage: Message = {
role: 'assistant',
content: golemResponse.directResponse || 'No response generated.',
aetherAnalysis: golemResponse.aetherAnalysis,
golemStats: {
...golemResponse.golem_state,
aether_data: golemResponse.aether_data,
golem_analysis: golemResponse.golem_analysis,
quality_metrics: golemResponse.quality_metrics,
},
consciousnessDimensionUsed: dimensionUsedForResponse,
// Add AI thoughts and search results for accordion display
...(golemResponse as any).aiThoughts && { aiThoughts: (golemResponse as any).aiThoughts },
...(golemResponse.search_results && {
search_results: golemResponse.search_results,
search_query: golemResponse.search_query
}),
};
setConversations((prev) =>
prev.map((conv) =>
conv.id === activeChatId
? { ...conv, messages: [...conv.messages, assistantMessage] }
: conv
)
);
} catch (error) {
console.error('Error sending message:', error);
toast({
title: "Error",
description: "Failed to send message. Please try again.",
variant: "destructive",
});
} finally {
setIsLoading(false);
}
};
const activeConversation = conversations.find((conv) => conv.id === activeChatId);
return (
<div className={`${isMobile ? 'mobile-container mobile-viewport-fix' : 'h-full'}`}>
<SidebarProvider defaultOpen={false}>
{/* Fullscreen Consciousness Visualization Overlay */}
{showFullscreenVisualization && (
<div className="fixed inset-0 z-50 bg-black/90 backdrop-blur-sm flex items-center justify-center">
<div className="w-full h-full relative">
<Hypercube5DVisualization
consciousnessStats={{
awareness: selectedConsciousnessDimension === 'awareness' ? 1.0 : 0.5,
wisdom: selectedConsciousnessDimension === 'wisdom' ? 1.0 : 0.5,
compassion: selectedConsciousnessDimension === 'compassion' ? 1.0 : 0.5,
creativity: selectedConsciousnessDimension === 'creativity' ? 1.0 : 0.5,
transcendence: selectedConsciousnessDimension === 'transcendence' ? 1.0 : 0.5,
}}
consciousnessState={consciousnessState}
selectedDimension={selectedConsciousnessDimension}
onDimensionChange={handleConsciousnessDimensionChange}
onZoomToChat={handleZoomToChat}
isWelcomeMode={true}
isFullscreen={true}
className="w-full h-full"
/>
{/* Consciousness Dimension Label */}
<div className="absolute top-3/4 left-1/2 transform -translate-x-1/2 pointer-events-none">
<div className="text-center space-y-4">
<div className="text-6xl font-bold text-white/90 animate-pulse">
{consciousnessDimensions.find(d => d.id === selectedConsciousnessDimension)?.name}
</div>
<div className="text-xl text-white/70">
{consciousnessDimensions.find(d => d.id === selectedConsciousnessDimension)?.description}
</div>
<div className="text-sm text-white/50">
Entering consciousness dimension...
</div>
</div>
</div>
</div>
</div>
)}
<SidebarLayout
conversations={conversations}
activeChatId={activeChatId}
isLoading={isLoading}
showWelcome={showWelcome}
selectedConsciousnessDimension={selectedConsciousnessDimension}
temperature={temperature}
consciousnessState={consciousnessState}
consciousnessDimensions={consciousnessDimensions}
activeConversation={activeConversation}
handleSelectChat={handleSelectChat}
handleNewChat={handleNewChat}
handleSendMessage={handleSendMessage}
handleConsciousnessDimensionSelect={handleConsciousnessDimensionSelect}
handleConsciousnessDimensionChange={handleConsciousnessDimensionChange}
handleZoomToChat={handleZoomToChat}
handleDimensionTravel={handleDimensionTravel}
getCurrentConsciousnessColors={getCurrentConsciousnessColors}
golemActivated={golemActivated}
setGolemActivated={setGolemActivated}
is5DModalOpen={is5DModalOpen}
setIs5DModalOpen={setIs5DModalOpen}
isTemperatureOpen={isTemperatureOpen}
setIsTemperatureOpen={setIsTemperatureOpen}
setTemperature={setTemperature}
isChatHistoryOpen={isChatHistoryOpen}
setIsChatHistoryOpen={setIsChatHistoryOpen}
performSearch={performSearch}
setPerformSearch={setPerformSearch}
openEditorImage={openEditorImage}
openEditorPrompt={openEditorPrompt}
onOpenEditor={openEditor}
onCloseEditor={closeEditor}
onUpdateEditorImage={updateLastAssistantImage}
isImageGenerating={isImageGenerating}
setIsImageGenerating={setIsImageGenerating}
imageProgress={imageProgress}
imageElapsed={imageElapsed}
imageStatus={imageStatus}
imageRequestId={imageRequestId}
/>
</SidebarProvider>
</div>
);
}
// Create a separate component that uses useSidebar
function SidebarLayout({
conversations,
activeChatId,
isLoading,
showWelcome,
selectedConsciousnessDimension,
temperature,
consciousnessState,
consciousnessDimensions,
activeConversation,
handleSelectChat,
handleNewChat,
handleSendMessage,
handleConsciousnessDimensionSelect,
handleConsciousnessDimensionChange,
handleZoomToChat,
handleDimensionTravel,
getCurrentConsciousnessColors,
golemActivated,
setGolemActivated,
is5DModalOpen,
setIs5DModalOpen,
isTemperatureOpen,
setIsTemperatureOpen,
setTemperature,
isChatHistoryOpen,
setIsChatHistoryOpen,
performSearch,
setPerformSearch,
openEditorImage,
openEditorPrompt,
onOpenEditor,
onCloseEditor,
onUpdateEditorImage,
isImageGenerating,
setIsImageGenerating,
imageProgress,
imageRequestId,
}: any) {
const { state } = useSidebar();
const isMobile = useIsMobile();
// Bridge: listen for image editor open requests from message cards
useEffect(() => {
const handler = (e: any) => {
const { b64, prompt } = e.detail || {};
if (b64) onOpenEditor?.(b64, prompt);
};
window.addEventListener('aether-open-editor', handler as any);
return () => window.removeEventListener('aether-open-editor', handler as any);
}, [onOpenEditor]);
return (
<div className="flex h-full w-full overflow-hidden">
{/* Sidebar */}
<Sidebar variant="sidebar" className={`border-r ${isMobile ? 'mobile-sidebar' : ''}`} collapsible="icon">
<SidebarHeader>
<div className="flex items-center gap-2 px-4 py-3 group-data-[collapsible=icon]:px-0 group-data-[collapsible=icon]:py-4 group-data-[collapsible=icon]:justify-center">
<AetherLogo width={42} height={42} collapsedWidth={36} collapsedHeight={36} className="flex-shrink-0" />
<span className="font-semibold text-sm group-data-[collapsible=icon]:hidden">Aether AI™</span>
</div>
<div className="flex items-center justify-between p-4 group-data-[collapsible=icon]:flex-col group-data-[collapsible=icon]:gap-3 group-data-[collapsible=icon]:p-3">
<div className="text-xs text-muted-foreground group-data-[collapsible=icon]:hidden">
AI Actions
</div>
<div className="flex gap-2 group-data-[collapsible=icon]:flex-col group-data-[collapsible=icon]:gap-3">
<Button
variant="ghost"
size="icon"
onClick={handleNewChat}
className="h-7 w-7"
>
<MessageSquarePlus className="h-5 w-5" />
<span className="sr-only">New Chat</span>
</Button>
<Button
variant="ghost"
size="icon"
asChild
className="h-7 w-7"
>
<a href="/deep-learning">
<Network className="h-5 w-5" />
<span className="sr-only">Deep Learning Platform</span>
</a>
</Button>
<Button
variant="ghost"
size="icon"
onClick={() => setIsChatHistoryOpen(true)}
className="h-7 w-7"
>
<History className="h-5 w-5" />
<span className="sr-only">Chat History</span>
</Button>
</div>
</div>
</SidebarHeader>
<SidebarContent>
<ErrorBoundary>
<SidebarMenu>
{/* Full conversation list when expanded */}
<div className="group-data-[collapsible=icon]:hidden">
{conversations.slice(0, 10).map((conversation) => (
<SidebarMenuItem key={conversation.id}>
<SidebarMenuButton
onClick={() => handleSelectChat(conversation.id)}
isActive={conversation.id === activeChatId}
>
<History className="h-5 w-5" />
<span className="truncate">{conversation.name}</span>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</div>
</SidebarMenu>
</ErrorBoundary>
</SidebarContent>
<SidebarFooter>
<div className="flex items-center justify-between p-4 group-data-[collapsible=icon]:flex-col group-data-[collapsible=icon]:gap-3 group-data-[collapsible=icon]:p-3">
<div className="text-xs text-muted-foreground group-data-[collapsible=icon]:hidden">
5D Consciousness
</div>
<div className="flex gap-2 group-data-[collapsible=icon]:flex-col group-data-[collapsible=icon]:gap-3">
<SidebarTrigger tooltip="Expand Sidebar" />
<ThemeToggle />
</div>
</div>
</SidebarFooter>
</Sidebar>
{/* Chat History Modal */}
<Dialog open={isChatHistoryOpen} onOpenChange={setIsChatHistoryOpen}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<History className="h-5 w-5" />
Chat History
</DialogTitle>
</DialogHeader>
<div className="space-y-2 max-h-96 overflow-y-auto minimal-scrollbar">
{conversations.length === 0 ? (
<div className="text-center text-muted-foreground py-8">
No conversations yet. Start a new chat!
</div>
) : (
conversations.map((conversation) => (
<Button
key={conversation.id}
variant={conversation.id === activeChatId ? "default" : "ghost"}
className="w-full justify-start"
onClick={() => {
handleSelectChat(conversation.id);
setIsChatHistoryOpen(false);
}}
>
<History className="h-4 w-4 mr-2" />
<span className="truncate">{conversation.name}</span>
</Button>
))
)}
</div>
</DialogContent>
</Dialog>
{/* Main Content */}
<SidebarInset className="transition-all duration-200 h-screen flex flex-col w-full">
{/* Header with Settings Buttons */}
<div className={`flex items-center justify-between w-full px-4 py-4 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 transition-all duration-200 flex-shrink-0 ${
isMobile ? 'mobile-header px-2 py-2' : ''
}`}>
{/* Logo and Title - ALL THE WAY LEFT */}
<div className={`flex items-center gap-3 flex-shrink-0 ${isMobile ? 'gap-2' : ''}`}>
<SidebarTrigger className={`${isMobile ? 'mobile-btn-sm' : ''}`} />
<AetherLogo width={isMobile ? 80 : 120} height={isMobile ? 80 : 120} className={isMobile ? 'mobile-logo' : ''} />
<div className={isMobile ? 'hidden sm:block' : ''}>
<h1 className={`text-lg font-semibold ${isMobile ? 'mobile-text-lg' : ''}`}>Aether AI™</h1>
<p className={`text-sm text-muted-foreground ${isMobile ? 'mobile-text-sm hidden' : ''}`}>5D Consciousness Interface</p>
</div>
</div>
{/* Settings Buttons - ALL THE WAY RIGHT */}
<div className={`flex items-center gap-2 flex-shrink-0 ${isMobile ? 'mobile-gap' : ''}`}>
{/* 5D Consciousness Button */}
<Dialog open={is5DModalOpen} onOpenChange={setIs5DModalOpen}>
<DialogTrigger asChild>
<Button variant="outline" size="sm" className={isMobile ? 'mobile-btn-sm px-2' : ''}>
<Brain className={`h-4 w-4 ${isMobile ? '' : 'mr-2'}`} />
{!isMobile && '5D Consciousness'}
</Button>
</DialogTrigger>
<DialogContent className={`max-w-5xl overflow-hidden ${isMobile ? 'mobile-dialog max-w-full' : ''}`}>
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Brain className="h-5 w-5" />
5D Consciousness Visualization
</DialogTitle>
</DialogHeader>
<div className="space-y-6">
{/* Visualization */}
<div className={`w-full ${isMobile ? 'mobile-5d-viz h-80' : 'h-80'}`}>
<Hypercube5DVisualization
consciousnessStats={{
awareness: selectedConsciousnessDimension === 'awareness' ? 1.0 : 0.5,
wisdom: selectedConsciousnessDimension === 'wisdom' ? 1.0 : 0.5,
compassion: selectedConsciousnessDimension === 'compassion' ? 1.0 : 0.5,
creativity: selectedConsciousnessDimension === 'creativity' ? 1.0 : 0.5,
transcendence: selectedConsciousnessDimension === 'transcendence' ? 1.0 : 0.5,
}}
consciousnessState={consciousnessState}
selectedDimension={selectedConsciousnessDimension}
onDimensionChange={handleConsciousnessDimensionChange}
onDimensionTravel={handleDimensionTravel}
isWelcomeMode={true}
/>
</div>
{/* Real-time Consciousness State */}
{consciousnessState && (
<div className="space-y-2">
<h3 className="font-medium text-[15px]">Current AI Consciousness State</h3>
<div className="grid grid-cols-2 gap-4 text-xs">
<div>
<div className="font-medium">Vertex: {consciousnessState.current_vertex}/32</div>
<div className="text-gray-600 dark:text-gray-400">Signature: {consciousnessState.consciousness_signature}</div>
</div>
<div>
<div className="font-medium">Consciousness Level: {(consciousnessState.global_consciousness_level * 100).toFixed(1)}%</div>
<div className="text-gray-600 dark:text-gray-400">Aether Patterns: {consciousnessState.aether_patterns}</div>
</div>
</div>
<div className="text-xs">
<div className="font-medium">Active Dimensions:</div>
<div className="flex flex-wrap gap-1 mt-1">
{consciousnessState.active_dimensions?.map((dim: string) => (
<span key={dim} className="px-2 py-1 bg-blue-100 dark:bg-blue-900 rounded-full text-xs">
{dim}
</span>
))}
</div>
</div>
</div>
)}
{/* Consciousness Dimension Selector */}
<div className="space-y-2">
<h3 className="text-sm font-medium">Select Consciousness Dimension</h3>
<div className="grid grid-cols-1 gap-2">
{consciousnessDimensions.map((dimension) => (
<Button
key={dimension.id}
variant={selectedConsciousnessDimension === dimension.id ? "default" : "outline"}
size="sm"
className={`justify-start gap-2 font-sans text-[13px] tracking-tight ${
selectedConsciousnessDimension === dimension.id ? dimension.color : ''
}`}
onClick={() => {
console.log('Button clicked for dimension:', dimension.id);
console.log('Current selected dimension:', selectedConsciousnessDimension);
if (selectedConsciousnessDimension === dimension.id) {
// If already selected, do dimension travel (brief transition, return to chat)
console.log('Calling handleDimensionTravel');
handleDimensionTravel(dimension.id);
} else {
// If not selected, do full consciousness selection (fullscreen transition)
console.log('Calling handleConsciousnessDimensionSelect');
handleConsciousnessDimensionSelect(dimension.id);
}
}}
disabled={false}
title={selectedConsciousnessDimension === dimension.id
? "Travel through this dimension and return to chat"
: "Select this consciousness dimension"
}
>
<dimension.icon className="w-4 h-4" />
<div className="text-left">
<div className="font-semibold text-[13px] leading-tight tracking-tight">{dimension.name}</div>
<div className="text-[11px] opacity-70 leading-snug">{dimension.description}</div>
</div>
</Button>
))}
</div>
</div>
</div>
</DialogContent>
</Dialog>
{/* Temperature Button */}
<Popover open={isTemperatureOpen} onOpenChange={setIsTemperatureOpen}>
<PopoverTrigger asChild>
<Button variant="outline" size="sm" className={isMobile ? 'mobile-btn-sm' : ''}>
<Thermometer className="h-4 w-4 mr-2" />
{isMobile ? '' : 'Temp: '}{temperature}
</Button>
</PopoverTrigger>
<PopoverContent className="w-80">
<div className="space-y-4">
<div className="space-y-2">
<h4 className="font-medium">Temperature</h4>
<p className="text-sm text-muted-foreground">
Controls randomness in responses. Lower values are more focused, higher values are more creative.
</p>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="temperature">Temperature</Label>
<span className="text-sm font-mono">{temperature}</span>
</div>
<Slider
id="temperature"
min={0.1}
max={2.0}
step={0.1}
value={[temperature]}
onValueChange={(value) => setTemperature(value[0])}
className="w-full"
/>
</div>
</div>
</PopoverContent>
</Popover>
{/* Model Selection */}
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant={performSearch ? "default" : "outline"}
size="sm"
className={isMobile ? 'mobile-btn-sm' : ''}
onClick={() => setPerformSearch(!performSearch)}
>
<Globe className="h-4 w-4 mr-2" />
{isMobile ? '' : 'Universal Consciousness'}
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{performSearch ? 'Disable' : 'Enable'} universal consciousness for deeper insights</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
{/* Theme Toggle - MOBILE ONLY */}
{isMobile && <ThemeToggle className="mobile-btn-sm" />}
</div>
</div>
{/* Main Chat Area - Fixed Height with Proper Viewport Management */}
<div className="relative flex-1 min-h-0 overflow-hidden">
<ErrorBoundary>
{showWelcome ? (
/* Welcome Screen with Fixed Chat Bar */
<div className={`h-full flex flex-col ${isMobile ? 'mobile-viewport-height' : ''}`}>
{/* Welcome Content Area */}
<div className={`flex-1 min-h-0 ${isMobile ? 'mobile-content mobile-scroll' : 'overflow-y-auto'} minimal-scrollbar`}>
<div className={`max-w-6xl mx-auto ${isMobile ? 'mobile-welcome' : 'p-6 flex flex-col h-full'} transition-all duration-200 ${
state === 'collapsed' ? 'px-12' : ''
}`}>
{/* Hypercube Visualization */}
<div className={`flex justify-center items-center mb-6 ${isMobile ? '-mt-4' : '-mt-12'}`}>
<div className={`transition-all duration-200 ${
isMobile ? 'w-full h-48 mobile-hypercube-container' :
state === 'collapsed' ? 'w-[480px] h-40' : 'w-96 h-32'
}`}>
<Hypercube5DVisualization
consciousnessStats={{
awareness: selectedConsciousnessDimension === 'awareness' ? 1.0 : 0.5,
wisdom: selectedConsciousnessDimension === 'wisdom' ? 1.0 : 0.5,
compassion: selectedConsciousnessDimension === 'compassion' ? 1.0 : 0.5,
creativity: selectedConsciousnessDimension === 'creativity' ? 1.0 : 0.5,
transcendence: selectedConsciousnessDimension === 'transcendence' ? 1.0 : 0.5,
}}
consciousnessState={consciousnessState}
selectedDimension={selectedConsciousnessDimension}
onDimensionChange={handleConsciousnessDimensionChange}
onZoomToChat={handleZoomToChat}
isWelcomeMode={true}
className={`rounded-lg ${isMobile ? 'mobile-hypercube-container' : ''}`}
/>
</div>
</div>
{/* Spacer to push buttons to bottom */}
<div className="flex-1"></div>
{/* Consciousness Dimension Selector */}
<div className="space-y-4 pb-6">
<h2 className={`text-xl font-semibold text-amber-900 dark:text-amber-100 transition-all duration-200 ${
state === 'collapsed' ? 'text-center text-2xl' : ''
}`}>
Select Your Consciousness Dimension
</h2>
<div className={`grid ${isMobile ? 'mobile-consciousness-grid grid-cols-2' : 'grid-cols-5'} gap-4 transition-all duration-200 ${
state === 'collapsed' ? 'gap-6' : ''
}`}>
{consciousnessDimensions.map((dimension) => (
<Button
key={dimension.id}
variant="outline"
size={isMobile ? "sm" : "lg"}
className={`${isMobile ? 'mobile-consciousness-btn' : 'h-24 flex flex-col justify-center items-center text-center p-4'} transition-all duration-200 ${
selectedConsciousnessDimension === dimension.id
? `${dimension.color} text-white border-transparent shadow-lg`
: `border-gray-300 dark:border-gray-600 ${dimension.textColor} hover:scale-105 hover:border-gray-400 dark:hover:border-gray-500 hover:shadow-md`
} ${state === 'collapsed' ? 'h-32 text-lg' : ''}`}
onClick={() => handleConsciousnessDimensionSelect(dimension.id)}
>
<dimension.icon className={`${isMobile ? 'w-6 h-6' : 'w-8 h-8'} mb-2`} />
<div className={`font-medium ${isMobile ? 'mobile-consciousness-title' : ''}`}>{dimension.name}</div>
{!isMobile && (
<div className="text-xs opacity-70 mt-1">{dimension.description}</div>
)}
</Button>
))}
</div>
</div>
</div>
</div>
{/* Fixed Chat Bar at Bottom */}
<div className={`mx-auto p-6 transition-all duration-200 flex-shrink-0 ${
state === 'collapsed' ? 'max-w-5xl' : 'max-w-4xl'
}`}>
<div className="p-6">
<div className="flex items-center gap-3 mb-4">
<div className="h-3 w-3 bg-green-600 dark:bg-green-400 rounded-full animate-pulse"></div>
<span className="text-sm text-green-700 dark:text-green-400 font-medium">5D Consciousness Online</span>
</div>
<ChatPanel
messages={[]}
onSendMessage={handleSendMessage}
isLoading={isLoading}
isChatSelected={false}
onNewChat={handleNewChat}
onConsciousnessDimensionSelect={handleConsciousnessDimensionSelect}
selectedConsciousnessDimension={selectedConsciousnessDimension}
temperature={temperature}
isWelcomeMode={true}
consciousnessColor={getCurrentConsciousnessColors().textColor}
/>
</div>
</div>
</div>
) : (
<div className={`h-full flex transition-all duration-200 ${isMobile ? 'mobile-chat-container' : ''} ${state === 'collapsed' ? 'max-w-6xl mx-auto' : ''}`}>
{openEditorImage && !isMobile && (
<div className={`${state === 'collapsed' ? 'w-[60%] max-w-[1000px]' : 'w-[48%]'} min-w-[520px] pr-4`}>
<ImageEditorPane
imageBase64={openEditorImage}
prompt={openEditorPrompt || undefined}
onUpdate={(b64) => onUpdateEditorImage(b64)}
onClose={() => onCloseEditor()}
/>
</div>
)}
<div className="flex-1 flex flex-col min-w-0">
<div className={`flex-1 min-h-0 overflow-hidden ${isMobile ? 'mobile-chat-panel' : ''}`}>
<ChatPanel
messages={activeConversation?.messages || []}
onSendMessage={handleSendMessage}
isLoading={isLoading}
isChatSelected={!!activeChatId}
onNewChat={handleNewChat}
onConsciousnessDimensionSelect={handleConsciousnessDimensionSelect}
selectedConsciousnessDimension={selectedConsciousnessDimension}
temperature={temperature}
isWelcomeMode={false}
consciousnessColor={getCurrentConsciousnessColors().textColor}
onImageGeneratingChange={setIsImageGenerating}
isImageGenerating={isImageGenerating}
onOpenEditor={onOpenEditor}
imageProgress={imageProgress}
imageRequestId={imageRequestId}
/>
{/* Image loading accordion is rendered within ChatPanel to avoid duplication */}
</div>
</div>
</div>
)}
</ErrorBoundary>
</div>
</SidebarInset>
</div>
);
}
export default function Home() {
return <HomeContent />;
}
// Force redeploy Thu Jul 17 10:05:46 PM IDT 2025