Ana içeriğe atla

Sign in or register

Save your chat history, get personalized recommendations and discover more.

OR

Welcome back

OR

Create your account

Sign in with phone

We will send a verification code to your phone number.

Verification code

Enter the 6-digit code sent to your phone.

Forgot Password

Enter your email address and we will send you a password reset link.

Set New Password

Profil

U
User
user@example.com

Settings

Profile
U
0/160 characters
Appearance
Theme
Interface appearance
Language
Interface language
Font Size
Mesaj metni boyutu
Message Density
Spacing between messages
Chat Width
Message area width
AI Preferences
Preferred Model
Default AI model
Auto-Save History
Automatically save chats
Response Length
Length of AI responses
Conversation Memory
Allow AI to remember previous messages
AI Response Language
Language for AI responses
Code Theme
Color scheme for code blocks
Bildirimler
Push Bildirimleri
Receive browser notifications
Email Notifications
Receive email for important updates
Sesler
Enable interface sounds
Privacy
Use Chat History
Allow AI to learn from previous chats
Analitik
Share anonymous usage data
Chat History
Export Chats
Download all chat history as JSON
Delete All Chats
Permanently delete all chat history
Data Management
Download All My Data
Download all data associated with your account (GDPR)
Clear Search History
Delete your web search history
Freeze Account
Temporarily disable your account
Security Status: Good
2FA not active - Enable it to protect your account
Two-Factor Authentication
Extra Protection with 2FA
Inactive
Two-factor authentication adds an extra layer of security to your account.
Connected Accounts
Google Hesabi
Not connected
Password
Active Sessions
PC
Bu Cihaz Aktif
Chrome - Su an aktif
Recent Login Activity
Last Login
Loading information...
-
Danger Zone

This action is irreversible. All your data will be deleted.

Security

Security Status: Good
2FA not active - Enable it to protect your account
Two-Factor Authentication
Extra Protection with 2FA
Inactive
Two-factor authentication adds an extra layer of security to your account. You will need to verify with your phone app on each login.
Connected Accounts
Google Hesabi
Not connected
Password
Use uppercase/lowercase letters, numbers, and special characters for a strong password
Active Sessions
PC
Bu Cihaz Aktif
Chrome • Currently active
Recent Login Activity
Last Login
Loading information...
-
Danger Zone

This action is irreversible. All your data will be deleted.

LyDian AI
LyDian
How Can I Help You?
AI is thinking
0 characters ~0 token
'; } // DESKTOP: Side Panel (manus.com style) if (window.innerWidth > 768) { var panel = document.getElementById('codeSidePanel'); var iframe = document.getElementById('sidePanelIframe'); var sourceEl = document.getElementById('sidePanelSource'); if (!panel || !iframe) return; // Set content iframe.srcdoc = htmlContent; sourceEl.textContent = code; // Reset tabs to Preview var tabs = panel.querySelectorAll('.code-preview-tab[data-view]'); tabs.forEach(function (t) { t.classList.remove('active'); }); var previewTab = panel.querySelector('.code-preview-tab[data-view="preview"]'); if (previewTab) previewTab.classList.add('active'); iframe.style.display = 'block'; sourceEl.style.display = 'none'; // Open panel panel.classList.add('open'); document.body.classList.add('side-panel-open'); // Setup event listeners (only once) if (!panel._listenersAttached) { panel._listenersAttached = true; // Tab switching tabs.forEach(function (tab) { tab.addEventListener('click', function () { tabs.forEach(function (t) { t.classList.remove('active'); }); tab.classList.add('active'); if (tab.dataset.view === 'preview') { iframe.style.display = 'block'; sourceEl.style.display = 'none'; } else { iframe.style.display = 'none'; sourceEl.style.display = 'block'; } }); }); // Refresh document.getElementById('sidePanelRefresh').addEventListener('click', function () { var currentSrcdoc = iframe.srcdoc; iframe.srcdoc = ''; setTimeout(function () { iframe.srcdoc = currentSrcdoc; }, 50); }); // Close document.getElementById('sidePanelClose').addEventListener('click', function () { closeSidePanel(); }); } // ESC to close var escHandler = function (e) { if (e.key === 'Escape') { closeSidePanel(); document.removeEventListener('keydown', escHandler); } }; document.addEventListener('keydown', escHandler); } else { // MOBILE: Full-screen overlay (existing behavior) var overlay = document.createElement('div'); overlay.className = 'code-preview-overlay'; overlay.innerHTML = '
' + '
' + '
' + '
Preview
' + '
Code
' + '
' + '
' + '' + '' + '
' + '
' + '' + '' + '
'; document.body.appendChild(overlay); var mobileIframe = overlay.querySelector('iframe'); mobileIframe.srcdoc = htmlContent; var mobileSource = overlay.querySelector('.code-preview-source'); mobileSource.textContent = code; var mobileTabs = overlay.querySelectorAll('.code-preview-tab[data-view]'); mobileTabs.forEach(function (tab) { tab.addEventListener('click', function () { mobileTabs.forEach(function (t) { t.classList.remove('active'); }); tab.classList.add('active'); if (tab.dataset.view === 'preview') { mobileIframe.style.display = 'block'; mobileSource.style.display = 'none'; } else { mobileIframe.style.display = 'none'; mobileSource.style.display = 'block'; } }); }); overlay.querySelector('#previewRefreshBtn').addEventListener('click', function () { mobileIframe.srcdoc = htmlContent; }); overlay.querySelector('#previewCloseBtn').addEventListener('click', function () { overlay.remove(); }); overlay.addEventListener('click', function (e) { if (e.target === overlay) overlay.remove(); }); var mobileEsc = function (e) { if (e.key === 'Escape') { overlay.remove(); document.removeEventListener('keydown', mobileEsc); } }; document.addEventListener('keydown', mobileEsc); } } function closeSidePanel() { var panel = document.getElementById('codeSidePanel'); if (panel) panel.classList.remove('open'); document.body.classList.remove('side-panel-open'); } function copyCodeBlock(btn) { var code = btn.closest('.code-block-container').querySelector('code').textContent; navigator.clipboard .writeText(code) .then(function () { var original = btn.innerHTML; btn.innerHTML = ' Kopyalandi'; setTimeout(function () { btn.innerHTML = original; }, 2000); }) .catch(function () { // Fallback for older browsers var textarea = document.createElement('textarea'); textarea.value = code; document.body.appendChild(textarea); textarea.select(); document.execCommand('copy'); document.body.removeChild(textarea); var original = btn.innerHTML; btn.innerHTML = ' Kopyalandi'; setTimeout(function () { btn.innerHTML = original; }, 2000); }); } /** * Generate contextual suggestions based on conversation * LyDian-style follow-up suggestions */ function generateSuggestions(aiResponse, messages) { const suggestions = []; const lastUserMessage = messages .filter(m => m.role === 'user') .pop() ?.content?.toLowerCase() || ''; const responseLower = aiResponse.toLowerCase(); // Topic-based suggestions - WITH ICONS const topicSuggestions = { // Programlama ve Teknoloji kod: [ { text: 'Bu kodu optimize edebilir misin?', icon: '' }, { text: 'Do you have debugging suggestions?', icon: '🔧' }, { text: 'Can you show an alternative approach?', icon: '🔄' }, ], python: [ { text: 'Bu kodu daha Pythonic yapabilir misin?', icon: '🐍' }, { text: 'Unit test ornekleri ekler misin?', icon: '' }, { text: 'Suggest performance improvements', icon: '🚀' }, ], javascript: [ { text: 'Modern ES6+ syntax ile yaz', icon: '✨' }, { text: 'Show the TypeScript version', icon: '📘' }, { text: 'Error handling ekle', icon: '🛡️' }, ], // Genel Bilgi tarih: [ { text: 'What are the key events of this period?', icon: '📜' }, { text: 'Can you explain in more detail?', icon: '🔍' }, { text: 'What are its effects today?', icon: '🌍' }, ], // Health health: [ { text: 'Belirtiler ne anlama geliyor?', icon: '🩺' }, { text: 'Which specialist should I see?', icon: '👨‍⚕️' }, { text: 'What are the prevention methods?', icon: '💪' }, ], // Finans finans: [ { text: 'Suggest investment strategies', icon: '📈' }, { text: 'Risk analizi yapabilir misin?', icon: '⚖️' }, { text: 'What are the alternative options?', icon: '💡' }, ], // Education learn: [ { text: 'Can you give practical examples?', icon: '📝' }, { text: 'Do you have resource recommendations?', icon: '📚' }, { text: 'Explain step by step', icon: '🎯' }, ], // Yemek ve Tarif tarif: [ { text: 'Malzeme listesi verir misin?', icon: '🥗' }, { text: 'How long is the cooking time?', icon: '⏱️' }, { text: 'Is there a vegan alternative?', icon: '🌱' }, ], // Seyahat seyahat: [ { text: 'En iyi zaman ne zaman?', icon: '📅' }, { text: 'Can you suggest a budget?', icon: '💰' }, { text: 'What are the must-see places?', icon: '📍' }, ], }; // General suggestions (fallback) - WITH ICONS const generalSuggestions = [ { text: 'Can you explain in more detail?', icon: '🔍' }, { text: 'Can you give other examples?', icon: '💡' }, { text: 'Bu konuda nelere dikkat etmeliyim?', icon: '' }, { text: 'Can you summarize?', icon: '📋' }, ]; // Suggestions based on response content if ( responseLower.includes('kod') || responseLower.includes('function') || responseLower.includes('class') ) { suggestions.push(...(topicSuggestions.kod || [])); } if (responseLower.includes('python') || lastUserMessage.includes('python')) { suggestions.push(...(topicSuggestions.python || [])); } if ( responseLower.includes('javascript') || responseLower.includes('js') || lastUserMessage.includes('javascript') ) { suggestions.push(...(topicSuggestions.javascript || [])); } if ( responseLower.includes('tarih') || responseLower.includes('century') || responseLower.includes('period') ) { suggestions.push(...(topicSuggestions.tarih || [])); } if ( responseLower.includes('health') || responseLower.includes('disease') || responseLower.includes('tedavi') ) { suggestions.push(...(topicSuggestions.health || [])); } if ( responseLower.includes('invest') || responseLower.includes('finans') || responseLower.includes('para') ) { suggestions.push(...(topicSuggestions.finans || [])); } if ( responseLower.includes('tarif') || responseLower.includes('yemek') || responseLower.includes('cook') ) { suggestions.push(...(topicSuggestions.tarif || [])); } if ( responseLower.includes('seyahat') || responseLower.includes('gezi') || responseLower.includes('tur') ) { suggestions.push(...(topicSuggestions.seyahat || [])); } // If no topic-based suggestion found, use general suggestions if (suggestions.length === 0) { suggestions.push(...generalSuggestions); } // Return unique and max 3 suggestions const uniqueSuggestions = [...new Map(suggestions.map(s => [s.text, s])).values()]; return uniqueSuggestions.slice(0, 3); } function startNewChat() { // Save current chat before clearing (prevents history loss) if (state.messages.length > 0 && state.currentChatId) { saveCurrentChat(); saveCurrentChatToHistory(); } state.messages = []; state.currentChatId = null; // Desktop: Reset to initial centered state if (window.innerWidth > 768 && document.body.classList.contains('desktop-centered')) { document.body.classList.remove('chat-active'); document.body.classList.add('chat-initial'); } elements.messagesContainer.innerHTML = `
${getTimeGreeting()}
How Can I Help You?
`; elements.emptyState = document.getElementById('emptyState'); initParticles(); } // 📥 Download conversation in multiple formats function downloadConversation(format) { if (state.messages.length === 0) { showToast('Indirilecek mesaj yok!'); return; } let content = ''; let mimeType = ''; let filename = `chat-${Date.now()}`; switch (format) { case 'json': content = JSON.stringify(state.messages, null, 2); mimeType = 'application/json'; filename += '.json'; break; case 'txt': content = state.messages .map(m => `[${m.role.toUpperCase()}]: ${m.content}`) .join('\n\n'); mimeType = 'text/plain'; filename += '.txt'; break; case 'md': content = '# LYDIAN Chat Export\n\n'; content += state.messages .map(m => { const prefix = m.role === 'user' ? '### 👤 User' : '### 🤖 Assistant'; return `${prefix}\n\n${m.content}\n\n---\n`; }) .join('\n'); mimeType = 'text/markdown'; filename += '.md'; break; case 'csv': content = 'Role,Content,Timestamp\n'; content += state.messages .map(m => { const escapedContent = (m.content || '').replace(/"/g, '""'); return `"${m.role}","${escapedContent}","${new Date().toISOString()}"`; }) .join('\n'); mimeType = 'text/csv'; filename += '.csv'; break; } const blob = new Blob([content], { type: mimeType }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showToast(`Sohbet ${format.toUpperCase()} downloaded!`); } // 🔗 Share conversation - Secure encrypted share async function shareConversation() { if (state.messages.length === 0) { showToast('No messages to share', 'warning'); return; } try { // Show loading showToast('Creating share link...', 'info'); // Get title from first user message const title = getTitle(); // Call secure share API const response = await fetch('/api/share/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages: state.messages, title, expiresIn: '24h', whatsAppOnly: true, }), }); const result = await response.json(); if (result.success) { showShareModal(result.shareUrl, result.expiresAt); } else { showToast(result.error || 'Sharing failed', 'error'); } } catch (error) { console.error('[SHARE_ERR]', error); showToast('Sharing failed. Please try again.', 'error'); } } // 📤 Show share modal function showShareModal(shareUrl, expiresAt) { // Remove existing modal if any const existingModal = document.getElementById('shareModalOverlay'); if (existingModal) existingModal.remove(); const expiresDate = new Date(expiresAt).toLocaleString('tr-TR'); const whatsappUrl = `https://wa.me/?text=${encodeURIComponent('LyDian AI Sohbet: ' + shareUrl)}`; const modal = document.createElement('div'); modal.id = 'shareModalOverlay'; modal.className = 'share-modal-overlay'; modal.innerHTML = `

Share Chat

Share via WhatsApp
`; document.body.appendChild(modal); // Close on overlay click modal.addEventListener('click', e => { if (e.target === modal) modal.remove(); }); } // Copy share URL to clipboard function copyShareUrl() { const input = document.getElementById('shareUrlInput'); if (input) { navigator.clipboard .writeText(input.value) .then(() => showToast('Link copied!', 'success')) .catch(() => showToast('Copy failed', 'error')); } } function copyToClipboard(text) { if (navigator.clipboard) { navigator.clipboard .writeText(text) .then(() => { showToast('Chat copied to clipboard!'); }) .catch(() => { showToast('Panoya kopyalanamadi!'); }); } else { // Fallback for older browsers const textarea = document.createElement('textarea'); textarea.value = text; document.body.appendChild(textarea); textarea.select(); document.execCommand('copy'); document.body.removeChild(textarea); showToast('Chat copied to clipboard!'); } } function getTitle() { const firstMessage = state.messages.find(m => m.role === 'user'); if (firstMessage) { return ( firstMessage.content.substring(0, 40) + (firstMessage.content.length > 40 ? '...' : '') ); } return 'New Chat'; } /** * Premium Theme System with Dark/Light/System support * @param {string} themeName - Theme name (dark, light, system) * @param {boolean} silent - If true, don't show notification (used on page load) */ function setTheme(themeName, silent = false) { // Store the user's preference localStorage.setItem('lydian_theme', themeName); // Remove existing theme attribute document.body.removeAttribute('data-theme'); if (themeName === 'system') { // System theme - detect user preference document.body.setAttribute('data-theme', 'system'); } else if (themeName === 'light') { document.body.setAttribute('data-theme', 'light'); } else if (themeName !== 'dark') { // Legacy themes (ocean, forest, etc.) - fallback to dark document.body.setAttribute('data-theme', themeName); } // 'dark' is the default, no attribute needed updateActiveThemeOption(); // Swap highlight.js theme for light/dark var hljsDark = document.getElementById('hljs-dark-theme'); var hljsLight = document.getElementById('hljs-light-theme'); if (hljsDark && hljsLight) { var isLight = themeName === 'light' || (themeName === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches); hljsDark.disabled = isLight; hljsLight.disabled = !isLight; } } function getThemeName(theme) { const names = { dark: 'Koyu', light: 'Acik', system: 'Sistem' }; return names[theme] || theme.charAt(0).toUpperCase() + theme.slice(1); } function updateActiveThemeOption() { const currentTheme = localStorage.getItem('lydian_theme') || 'dark'; document.querySelectorAll('.theme-option').forEach(option => { if (option.dataset.theme === currentTheme) { option.classList.add('active'); } else { option.classList.remove('active'); } }); } // Listen for system theme changes if (window.matchMedia) { window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { const currentTheme = localStorage.getItem('lydian_theme'); if (currentTheme === 'system') { // Re-apply system theme to trigger CSS update document.body.removeAttribute('data-theme'); document.body.setAttribute('data-theme', 'system'); } }); } // 📁 Handle file upload function handleFileUpload(file) { // Max file size: 10MB const maxSize = 10 * 1024 * 1024; if (file.size > maxSize) { showToast("File size cannot exceed 10MB"); return; } // Check file type const isImage = file.type.startsWith('image/'); const allowedTypes = [ 'image/', 'application/pdf', 'text/', 'application/msword', 'application/vnd.openxmlformats', ]; const isAllowed = allowedTypes.some( type => file.type.startsWith(type) || file.type.includes(type) ); if (!isAllowed) { showToast('Bu dosya turu desteklenmiyor'); return; } // Show toast for feedback showToast(`Uploading file: ${file.name}`); // For images, show preview in input area if (isImage) { const reader = new FileReader(); reader.onload = function (e) { // Store the image data for sending with the message state.pendingFile = { name: file.name, type: file.type, size: file.size, data: e.target.result, }; // Show preview badge showFilePreview(file.name, e.target.result, true); }; reader.readAsDataURL(file); } else { // For documents, store reference state.pendingFile = { name: file.name, type: file.type, size: file.size, }; // Show file badge showFilePreview(file.name, null, false); } } // Show file preview badge near input - LyDian style function showFilePreview(fileName, imageData, isImage) { // Remove existing preview const existing = document.getElementById('filePreviewBadge'); if (existing) existing.remove(); const badge = document.createElement('div'); badge.id = 'filePreviewBadge'; badge.style.cssText = ` display: inline-flex; align-items: center; gap: 10px; padding: ${isImage ? '6px 10px 6px 6px' : '10px 14px'}; background: rgba(32, 33, 36, 0.95); border: 1px solid rgba(255, 255, 255, 0.12); border-radius: ${isImage ? '12px' : '10px'}; margin-bottom: 10px; max-width: 280px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); animation: slideInUp 0.2s ease-out; `; const preview = isImage && imageData ? `` : `
`; // Get file extension const ext = fileName.split('.').pop().toUpperCase(); const fileTypeLabel = isImage ? 'Image' : ext; badge.innerHTML = ` ${preview}
${fileName} ${fileTypeLabel}
`; // Insert before input wrapper const inputWrapper = document.querySelector('.input-wrapper'); if (inputWrapper && inputWrapper.parentNode) { inputWrapper.parentNode.insertBefore(badge, inputWrapper); } } // Clear file preview function clearFilePreview() { const badge = document.getElementById('filePreviewBadge'); if (badge) badge.remove(); state.pendingFile = null; // Clear file input const fileInput = document.getElementById('mainFileInput'); if (fileInput) fileInput.value = ''; } /** * Premium Toast Notification System * @param {string} message - Toast message * @param {string} type - 'success' | 'error' | 'info' | 'welcome' * @param {Object} options - Additional options */ function showToast(message, type = 'info', options = {}) { // Remove any existing toast const existingToast = document.querySelector('.toast-notification'); if (existingToast) existingToast.remove(); const toast = document.createElement('div'); toast.className = `toast-notification ${type}`; const iconSvg = type === 'success' ? '' : type === 'error' ? '' : type === 'welcome' ? '' : ''; toast.innerHTML = `
${options.avatar || iconSvg}
${message}
${options.subtitle ? `
${options.subtitle}
` : ''}
`; document.body.appendChild(toast); // Trigger animation requestAnimationFrame(() => { requestAnimationFrame(() => { toast.classList.add('show'); }); }); // Auto remove const duration = options.duration || 3000; setTimeout(() => { toast.classList.remove('show'); toast.classList.add('hide'); setTimeout(() => toast.remove(), 400); }, duration); return toast; } /** * Premium Welcome Toast for Login * Shows personalized greeting with avatar and last login info */ function showWelcomeToast(user) { const userName = user.displayName || user.name || user.username || 'User'; const initial = userName.charAt(0).toUpperCase(); const lastLogin = localStorage.getItem('lydian_last_login'); const isReturning = !!lastLogin; // Save current login time localStorage.setItem('lydian_last_login', new Date().toISOString()); let subtitle = ''; if (isReturning && lastLogin) { const lastDate = new Date(lastLogin); const now = new Date(); const diffMs = now - lastDate; const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / 86400000); if (diffMins < 60) { subtitle = `Last login: ${diffMins} minutes ago`; } else if (diffHours < 24) { subtitle = `Last login: ${diffHours} hours ago`; } else if (diffDays < 7) { subtitle = `Last login: ${diffDays} days ago`; } else { subtitle = `Last login: ${lastDate.toLocaleDateString('en-US')}`; } } else { subtitle = "Welcome to AILYDIAN!"; } const greeting = isReturning ? `Welcome back, ${userName}!` : `Welcome, ${userName}!`; showToast(greeting, 'welcome', { avatar: initial, subtitle: subtitle, duration: 4000, }); } function initParticles() { const particlesContainer = document.getElementById('particles'); if (!particlesContainer) return; particlesContainer.innerHTML = ''; for (let i = 0; i < 30; i++) { const particle = document.createElement('div'); particle.className = 'particle'; particle.style.left = Math.random() * 100 + '%'; particle.style.animationDelay = Math.random() * 10 + 's'; particle.style.animationDuration = 10 + Math.random() * 5 + 's'; particlesContainer.appendChild(particle); } } // Copy message window.copyMessage = async function (button) { const messageContent = button.closest('.message-content').querySelector('.message-text'); const text = messageContent.innerText; try { await navigator.clipboard.writeText(text); const originalText = button.innerHTML; button.innerHTML = ` Copied! `; button.style.color = '#E97451'; setTimeout(() => { button.innerHTML = originalText; button.style.color = ''; }, 2000); } catch (error) { console.error('Copy error:', error); alert('Copy failed!'); } }; // Feedback message (thumbs up/down) window.feedbackMessage = function (button, type) { const actionsDiv = button.closest('.message-actions'); if (!actionsDiv) return; const allFeedbackBtns = actionsDiv.querySelectorAll('.feedback-btn'); const isAlreadyActive = button.classList.contains('active-' + type); // Remove all active states first allFeedbackBtns.forEach(btn => { btn.classList.remove('active-up', 'active-down'); }); // Toggle: if same button clicked again, just remove (already done above) if (isAlreadyActive) { removeFeedback(actionsDiv); return; } // Set active state button.classList.add('active-' + type); // Get message text for identification const messageEl = button.closest('.message'); const messageText = messageEl ? messageEl.querySelector('.message-text') : null; const msgContent = messageText ? messageText.innerText.substring(0, 200) : ''; const msgId = messageEl ? messageEl.dataset.msgId || hashString(msgContent) : Date.now().toString(); // Store in localStorage storeFeedback(msgId, type, msgContent); // Send to server if authenticated syncFeedbackToServer(msgId, type, msgContent); showToast( type === 'up' ? 'Geri bildirim kaydedildi' : 'Geri bildirim kaydedildi', 'success' ); }; function hashString(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = (hash << 5) - hash + char; hash |= 0; } return 'fb_' + Math.abs(hash).toString(36); } function storeFeedback(msgId, type, content) { try { const feedbackData = JSON.parse(localStorage.getItem('lydian_feedback') || '{}'); feedbackData[msgId] = { type: type, content: content, timestamp: Date.now(), conversationId: state.currentConversationId || null, }; // Keep max 200 feedback entries const keys = Object.keys(feedbackData); if (keys.length > 200) { const sorted = keys.sort( (a, b) => feedbackData[a].timestamp - feedbackData[b].timestamp ); sorted.slice(0, keys.length - 200).forEach(k => delete feedbackData[k]); } localStorage.setItem('lydian_feedback', JSON.stringify(feedbackData)); } catch (e) { console.warn('[FEEDBACK] Storage error:', e); } } function removeFeedback(actionsDiv) { const messageEl = actionsDiv.closest('.message'); const messageText = messageEl ? messageEl.querySelector('.message-text') : null; const msgContent = messageText ? messageText.innerText.substring(0, 200) : ''; const msgId = messageEl ? messageEl.dataset.msgId || hashString(msgContent) : null; if (!msgId) return; try { const feedbackData = JSON.parse(localStorage.getItem('lydian_feedback') || '{}'); delete feedbackData[msgId]; localStorage.setItem('lydian_feedback', JSON.stringify(feedbackData)); } catch (e) { console.warn('[FEEDBACK] Remove error:', e); } } async function syncFeedbackToServer(msgId, type, content) { if (typeof ChatAuth === 'undefined' || !ChatAuth.state?.isAuthenticated) return; try { await fetch('/api/chat-auth/feedback', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ messageId: msgId, feedback: type, content: content, conversationId: state.currentConversationId || null, }), }); } catch (e) { console.warn('[FEEDBACK] Server sync failed:', e.message); } } // Regenerate message window.regenerateMessage = async function (button) { if (state.isTyping) { alert('Please wait for the current response to complete.'); return; } const messageElements = Array.from(elements.messagesContainer.querySelectorAll('.message')); const currentMessageEl = button.closest('.message'); const currentIndex = messageElements.indexOf(currentMessageEl); let lastUserMessage = null; for (let i = currentIndex - 1; i >= 0; i--) { if (messageElements[i].classList.contains('user')) { const messageText = messageElements[i].querySelector('.message-text'); lastUserMessage = messageText.innerText; break; } } if (!lastUserMessage) { alert('Message to regenerate not found!'); return; } currentMessageEl.remove(); const messageIndex = state.messages.findIndex( (msg, idx) => msg.role === 'assistant' && idx === currentIndex ); if (messageIndex !== -1) { state.messages.splice(messageIndex, 1); } showTypingIndicator(); try { // Clean history: remove timestamp property for API compatibility const cleanHistory = state.messages.slice(-10).map(msg => ({ role: msg.role, content: msg.content, })); const response = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept-Language': 'tr-TR', 'Content-Language': 'tr', }, body: JSON.stringify({ model: 'LYDIAN_QUANTUM', // LyDian primary engine message: lastUserMessage, temperature: 0.9, max_tokens: 8000, // Maximum detail history: cleanHistory, language: 'tr', locale: 'tr-TR', }), }); if (!response.ok) { throw new Error(`API Error: ${response.status}`); } const data = await response.json(); hideTypingIndicator(); if (data.success && data.response) { addMessage('assistant', data.response); // DEBUG: console.log('🔄 Message regenerated successfully'); } else { throw new Error('Invalid response from API'); } } catch (error) { console.error('❌ Regeneration Error:', error); hideTypingIndicator(); addMessage( 'assistant', `**Regeneration Error**\n\nMessage could not be regenerated.\n\n*An error occurred. Please try again.*` ); } }; // User menu dropdown toggle const userMenuBtn = document.getElementById('userMenuBtn'); const userDropdown = document.getElementById('userDropdown'); /* DEBUG: console.log('🔍 User menu elements:', { userMenuBtn: !!userMenuBtn, userDropdown: !!userDropdown, }); */ if (userMenuBtn && userDropdown) { userMenuBtn.addEventListener('click', e => { e.stopPropagation(); // DEBUG: console.log('👤 User menu clicked'); userDropdown.classList.toggle('active'); userMenuBtn.classList.toggle('active'); }); // Close dropdown when clicking outside document.addEventListener('click', e => { if (!userMenuBtn.contains(e.target) && !userDropdown.contains(e.target)) { userDropdown.classList.remove('active'); userMenuBtn.classList.remove('active'); } }); // DEBUG: console.log('✅ User menu listener added'); } else { console.error('❌ User menu elements not found'); } // Sidebar search functionality const sidebarSearch = document.getElementById('sidebarSearch'); if (sidebarSearch) { sidebarSearch.addEventListener('input', e => { const searchTerm = e.target.value.toLowerCase().trim(); const chatItems = elements.chatHistory.querySelectorAll('.chat-item'); chatItems.forEach(item => { const title = item.querySelector('.chat-item-title')?.textContent.toLowerCase() || ''; const preview = item.querySelector('.chat-item-preview')?.textContent.toLowerCase() || ''; if (title.includes(searchTerm) || preview.includes(searchTerm)) { item.style.display = ''; } else { item.style.display = 'none'; } }); }); } // ========== USER NAME DISPLAY ========== function loadUserName() { try { // Priority 1: Genesis Bridge profile (new auth system) const genesisProfile = localStorage.getItem('genesis_user_profile'); if (genesisProfile) { const gp = JSON.parse(genesisProfile); const name = gp.name || gp.displayName || gp.username || ''; if (name) { updateWelcomeText(name); return; } } // Priority 2: Existing auth user data const userDataStr = localStorage.getItem('lydian_chat_user') || localStorage.getItem('lydian_user'); if (userDataStr) { const userData = JSON.parse(userDataStr); const userName = userData.name || userData.displayName || userData.username || 'Misafir'; updateWelcomeText(userName); } else { // Default: time-based greeting updateWelcomeText(getTimeGreeting()); } } catch (e) { console.warn('Could not load user name:', e); updateWelcomeText(getTimeGreeting()); } } function getTimeGreeting() { const hour = new Date().getHours(); if (hour >= 5 && hour < 12) return 'Good Morning'; if (hour >= 12 && hour < 18) return 'Good Afternoon'; if (hour >= 18 && hour < 22) return 'Good Evening'; return 'Good Night'; } function updateWelcomeText(userName) { const welcomeUserName = document.getElementById('welcomeUserName'); if (welcomeUserName) { if (userName && userName !== 'Welcome' && userName !== 'Misafir' && userName !== 'LyDian') { welcomeUserName.textContent = userName; } else { welcomeUserName.textContent = getTimeGreeting(); } } } // Listen for Genesis auth events window.addEventListener('genesis:auth:login', function(e) { if (e.detail && (e.detail.name || e.detail.username)) { updateWelcomeText(e.detail.name || e.detail.username); } }); window.addEventListener('genesis:auth:logout', function() { updateWelcomeText(getTimeGreeting()); }); window.addEventListener('genesis:auth:restored', function(e) { if (e.detail && (e.detail.name || e.detail.username)) { updateWelcomeText(e.detail.name || e.detail.username); } }); // ─── Genesis Feature Manager ─────────────────────────────────────── var _genesisInited = false; var _activeTasks = []; var _genesisNotifInterval = null; var _genesisTaskInterval = null; // ── Toast System ── function _ensureToastContainer() { var c = document.getElementById('genesisToastContainer'); if (!c) { c = document.createElement('div'); c.id = 'genesisToastContainer'; c.className = 'genesis-toast-container'; document.body.appendChild(c); } return c; } function _showGenesisToast(message, type) { var container = _ensureToastContainer(); while (container.children.length >= 4) container.firstChild.remove(); var toast = document.createElement('div'); toast.className = 'genesis-toast toast-' + (type || 'info'); var icons = { success: '\u2705', warning: '\u26A0\uFE0F', error: '\u274C', info: '\u2139\uFE0F' }; toast.innerHTML = '' + (icons[type] || icons.info) + '' + message + ''; toast.addEventListener('click', function() { toast.classList.add('toast-exit'); toast.addEventListener('animationend', function() { toast.remove(); }, { once: true }); }); container.appendChild(toast); setTimeout(function() { if (toast.parentNode) { toast.classList.add('toast-exit'); toast.addEventListener('animationend', function() { toast.remove(); }, { once: true }); } }, 5000); } // ── Task Panel ── function _initTaskPanel() { var panel = document.getElementById('genesisTaskPanel'); if (!panel) return; panel.style.display = ''; // New task form toggle var newBtn = document.getElementById('newTaskBtn'); var form = document.getElementById('newTaskForm'); if (newBtn && form) { newBtn.onclick = function() { form.style.display = form.style.display === 'none' ? 'flex' : 'none'; if (form.style.display === 'flex') document.getElementById('newTaskInput').focus(); }; } // Submit new task var submitBtn = document.getElementById('submitTaskBtn'); var input = document.getElementById('newTaskInput'); if (submitBtn && input) { submitBtn.onclick = function() { _submitNewTask(input.value); }; input.addEventListener('keydown', function(e) { if (e.key === 'Enter') _submitNewTask(input.value); }); } // Load existing tasks _loadTasksFromGenesis(); // Auto-refresh every 15s if (_genesisTaskInterval) clearInterval(_genesisTaskInterval); _genesisTaskInterval = setInterval(_loadTasksFromGenesis, 15000); } async function _submitNewTask(desc) { if (!desc || !desc.trim()) return; var input = document.getElementById('newTaskInput'); var submitBtn = document.getElementById('submitTaskBtn'); if (submitBtn) submitBtn.disabled = true; try { var result = await GenesisBridge.createTask(desc.trim()); if (result) { _showGenesisToast('Gorev olusturuldu', 'success'); if (input) input.value = ''; document.getElementById('newTaskForm').style.display = 'none'; _loadTasksFromGenesis(); } } catch (e) { _showGenesisToast('Gorev olusturulamadi: ' + e.message, 'error'); } if (submitBtn) submitBtn.disabled = false; } async function _loadTasksFromGenesis() { if (typeof GenesisBridge === 'undefined' || !GenesisBridge.isAuthenticated()) return; try { var tasks = await GenesisBridge.listTasks(); if (!Array.isArray(tasks)) tasks = tasks.tasks || []; _activeTasks = tasks; _renderTaskList(tasks); } catch (e) { /* sessiz */ } } function _renderTaskList(tasks) { var list = document.getElementById('taskList'); var count = document.getElementById('taskCount'); if (!list) return; if (count) count.textContent = String(tasks.length); if (!tasks.length) { list.innerHTML = '
📋
Henuz aktif gorev yok
'; return; } list.innerHTML = ''; var statusMap = { pending: 'pending', running: 'running', in_progress: 'running', completed: 'completed', done: 'completed', failed: 'failed', error: 'failed' }; var statusLabels = { pending: 'Bekliyor', running: 'Calisiyor', completed: 'Tamamlandi', failed: 'Basarisiz' }; tasks.slice(0, 20).forEach(function(task) { var status = statusMap[task.status] || 'pending'; var item = document.createElement('div'); item.className = 'genesis-list-item'; item.innerHTML = '' + (statusLabels[status] || status) + '' + '
' + (task.title || task.description || task.task_description || 'Gorev').substring(0, 50) + '
' + '
' + (task.id ? 'ID: ' + String(task.id).substring(0, 8) : '') + '
'; list.appendChild(item); }); } function _addTaskToPanel(taskInfo) { _activeTasks.push(taskInfo); _renderTaskList(_activeTasks); } function _updateTaskStatus(taskId, newStatus) { var task = _activeTasks.find(function(t) { return t.id === taskId; }); if (task) { task.status = newStatus; _renderTaskList(_activeTasks); } } // ── Tool Browser ── function _initToolBrowser() { var panel = document.getElementById('genesisToolBrowser'); if (!panel) return; panel.style.display = ''; var searchInput = document.getElementById('toolSearchInput'); var debounceTimer = null; if (searchInput) { searchInput.addEventListener('input', function() { clearTimeout(debounceTimer); debounceTimer = setTimeout(function() { _searchTools(searchInput.value); }, 300); }); } // Load initial categories _loadToolCategories(); } async function _loadToolCategories() { try { var result = await GenesisBridge.searchTools('', 5); var tools = result.tools || []; var categories = {}; tools.forEach(function(t) { var cat = t.category || 'general'; categories[cat] = (categories[cat] || 0) + 1; }); var container = document.getElementById('toolCategories'); if (!container) return; container.innerHTML = ''; Object.keys(categories).forEach(function(cat) { var pill = document.createElement('button'); pill.className = 'genesis-category-pill'; pill.textContent = cat; pill.onclick = function() { _searchTools(cat); }; container.appendChild(pill); }); } catch (e) { /* sessiz */ } } async function _searchTools(query) { if (!query || !query.trim()) { var list = document.getElementById('toolList'); if (list) list.innerHTML = '
🔧
Arac aramak icin yazin
'; return; } try { var result = await GenesisBridge.searchTools(query.trim(), 15); var tools = result.tools || []; _renderToolList(tools); } catch (e) { _showGenesisToast('Arac arama basarisiz', 'error'); } } function _renderToolList(tools) { var list = document.getElementById('toolList'); if (!list) return; if (!tools.length) { list.innerHTML = '
🔍
Sonuc bulunamadi
'; return; } list.innerHTML = ''; var catIcons = { search: '\uD83D\uDD0D', web: '\uD83C\uDF10', code: '\uD83D\uDCBB', file: '\uD83D\uDCC4', math: '\uD83D\uDCCA', general: '\uD83D\uDD27' }; tools.forEach(function(tool) { var item = document.createElement('div'); item.className = 'genesis-list-item'; var icon = catIcons[tool.category] || catIcons.general; item.innerHTML = '
' + icon + '
' + '
' + (tool.name || tool.id || 'Tool') + '
' + '
' + (tool.description || '').substring(0, 60) + '
'; item.onclick = function() { // Araci secince chat input'a /tool komutu yaz var chatInput = document.getElementById('chatInput') || document.querySelector('textarea'); if (chatInput) { chatInput.value = '/tool ' + (tool.name || tool.id); chatInput.focus(); chatInput.dispatchEvent(new Event('input')); } }; list.appendChild(item); }); } // ── Notification System ── function _initNotifications() { // Start polling for notifications _checkGenesisNotifications(); if (_genesisNotifInterval) clearInterval(_genesisNotifInterval); _genesisNotifInterval = setInterval(_checkGenesisNotifications, 30000); } async function _checkGenesisNotifications() { if (typeof GenesisBridge === 'undefined' || !GenesisBridge.isAuthenticated()) return; try { var resp = await GenesisBridge.api('/notifications?limit=3&unread=true', { method: 'GET' }); if (!resp.ok) return; var data = await resp.json(); var notifications = data.notifications || data || []; if (Array.isArray(notifications) && notifications.length > 0) { notifications.forEach(function(n) { if (n.message || n.title) { _showGenesisToast(n.message || n.title, n.type || 'info'); } }); } } catch (e) { /* sessiz */ } } // ── Voice Input (LyDian Motor LyDian Ses STT) ── var _voiceRecorder = null; var _voiceChunks = []; function _initVoiceInput() { var voiceBtn = document.getElementById('voiceChatBtn'); if (!voiceBtn) return; // Replace existing voice button behavior with Genesis STT voiceBtn.onclick = function(e) { e.preventDefault(); e.stopPropagation(); if (_voiceRecorder && _voiceRecorder.state === 'recording') { _stopVoiceRecording(); } else { _startVoiceRecording(); } }; } async function _startVoiceRecording() { try { var stream = await navigator.mediaDevices.getUserMedia({ audio: true }); _voiceChunks = []; _voiceRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' }); _voiceRecorder.ondataavailable = function(e) { if (e.data.size > 0) _voiceChunks.push(e.data); }; _voiceRecorder.onstop = function() { stream.getTracks().forEach(function(t) { t.stop(); }); _processVoiceRecording(); }; _voiceRecorder.start(); var btn = document.getElementById('voiceChatBtn'); if (btn) { btn.classList.add('recording'); btn.style.color = '#F08A5D'; } _showGenesisToast('Dinleniyor... Durdurmak icin tekrar tiklayin', 'info'); // Auto-stop after 30 seconds setTimeout(function() { if (_voiceRecorder && _voiceRecorder.state === 'recording') { _stopVoiceRecording(); } }, 30000); } catch (e) { _showGenesisToast('Mikrofon erisimi reddedildi', 'error'); } } function _stopVoiceRecording() { if (_voiceRecorder && _voiceRecorder.state === 'recording') { _voiceRecorder.stop(); } var btn = document.getElementById('voiceChatBtn'); if (btn) { btn.classList.remove('recording'); btn.style.color = ''; } } async function _processVoiceRecording() { if (!_voiceChunks.length) return; var blob = new Blob(_voiceChunks, { type: 'audio/webm' }); _voiceChunks = []; _showGenesisToast('Ses isleniyor...', 'info'); try { var formData = new FormData(); formData.append('file', blob, 'recording.webm'); var token = GenesisBridge.getAccessToken(); var url = '/api/genesis-proxy?path=' + encodeURIComponent('/voice/transcribe'); var resp = await fetch(url, { method: 'POST', headers: token ? { 'Authorization': 'Bearer ' + token } : {}, body: formData }); if (resp.ok) { var data = await resp.json(); var text = data.text || data.transcription || ''; if (text) { var chatInput = document.getElementById('chatInput') || document.querySelector('textarea'); if (chatInput) { chatInput.value = text; chatInput.focus(); chatInput.dispatchEvent(new Event('input')); } _showGenesisToast('Ses metne cevirildi', 'success'); } else { _showGenesisToast('Ses anlasilamadi, tekrar deneyin', 'warning'); } } else { _showGenesisToast('STT servisi kullanilamiyor', 'warning'); } } catch (e) { _showGenesisToast('Ses isleme basarisiz: ' + e.message, 'error'); } } // ── Show/Hide Genesis Features ── function _showGenesisFeatures() { if (!_genesisInited) { _genesisInited = true; _initTaskPanel(); _initToolBrowser(); _initNotifications(); _initVoiceInput(); } else { // Re-show panels var taskPanel = document.getElementById('genesisTaskPanel'); var toolPanel = document.getElementById('genesisToolBrowser'); if (taskPanel) taskPanel.style.display = ''; if (toolPanel) toolPanel.style.display = ''; } // Show Autonomous mode in model selector var autonomousBtn = document.querySelector('.genesis-only-option'); if (autonomousBtn) autonomousBtn.style.display = ''; _showGenesisToast('Genesis ozellikleri aktif', 'success'); } function _hideGenesisFeatures() { var taskPanel = document.getElementById('genesisTaskPanel'); var toolPanel = document.getElementById('genesisToolBrowser'); if (taskPanel) taskPanel.style.display = 'none'; if (toolPanel) toolPanel.style.display = 'none'; var autonomousBtn = document.querySelector('.genesis-only-option'); if (autonomousBtn) autonomousBtn.style.display = 'none'; // Clear intervals if (_genesisNotifInterval) { clearInterval(_genesisNotifInterval); _genesisNotifInterval = null; } if (_genesisTaskInterval) { clearInterval(_genesisTaskInterval); _genesisTaskInterval = null; } } window.addEventListener('genesis:auth:login', function() { _showGenesisFeatures(); }); window.addEventListener('genesis:auth:restored', function() { _showGenesisFeatures(); }); window.addEventListener('genesis:auth:logout', function() { _hideGenesisFeatures(); }); // Auto-init on page load if already authenticated if (typeof GenesisBridge !== 'undefined' && GenesisBridge.isAuthenticated()) { setTimeout(_showGenesisFeatures, 200); } // 🔒 GUEST QUERY LIMIT SYSTEM function getGuestQueryCount() { try { const data = JSON.parse(localStorage.getItem('lydian_guest_queries') || '{}'); const today = new Date().toISOString().split('T')[0]; if (data.date !== today) return 0; return data.count || 0; } catch { return 0; } } function incrementGuestQuery() { const today = new Date().toISOString().split('T')[0]; let data = {}; try { data = JSON.parse(localStorage.getItem('lydian_guest_queries') || '{}'); } catch {} if (data.date !== today) { data = { date: today, count: 0 }; } data.count = (data.count || 0) + 1; localStorage.setItem('lydian_guest_queries', JSON.stringify(data)); return data.count; } function showQueryLimitModal() { // Remove existing if any const existing = document.getElementById('queryLimitModal'); if (existing) existing.remove(); const modal = document.createElement('div'); modal.id = 'queryLimitModal'; modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);display:flex;align-items:center;justify-content:center;z-index:100000;padding:1rem;animation:fadeIn 0.3s ease'; modal.innerHTML = `

Daily Limit Reached

As a guest, you have 10 queries. Create a free account for unlimited usage!

`; document.body.appendChild(modal); modal.addEventListener('click', e => { if (e.target === modal) modal.remove(); }); } // ========== INITIALIZATION ========== window.addEventListener('DOMContentLoaded', () => { // Start with sidebar closed on mobile if (window.innerWidth <= 768) { elements.sidebar.classList.add('collapsed'); } // Restore AI mode from sessionStorage if (state.selectedAiType && state.selectedAiType !== 'general') { var savedType = state.selectedAiType; // Update desktop code button var deskCodeRestore = document.getElementById('desktopCodeBtn'); if (deskCodeRestore && savedType === 'code-write') { deskCodeRestore.classList.add('active'); } // Update placeholder if (elements.messageInput && savedType === 'code-write') { elements.messageInput.placeholder = 'Write your code request...'; } // Update mode selector buttons document.querySelectorAll('[data-mode]').forEach(function (b) { b.classList.remove('active'); }); var savedModeBtn = document.querySelector('[data-mode="' + savedType + '"]'); if (savedModeBtn) savedModeBtn.classList.add('active'); } // Desktop centered layout initialization if (window.innerWidth > 768) { document.body.classList.add('desktop-centered', 'chat-initial'); // Enable transitions after layout settles to prevent FOUC requestAnimationFrame(() => { requestAnimationFrame(() => { document.body.classList.add('transitions-ready'); // Remove early FOUC suppressor now that layout is stable document.documentElement.classList.remove('desktop-early'); }); }); // Click on logo - LyDian style: auth check first const desktopLogo = document.getElementById('desktopLogo'); if (desktopLogo) { desktopLogo.addEventListener('click', () => { if (window.ChatAuth && ChatAuth.state && ChatAuth.state.isAuthenticated) { startNewChat(); } else if (window.ChatAuth) { ChatAuth.openPanel('main'); } }); } } // Listen for window resize to add/remove desktop mode window.addEventListener('resize', () => { if (window.innerWidth > 768) { document.body.classList.add('desktop-centered'); // If no messages, show initial state if (!state.messages || state.messages.length === 0) { document.body.classList.add('chat-initial'); document.body.classList.remove('chat-active'); } } else { document.body.classList.remove('desktop-centered', 'chat-initial', 'chat-active'); } }); // Mobile keyboard handler - SIMPLE & SAFE approach if (window.innerWidth <= 768) { const messageInput = document.getElementById('messageInput'); const messagesContainer = document.getElementById('messagesContainer'); if (messageInput) { // On focus: just scroll messages to bottom after keyboard opens messageInput.addEventListener('focus', () => { setTimeout(() => { if (messagesContainer) { messagesContainer.scrollTop = messagesContainer.scrollHeight; } // Ensure input is visible - simple native scroll messageInput.scrollIntoView({ block: 'nearest' }); }, 350); }); } } // Load saved theme const savedTheme = localStorage.getItem('lydian_theme') || 'dark'; setTheme(savedTheme, true); // Silent mode on page load // Initialize particles initParticles(); // Load and display user name from login loadUserName(); // Load chat history (only valid chat objects with id, title, messages) const chatKeys = Object.keys(localStorage).filter(key => key.startsWith('chat_')); chatKeys.forEach(key => { try { const chatData = JSON.parse(localStorage.getItem(key)); // Validate it's a proper chat object (not shared or other data) if (chatData && chatData.id && chatData.messages && Array.isArray(chatData.messages)) { addChatToHistory(chatData); } } catch (e) { console.warn('Invalid chat data for key:', key); } }); // DEBUG: console.log('🚀 LyDian Quantum Chat initialized'); }); function addChatToHistory(chatData) { const chatItem = document.createElement('div'); chatItem.className = 'chat-item'; chatItem.dataset.chatId = chatData.id; // Truncate title to 30 chars for cleaner look const displayTitle = chatData.title.length > 30 ? chatData.title.substring(0, 30) + '...' : chatData.title; chatItem.innerHTML = `
${displayTitle}
`; // Click handler for chat item (not delete button) chatItem.addEventListener('click', e => { if (!e.target.closest('.chat-item-delete')) { loadChat(chatData.id); } }); elements.chatHistory.insertBefore(chatItem, elements.chatHistory.firstChild); } // Save current chat to localStorage and server (if authenticated) async function saveCurrentChat() { if (state.messages.length === 0) return; // Create or update chat ID const isNewChat = !state.currentChatId; if (!state.currentChatId) { state.currentChatId = `chat_${Date.now()}`; } // Get first user message as title (max 50 chars) const firstUserMessage = state.messages.find(msg => msg.role === 'user'); const title = firstUserMessage ? firstUserMessage.content.substring(0, 50) + (firstUserMessage.content.length > 50 ? '...' : '') : 'New Chat'; const chatData = { id: state.currentChatId, title: title, timestamp: Date.now(), messages: state.messages, }; // Save to localStorage localStorage.setItem(state.currentChatId, JSON.stringify(chatData)); // Check if already in chat history UI const existingItem = document.querySelector(`[data-chat-id="${state.currentChatId}"]`); if (!existingItem) { // Add to chat history UI only if it's a new chat addChatToHistory(chatData); } // Refresh desktop history drawer if it exists if (typeof loadDesktopHistory === 'function') { try { loadDesktopHistory(); } catch (e) {} } // Sync to server if user is authenticated if (typeof ChatAuth !== 'undefined' && ChatAuth.state && ChatAuth.state.isAuthenticated) { try { await syncChatToServer(chatData, isNewChat); } catch (err) { console.warn('Server sync failed:', err.message); } } } // Sync chat to server async function syncChatToServer(chatData, isNewChat) { if (typeof ChatAuth === 'undefined' || !ChatAuth.state?.isAuthenticated) return; // Check if we have a server conversation ID mapped let serverConvId = localStorage.getItem(`server_conv_${chatData.id}`); // Create new conversation on server if needed if (!serverConvId) { const createRes = await fetch('/api/chat-auth/history', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ title: chatData.title, model: 'premium', }), }); const createData = await createRes.json(); if (createData.success && createData.conversationId) { serverConvId = createData.conversationId; localStorage.setItem(`server_conv_${chatData.id}`, serverConvId); } else { console.warn('Failed to create server conversation'); return; } } // Sync messages to server const lastSyncIndex = parseInt(localStorage.getItem(`sync_index_${chatData.id}`) || '0'); const newMessages = chatData.messages.slice(lastSyncIndex); if (newMessages.length > 0) { const msgRes = await fetch('/api/chat-auth/messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ conversationId: serverConvId, messages: newMessages.map(m => ({ role: m.role, content: m.content, model: 'premium', })), }), }); if (msgRes.ok) { localStorage.setItem(`sync_index_${chatData.id}`, String(chatData.messages.length)); } } // Update title if changed if (!isNewChat) { await fetch('/api/chat-auth/history', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ id: serverConvId, title: chatData.title, }), }); } } // Delete chat from history window.deleteChatFromHistory = async function (chatId, event) { event.stopPropagation(); if (confirm('Are you sure you want to delete this chat?')) { // Remove from DOM const chatItem = document.querySelector(`[data-chat-id="${chatId}"]`); if (chatItem) { chatItem.style.animation = 'slideOutLeft 0.3s ease'; setTimeout(() => { chatItem.remove(); }, 300); } // Delete from server if it's a server chat if (chatId.startsWith('server_')) { const serverId = chatId.replace('server_', ''); try { await fetch(`/api/chat-auth/history?id=${serverId}`, { method: 'DELETE', credentials: 'include', }); } catch (err) { console.warn('Server delete failed:', err); } } else { // Check if local chat is synced to server const serverConvId = localStorage.getItem(`server_conv_${chatId}`); if ( serverConvId && typeof ChatAuth !== 'undefined' && ChatAuth.state?.isAuthenticated ) { try { await fetch(`/api/chat-auth/history?id=${serverConvId}`, { method: 'DELETE', credentials: 'include', }); } catch (err) { console.warn('Server delete failed:', err); } } // Remove server mapping localStorage.removeItem(`server_conv_${chatId}`); localStorage.removeItem(`sync_index_${chatId}`); } // Remove chat data from localStorage localStorage.removeItem(chatId); // Also remove from chatHistory array (if exists) const savedChats = JSON.parse(localStorage.getItem('chatHistory') || '[]'); const updatedChats = savedChats.filter(chat => chat.id !== chatId); localStorage.setItem('chatHistory', JSON.stringify(updatedChats)); } }; async function loadChat(chatId) { // Check if it's a server chat if (chatId.startsWith('server_')) { const serverId = chatId.replace('server_', ''); await loadServerChat(serverId, chatId); return; } // Load from localStorage const chatData = JSON.parse(localStorage.getItem(chatId)); if (chatData) { state.messages = chatData.messages; state.currentChatId = chatId; renderMessages(); // Hide empty state if (elements.emptyState) { elements.emptyState.style.display = 'none'; } // Desktop: Activate chat layout when loading chat with messages if ( window.innerWidth > 768 && document.body.classList.contains('desktop-centered') && chatData.messages.length > 0 ) { document.body.classList.remove('chat-initial'); document.body.classList.add('chat-active'); } } } // Load chat from server async function loadServerChat(serverId, localId) { try { const res = await fetch(`/api/chat-auth/history?id=${serverId}`, { credentials: 'include', }); const data = await res.json(); if (data.success && data.messages) { // Convert server messages to local format state.messages = data.messages.map(m => ({ role: m.role, content: m.content, timestamp: new Date(m.createdAt).getTime(), })); state.currentChatId = localId; // Cache locally const chatData = { id: localId, serverId: serverId, title: data.conversation?.title || 'Sohbet', timestamp: Date.now(), messages: state.messages, isServerChat: true, }; localStorage.setItem(localId, JSON.stringify(chatData)); localStorage.setItem(`server_conv_${localId}`, serverId); localStorage.setItem(`sync_index_${localId}`, String(state.messages.length)); renderMessages(); // Hide empty state if (elements.emptyState) { elements.emptyState.style.display = 'none'; } // Desktop: Activate chat layout when loading chat with messages if ( window.innerWidth > 768 && document.body.classList.contains('desktop-centered') && state.messages.length > 0 ) { document.body.classList.remove('chat-initial'); document.body.classList.add('chat-active'); } } } catch (err) { console.error('Failed to load server chat:', err); showToast('Sohbet yuklenemedi', 'error'); } } function renderMessages() { elements.messagesContainer.innerHTML = ''; state.messages.forEach(msg => { addMessage(msg.role, msg.content); }); } // ============================================================ // ✅ UNLIMITED GUEST ACCESS - TIMER DISABLED // User requested: Remove 5-minute limit, make chat unlimited // ============================================================ (async function initGuestTimer() { // DEBUG: console.log('✅ Guest chat timer DISABLED - unlimited access enabled'); // Clear any existing timer data from localStorage localStorage.removeItem('guestChatStartTime'); // No timer functionality - unlimited access for all users // This allows both logged-in and guest users to use chat without restrictions return; })();

No saved chats yet

Preview
Code