aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
686 lines (683 loc) • 33.6 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { cn } from '../../lib/utilsComprehensive.js';
import { Hash, Search, Phone, Video, MoreVertical, Users, Image, File, Smile, X, Paperclip, MicOff, Mic, Send } from 'lucide-react';
import { useState, useRef, useEffect, useCallback } from 'react';
import '../../primitives/GlassCore.js';
import '../../primitives/glass/GlassAdvanced.js';
import '../../primitives/OptimizedGlassCore.js';
import '../../primitives/glass/OptimizedGlassAdvanced.js';
import '../../primitives/MotionNative.js';
import { MotionFramer } from '../../primitives/motion/MotionFramer.js';
import { GlassButton } from '../button/GlassButton.js';
import '../button/GlassFab.js';
import '../button/GlassMagneticButton.js';
import { CardHeader, CardTitle, CardContent } from '../card/index.js';
import { usePredictiveEngine, useInteractionRecorder } from '../advanced/GlassPredictiveEngine.js';
import { useAchievements } from '../advanced/GlassAchievementSystem.js';
import { useBiometricAdaptation } from '../advanced/GlassBiometricAdaptation.js';
import { useEyeTracking } from '../advanced/GlassEyeTracking.js';
import { useSpatialAudio } from '../advanced/GlassSpatialAudio.js';
import { GlassCard } from '../card/GlassCard.js';
/**
* GlassChat component
* A comprehensive chat interface with messages, participants, and real-time features
*/
const GlassChat = ({
messages,
participants = [],
currentUser,
title = "Chat",
subtitle,
enableReactions = true,
enableAttachments = true,
enableVoice = false,
enableTyping = true,
showParticipants = false,
showTimestamps = true,
showAvatars = true,
onSendMessage,
onMessageReaction,
onTyping,
className,
// Consciousness features
consciousness = false,
predictive = false,
adaptive = false,
eyeTracking = false,
spatialAudio = false,
trackAchievements = false,
...props
}) => {
const [newMessage, setNewMessage] = useState("");
const [isTyping, setIsTyping] = useState(false);
const [attachments, setAttachments] = useState([]);
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
const [isRecording, setIsRecording] = useState(false);
// Consciousness state
const [messageInteractionCounts, setMessageInteractionCounts] = useState({});
const [suggestedResponses, setSuggestedResponses] = useState([]);
const [adaptiveInputHeight, setAdaptiveInputHeight] = useState(40);
const [focusedMessage, setFocusedMessage] = useState(null);
const [conversationInsights, setConversationInsights] = useState(null);
const messagesEndRef = useRef(null);
const inputRef = useRef(null);
const fileInputRef = useRef(null);
// Consciousness hooks
const predictiveEngine = predictive ? usePredictiveEngine() : null;
const eyeTracker = eyeTracking ? useEyeTracking() : null;
const biometricAdapter = adaptive ? useBiometricAdaptation() : null;
const spatialAudioEngine = spatialAudio ? useSpatialAudio() : null;
const interactionRecorder = consciousness ? useInteractionRecorder() : null;
const achievementTracker = trackAchievements ? useAchievements() : null;
// Auto-scroll to bottom when new messages arrive
useEffect(() => {
messagesEndRef.current?.scrollIntoView({
behavior: "smooth"
});
}, [messages]);
// Handle typing indicator
useEffect(() => {
if (!enableTyping) return;
const timeout = setTimeout(() => {
if (isTyping) {
setIsTyping(false);
onTyping?.(false);
}
}, 2000);
return () => clearTimeout(timeout);
}, [newMessage, isTyping, enableTyping, onTyping]);
// Consciousness effects
// Biometric adaptation for input height and response timing
useEffect(() => {
if (!adaptive || !biometricAdapter) return;
const updateAdaptiveFeatures = () => {
const stressLevel = biometricAdapter.currentStressLevel;
// Adjust input height based on stress levels
if (stressLevel > 0.7) {
setAdaptiveInputHeight(60); // Larger input for stressed users
} else if (stressLevel < 0.3) {
setAdaptiveInputHeight(32); // Compact for calm users
} else {
setAdaptiveInputHeight(40); // Default
}
// Record biometric adaptation for achievements
if (trackAchievements && achievementTracker) {
achievementTracker.recordAction("chat_adaptive_ui", {
stressLevel: stressLevel,
inputHeight: adaptiveInputHeight
});
}
};
const interval = setInterval(updateAdaptiveFeatures, 2000);
return () => clearInterval(interval);
}, [adaptive, biometricAdapter, achievementTracker, trackAchievements, adaptiveInputHeight]);
// Eye tracking for message focus
useEffect(() => {
if (!eyeTracking || !eyeTracker) return;
// Eye tracking methods not available in current implementation
// eyeTracker.startTracking(handleGazeData);
// return () => eyeTracker.stopTracking();
}, [eyeTracking, eyeTracker, achievementTracker, trackAchievements]);
// Predictive response suggestions
useEffect(() => {
if (!predictive || !predictiveEngine) return;
const generateSuggestions = async () => {
try {
const conversationContext = {
messages: messages.slice(-5).map(m => ({
content: m.content,
sender: m.sender.name,
timestamp: m.timestamp
})),
currentUser: currentUser?.name || "User"
};
// Use available predictive data
const predictions = predictiveEngine.predictions;
const insights = predictiveEngine.insights;
// Generate simple suggestions based on available data
const suggestions = predictions.filter(p => p.type === "suggest").map(p => p.metadata?.text || "").filter(text => text.length > 0).slice(0, 3);
setSuggestedResponses(suggestions);
// Transform insights array to expected object format
const transformedInsights = insights && insights.length > 0 ? {
sentiment: insights.find(i => i.type === "sentiment")?.metadata?.sentiment || "neutral",
urgency: insights.find(i => i.type === "urgency")?.metadata?.urgency || "medium",
topics: insights.filter(i => i.type === "topic").map(i => i.metadata?.topic || "").filter(topic => topic.length > 0)
} : null;
setConversationInsights(transformedInsights);
} catch (error) {
console.warn("Predictive response generation failed:", error);
}
};
if (messages.length > 0) {
const timeoutId = setTimeout(generateSuggestions, 500);
return () => clearTimeout(timeoutId);
}
}, [messages, predictive, predictiveEngine, currentUser]);
// Spatial audio for message notifications
useEffect(() => {
if (!spatialAudio || !spatialAudioEngine) return;
const latestMessage = messages[messages.length - 1];
if (!latestMessage || latestMessage.sender.id === currentUser?.id) return;
// Play spatial audio notification for new messages
spatialAudioEngine.playGlassSound("message-notification", undefined, {
volume: 0.6,
reverb: 0.3
});
// Record achievement for audio engagement
if (trackAchievements && achievementTracker) {
achievementTracker.recordAction("spatial_audio_notification", {
messageId: latestMessage.id,
sender: latestMessage.sender.name
});
}
}, [messages, spatialAudio, spatialAudioEngine, currentUser, trackAchievements, achievementTracker]);
// Message interaction tracking
useEffect(() => {
if (!consciousness || !interactionRecorder) return;
const recordMessageInteraction = (messageId, interactionType) => {
setMessageInteractionCounts(prev => ({
...prev,
[messageId]: (prev[messageId] || 0) + 1
}));
// Create synthetic event for interaction recording
const syntheticEvent = {
currentTarget: {
id: `message-${messageId}`
},
type: interactionType === "click" ? "click" : interactionType === "hover" ? "mouseenter" : "focus"
};
if (interactionType === "click") {
interactionRecorder.recordClick(syntheticEvent);
} else if (interactionType === "hover") {
interactionRecorder.recordHover(syntheticEvent);
} else {
interactionRecorder.recordFocus(syntheticEvent);
}
};
// Set up global interaction listeners for message elements
const handleMessageClick = event => {
const messageElement = event.target.closest('[data-consciousness-message="true"]');
if (messageElement) {
const messageId = messageElement.getAttribute("data-message-id");
if (messageId) {
recordMessageInteraction(messageId, "message_click");
}
}
};
document.addEventListener("click", handleMessageClick);
return () => document.removeEventListener("click", handleMessageClick);
}, [consciousness, interactionRecorder, title]);
// Handle input change
const handleInputChange = useCallback(e => {
setNewMessage(e.target.value);
if (enableTyping && !isTyping && e.target.value.length > 0) {
setIsTyping(true);
onTyping?.(true);
}
}, [enableTyping, isTyping, onTyping]);
// Enhanced send message handler with consciousness tracking
const handleSendMessage = useCallback(() => {
if (!newMessage.trim() && attachments.length === 0) return;
// Record interaction for achievements and learning
if (consciousness && interactionRecorder) {
// Create synthetic event for message send interaction
const syntheticEvent = {
currentTarget: {
id: "chat-input"
},
type: "click"
};
interactionRecorder.recordClick(syntheticEvent);
}
// Track achievement for message sending
if (trackAchievements && achievementTracker) {
achievementTracker.recordAction("chat_message_sent", {
messageLength: newMessage.trim().length,
hasAttachments: attachments.length > 0,
timestamp: Date.now()
});
}
// Play spatial audio feedback for message sent
if (spatialAudio && spatialAudioEngine) {
spatialAudioEngine.playGlassSound("message-sent", undefined, {
volume: 0.4,
reverb: 0.2
});
}
onSendMessage?.(newMessage.trim(), attachments);
setNewMessage("");
setAttachments([]);
setIsTyping(false);
onTyping?.(false);
setSuggestedResponses([]); // Clear suggestions after sending
// Reset textarea height with adaptive sizing
if (inputRef.current) {
inputRef.current.style.height = "auto";
if (adaptive) {
inputRef.current.style.minHeight = `${adaptiveInputHeight}px`;
}
}
}, [newMessage, attachments, onSendMessage, onTyping, consciousness, interactionRecorder, title, trackAchievements, achievementTracker, spatialAudio, spatialAudioEngine, suggestedResponses, adaptive, adaptiveInputHeight]);
// Handle key press
const handleKeyPress = useCallback(e => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
}, [handleSendMessage]);
// Handle file attachment
const handleFileAttachment = useCallback(() => {
fileInputRef.current?.click();
}, []);
const handleFileChange = useCallback(e => {
const files = Array.from(e.target.files || []);
setAttachments(prev => [...prev, ...files]);
e.target.value = "";
}, []);
// Remove attachment
const removeAttachment = useCallback(index => {
setAttachments(prev => prev.filter((_, i) => i !== index));
}, []);
// Handle voice recording
const handleVoiceToggle = useCallback(() => {
setIsRecording(!isRecording);
// Voice recording logic would go here
}, [isRecording]);
// Format timestamp
const formatTimestamp = useCallback(date => {
const now = new Date();
const diff = now.getTime() - date.getTime();
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(diff / 3600000);
const days = Math.floor(diff / 86400000);
if (minutes < 1) return "Just now";
if (minutes < 60) return `${minutes}m ago`;
if (hours < 24) return `${hours}h ago`;
if (days === 1) return "Yesterday";
if (days < 7) return `${days}d ago`;
return date.toLocaleDateString();
}, []);
// Group messages by date
const groupedMessages = messages.reduce((groups, message) => {
const date = message.timestamp.toDateString();
if (!groups[date]) {
groups[date] = [];
}
groups[date].push(message);
return groups;
}, {});
// Auto-resize textarea
useEffect(() => {
if (inputRef.current) {
inputRef.current.style.height = "auto";
inputRef.current.style.height = `${Math.min(inputRef.current.scrollHeight, 120)}px`;
}
}, [newMessage]);
return jsx(MotionFramer, {
"data-glass-component": true,
preset: "fadeIn",
className: "glass-w-full glass-h-full",
children: jsxs(GlassCard, {
className: cn("flex flex-col h-full overflow-hidden", className),
...props,
children: [jsxs(CardHeader, {
className: 'pb-3 glass-border-b glass-border-white/10',
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between",
children: [jsxs("div", {
children: [jsxs(CardTitle, {
className: 'text-primary glass-text-lg font-semibold glass-flex glass-items-center glass-gap-2',
children: [jsx(Hash, {
className: 'w-5 h-5'
}), title]
}), subtitle && jsx("p", {
className: 'text-primary/60 glass-text-sm glass-mt-1',
children: subtitle
})]
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsx(GlassButton, {
variant: "ghost",
size: "sm",
className: "glass-p-2",
children: jsx(Search, {
className: 'w-4 h-4'
})
}), jsx(GlassButton, {
variant: "ghost",
size: "sm",
className: "glass-p-2",
children: jsx(Phone, {
className: 'w-4 h-4'
})
}), jsx(GlassButton, {
variant: "ghost",
size: "sm",
className: "glass-p-2",
children: jsx(Video, {
className: 'w-4 h-4'
})
}), jsx(GlassButton, {
variant: "ghost",
size: "sm",
className: "glass-p-2",
children: jsx(MoreVertical, {
className: 'w-4 h-4'
})
})]
})]
}), participants.length > 0 && jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2 glass-mt-2",
children: [jsx(Users, {
className: 'w-4 h-4 text-primary/60'
}), jsxs("span", {
className: 'text-primary/60 glass-text-sm',
children: [participants.filter(p => p.status === "online").length, " ", "online \u2022 ", participants.length, " total"]
})]
})]
}), jsxs("div", {
className: 'glass-flex glass-flex-1 overflow-hidden',
children: [jsxs("div", {
className: 'glass-flex-1 glass-flex glass-flex-col overflow-hidden',
children: [jsxs(CardContent, {
className: 'glass-flex-1 overflow-y-auto glass-p-4 glass-gap-4',
children: [Object.entries(groupedMessages).map(([date, dateMessages]) => jsxs("div", {
children: [jsx("div", {
className: "glass-flex glass-items-center glass-justify-center glass-my-6",
children: jsx("div", {
className: "glass-px-3 glass-py-1 glass-surface-subtle/10 glass-radius-full",
children: jsx("span", {
className: 'text-primary/60 glass-text-xs',
children: new Date(date).toLocaleDateString(undefined, {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
})
})
})
}), jsx("div", {
className: "glass-gap-3",
children: dateMessages.map((message, index) => {
const isCurrentUser = message.sender.id === currentUser?.id;
const prevMessage = dateMessages[index - 1];
const showAvatar = showAvatars && (!prevMessage || prevMessage.sender.id !== message.sender.id);
return jsxs("div", {
className: cn("flex glass-gap-3 group animate-slide-in-up", isCurrentUser ? "flex-row-reverse" : "flex-row", eyeTracking && focusedMessage === message.id && "consciousness-message-focused", consciousness && messageInteractionCounts[message.id] > 0 && "consciousness-message-interacted", predictive && conversationInsights?.sentiment === "positive" && "consciousness-positive-context", predictive && conversationInsights?.sentiment === "negative" && "consciousness-negative-context", predictive && conversationInsights?.urgency === "high" && "consciousness-urgent-context"),
style: {
animationDelay: `${Math.min(index, 20) * 20}ms`,
animationFillMode: "both"
},
"data-consciousness-message": "true",
"data-message-id": message.id,
"data-message-sentiment": conversationInsights?.sentiment,
"data-message-urgency": conversationInsights?.urgency,
"data-interaction-count": messageInteractionCounts[message.id] || 0,
"data-gaze-focused": focusedMessage === message.id,
children: [showAvatar && showAvatars && jsx("div", {
className: "glass-flex-shrink-0",
children: jsx("div", {
className: 'w-8 h-8 glass-radius-full glass-surface-subtle/20 glass-flex glass-items-center glass-justify-center',
children: message.sender.avatar ? jsx("img", {
src: message.sender.avatar,
alt: message.sender.name,
className: 'glass-w-full glass-h-full glass-radius-full object-cover'
}) : jsx("span", {
className: 'text-primary/80 glass-text-sm font-medium',
children: message.sender.name.charAt(0).toUpperCase()
})
})
}), jsxs("div", {
className: cn("flex-1 max-w-[70%]", isCurrentUser ? "items-end" : "items-start"),
children: [showAvatar && jsxs("div", {
className: cn("flex items-center glass-gap-2 glass-mb-1", isCurrentUser ? "justify-end" : "justify-start"),
children: [jsx("span", {
className: 'text-primary/80 glass-text-sm font-medium',
children: message.sender.name
}), message.sender.status && jsx("div", {
className: cn("w-2 h-2 glass-radius-full", message.sender.status === "online" ? "bg-green-400" : message.sender.status === "away" ? "bg-yellow-400" : message.sender.status === "busy" ? "bg-red-400" : "bg-gray-400")
})]
}), jsxs("div", {
className: cn("relative glass-px-4 glass-py-2 rounded-2xl max-w-full break-words", isCurrentUser ? "bg-primary text-primary-foreground ml-auto" : "bg-white/10 glass-text-primary"),
children: [message.replyTo && jsx("div", {
className: 'glass-text-xs opacity-70 mb-2 pb-2 glass-border-b glass-border-current glass-border-opacity-20',
children: "Replying to message"
}), jsx("div", {
className: "glass-text-sm",
children: message.content
}), message.attachments && message.attachments.length > 0 && jsx("div", {
className: "glass-mt-2 glass-gap-2",
children: message.attachments.map((attachment, attIndex) => jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2 glass-p-2 glass-surface-dark/20 glass-radius-md",
children: [attachment.type === "image" && jsx(Image, {
className: 'w-4 h-4'
}), attachment.type === "file" && jsx(File, {
className: 'w-4 h-4'
}), jsx("span", {
className: 'glass-text-xs truncate',
children: attachment.name
})]
}, attIndex))
}), message.reactions && message.reactions.length > 0 && jsx("div", {
className: "glass-flex glass-gap-1 glass-mt-2",
children: message.reactions.map((reaction, reactionIndex) => jsxs(GlassButton, {
variant: "ghost",
size: "sm",
onClick: e => onMessageReaction?.(message.id, reaction.emoji),
className: 'h-6 glass-px-2 glass-text-xs',
children: [reaction.emoji, " ", reaction.count]
}, reactionIndex))
})]
}), jsxs("div", {
className: cn("flex items-center glass-gap-2 glass-mt-1 glass-text-xs glass-text-primary/50", isCurrentUser ? "justify-end" : "justify-start"),
children: [showTimestamps && jsx("span", {
children: formatTimestamp(message.timestamp)
}), message.edited && jsx("span", {
children: "(edited)"
})]
})]
}), jsx("div", {
className: cn("opacity-0 group-hover:opacity-100 transition-opacity flex glass-gap-1", isCurrentUser ? "flex-row-reverse" : "flex-row"),
children: enableReactions && jsx(GlassButton, {
variant: "ghost",
size: "sm",
onClick: e => setShowEmojiPicker(!showEmojiPicker),
className: "glass-p-1",
children: jsx(Smile, {
className: 'w-3 h-3'
})
})
})]
}, message.id);
})
})]
}, date)), jsx("div", {
ref: messagesEndRef
})]
}), jsxs("div", {
className: cn("border-t border-white/10 glass-p-4", adaptive && "consciousness-adaptive-input", predictive && suggestedResponses.length > 0 && "consciousness-predictive-suggestions", consciousness && "consciousness-enhanced-input"),
"data-consciousness-input-area": "true",
"data-adaptive-height": adaptiveInputHeight,
"data-has-suggestions": suggestedResponses.length > 0,
"data-conversation-sentiment": conversationInsights?.sentiment,
"data-conversation-urgency": conversationInsights?.urgency,
children: [predictive && suggestedResponses.length > 0 && jsxs("div", {
className: 'mb-3',
"data-consciousness-suggestions": "true",
children: [jsxs("div", {
className: 'glass-text-xs text-primary/60 mb-2 glass-flex glass-items-center glass-gap-1',
children: [jsx("span", {
children: "Suggested responses"
}), conversationInsights?.sentiment && jsx("span", {
className: cn("glass-px-2 glass-py-0.5 glass-radius-md glass-text-xs", conversationInsights.sentiment === "positive" ? "bg-green-500/20 text-green-300" : conversationInsights.sentiment === "negative" ? "bg-red-500/20 text-red-300" : "bg-yellow-500/20 text-yellow-300"),
children: conversationInsights.sentiment
}), conversationInsights?.urgency === "high" && jsx("span", {
className: "glass-px-2 glass-py-0.5 glass-radius-md glass-text-xs glass-surface-primary/20 glass-text-secondary",
children: "urgent"
})]
}), jsx("div", {
className: "glass-flex glass-gap-2 glass-flex-wrap",
children: suggestedResponses.map((suggestion, index) => jsx(GlassButton, {
variant: "ghost",
size: "sm",
onClick: () => setNewMessage(suggestion),
className: 'glass-text-xs glass-px-3 glass-py-1 h-auto consciousness-suggestion-button',
"data-suggestion-index": index,
children: suggestion
}, index))
})]
}), predictive && conversationInsights?.topics && conversationInsights.topics.length > 0 && jsxs("div", {
className: 'mb-3',
"data-consciousness-insights": "true",
children: [jsx("div", {
className: 'glass-text-xs text-primary/60 mb-2',
children: "Conversation topics:"
}), jsx("div", {
className: "glass-flex glass-gap-1 glass-flex-wrap",
children: conversationInsights.topics.map((topic, index) => jsx("span", {
className: 'glass-px-2 glass-py-0.5 glass-radius-md glass-text-xs glass-surface-primary/20 text-primary',
children: topic
}, index))
})]
}), attachments.length > 0 && jsx("div", {
className: 'glass-flex glass-gap-2 mb-3 overflow-x-auto',
children: attachments.map((file, index) => jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2 glass-surface-subtle/10 glass-radius-md glass-px-3 glass-py-2",
children: [jsx(File, {
className: 'w-4 h-4'
}), jsx("span", {
className: 'glass-text-sm text-primary truncate max-w-32',
children: file.name
}), jsx(GlassButton, {
variant: "ghost",
size: "sm",
onClick: e => removeAttachment(index),
className: 'glass-p-1 h-auto',
children: jsx(X, {
className: 'w-3 h-3'
})
})]
}, index))
}), jsxs("div", {
className: "glass-flex glass-items-end glass-gap-2",
children: [enableAttachments && jsx(GlassButton, {
variant: "ghost",
size: "sm",
onClick: handleFileAttachment,
className: "glass-p-2",
children: jsx(Paperclip, {
className: 'w-4 h-4'
})
}), enableVoice && jsx(GlassButton, {
variant: isRecording ? "destructive" : "ghost",
size: "sm",
onClick: handleVoiceToggle,
className: "glass-p-2",
children: isRecording ? jsx(MicOff, {
className: 'w-4 h-4'
}) : jsx(Mic, {
className: 'w-4 h-4'
})
}), jsxs("div", {
className: 'glass-flex-1 relative',
children: [jsx("textarea", {
ref: inputRef,
value: newMessage,
onChange: handleInputChange,
onKeyPress: handleKeyPress,
placeholder: predictive && conversationInsights?.urgency === "high" ? "Type urgent response..." : adaptive ? "Type a message (adaptive sizing enabled)..." : "Type a message...",
className: cn("w-full bg-glass-fill ring-1 ring-white/10 glass-radius-lg glass-px-4 glass-py-2 glass-text-primary placeholder-white/50 focus:outline-none focus:ring-white/30 resize-none max-h-[120px]", adaptive && "consciousness-adaptive-textarea", predictive && conversationInsights?.urgency === "high" && "consciousness-urgent-input", eyeTracking && "consciousness-eye-trackable"),
style: {
minHeight: adaptive ? `${adaptiveInputHeight}px` : "40px"
},
rows: 1,
"data-consciousness-input": "true",
"data-adaptive-height": adaptiveInputHeight,
"data-urgency": conversationInsights?.urgency,
"data-eye-tracking": String(!!eyeTracking)
}), jsx(GlassButton, {
variant: "ghost",
size: "sm",
onClick: e => setShowEmojiPicker(!showEmojiPicker),
className: 'absolute right-2 glass-top-1/2 transform -translate-y-1/2 glass-p-1',
children: jsx(Smile, {
className: 'w-4 h-4'
})
})]
}), jsx(GlassButton, {
variant: "primary",
size: "sm",
onClick: handleSendMessage,
disabled: !newMessage.trim() && attachments.length === 0,
className: cn("glass-p-2", consciousness && "consciousness-send-button", predictive && conversationInsights?.urgency === "high" && "consciousness-urgent-send", adaptive && "consciousness-adaptive-send"),
consciousness: consciousness,
predictive: predictive,
adaptive: adaptive,
spatialAudio: spatialAudio,
trackAchievements: trackAchievements,
"data-consciousness-send": "true",
"data-urgency": conversationInsights?.urgency,
"data-has-attachments": attachments.length > 0,
"data-message-length": newMessage.trim().length,
children: jsx(Send, {
className: 'w-4 h-4'
})
})]
}), jsx("input", {
ref: fileInputRef,
type: "file",
multiple: true,
onChange: handleFileChange,
className: 'hidden glass-touch-target glass-contrast-guard',
accept: "image/*,video/*,audio/*,.pdf,.doc,.docx,.txt"
})]
})]
}), showParticipants && participants.length > 0 && jsxs("div", {
className: 'w-64 glass-border-l glass-border-white/10 glass-flex glass-flex-col',
children: [jsx("div", {
className: "glass-p-4 glass-border-b glass-border-white/10",
children: jsxs("h3", {
className: 'text-primary font-medium glass-flex glass-items-center glass-gap-2',
children: [jsx(Users, {
className: 'w-4 h-4'
}), "Participants (", participants.length, ")"]
})
}), jsx("div", {
className: 'glass-flex-1 overflow-y-auto glass-p-2',
children: participants.map(participant => jsxs("div", {
className: 'glass-flex glass-items-center glass-gap-3 glass-p-2 glass-radius-lg hover:glass-surface-subtle/5',
children: [jsxs("div", {
className: 'relative',
children: [jsx("div", {
className: 'w-8 h-8 glass-radius-full glass-surface-subtle/20 glass-flex glass-items-center glass-justify-center',
children: participant.avatar ? jsx("img", {
src: participant.avatar,
alt: participant.name,
className: 'glass-w-full glass-h-full glass-radius-full object-cover'
}) : jsx("span", {
className: 'text-primary/80 glass-text-sm font-medium',
children: participant.name.charAt(0).toUpperCase()
})
}), jsx("div", {
className: cn("absolute -bottom-1 -right-1 w-3 h-3 glass-radius-full border-2 border-black", participant.status === "online" ? "bg-green-400" : participant.status === "away" ? "bg-yellow-400" : participant.status === "busy" ? "bg-red-400" : "bg-gray-400")
})]
}), jsxs("div", {
className: "glass-flex-1 glass-min-w-0",
children: [jsx("p", {
className: 'text-primary glass-text-sm font-medium truncate',
children: participant.name
}), jsx("p", {
className: 'text-primary/60 glass-text-xs',
children: participant.status === "online" ? "Online" : participant.status === "away" ? "Away" : participant.status === "busy" ? "Busy" : participant.lastSeen ? `Last seen ${formatTimestamp(participant.lastSeen)}` : "Offline"
})]
})]
}, participant.id))
})]
})]
})]
})
});
};
export { GlassChat, GlassChat as default };
//# sourceMappingURL=GlassChat.js.map