🐘
chat.php
Back
📝 Php ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
<?php // chat.php - Chat Window & Functionality // Render markdown helper function render_markdown_with_code_separators($text) { $text = str_replace(["\r\n", "\r"], "\n", $text); $pat = '/```([a-zA-Z0-9_\-]+)?\s*\n([\s\S]*?)```/m'; $html=''; $pos=0; while (preg_match($pat,$text,$m,PREG_OFFSET_CAPTURE,$pos)) { $start=$m[0][1]; $len=strlen($m[0][0]); $lang=$m[1][0]??''; $code=$m[2][0]??''; $before = substr($text,$pos,$start-$pos); if ($before!=='') $html.='<div class="md-p">'.nl2br(h($before)).'</div>'; $html.='<div class="code-sep"></div>'; $html.='<div class="codeblock">'.($lang?'<div class="code-lang">'.h($lang).'</div>':'') .'<pre><code>'.h($code).'</code></pre></div>'; $pos = $start+$len; } if ($pos < strlen($text)) { $tail = substr($text,$pos); if ($tail!=='') $html.='<div class="md-p">'.nl2br(h($tail)).'</div>'; } if ($html==='') $html = '<div class="md-p">'.nl2br(h($text)).'</div>'; return $html; } ?> <style> /* Chat-specific styles */ .main{flex:1;display:flex;justify-content:center} .chat-wrap{width:100%;max-width:960px;padding:16px;overflow:auto} .chat{display:flex;flex-direction:column;gap:12px;padding-bottom:160px} .msg{display:flex;gap:10px;align-items:flex-start;position:relative} .msg .bubble{max-width:min(780px,92%);padding:12px 14px;border-radius:14px;border:1px solid var(--br);background:var(--bot)} .msg.user{justify-content:flex-end} .msg.user .bubble{background:#0e2240;border-color:#17345a} .badge{font-size:.75rem;color:var(--muted);margin-bottom:4px} .md-p{margin:.4rem 0;line-height:1.45} .code-sep{height:10px;border-top:3px double var(--sep);margin:.8rem 0} .codeblock{border:1px solid var(--sep);border-radius:10px;background:var(--code)} .code-lang{font-size:.8rem;color:#cbd5e1;padding:.4rem .6rem;border-bottom:1px solid var(--sep);background:#0f1521;border-top-left-radius:10px;border-top-right-radius:10px} pre{margin:0;padding:.75rem;overflow:auto;white-space:pre;color:#e6edf3} .composer{position:fixed;left:0;right:0;bottom:0;background:var(--panel);border-top:1px solid var(--br)} .composer-inner{max-width:960px;margin:0 auto;display:grid;grid-template-columns:1fr auto;gap:8px;padding:12px;position:relative} .input{border:1px solid var(--br);background:#0d111a;color:var(--ink);border-radius:12px;padding:12px 14px;font-size:1rem;width:100%} .send{min-width:88px;border:1px solid var(--br);background:var(--accent);color:#fff;border-radius:12px;font-weight:600;cursor:pointer} .send:hover{filter:brightness(1.05)} .tokens{grid-column:1 / -1; display:flex; justify-content:space-between; align-items:center; font-size:.85rem; color:var(--muted); padding:4px 2px} /* Message selection controls */ .msg-checkbox { position: absolute; top: 8px; right: 8px; width: 18px; height: 18px; accent-color: var(--accent); z-index: 10; opacity: 0; transition: opacity 0.2s ease; } .msg:hover .msg-checkbox { opacity: 1; } .msg.selected .msg-checkbox { opacity: 1; } .msg.selected .bubble { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(47, 111, 235, 0.2); } /* Bulk actions bar */ .bulk-actions { position: fixed; bottom: 80px; left: 50%; transform: translateX(-50%); background: var(--panel); border: 1px solid var(--br); border-radius: 12px; padding: 12px 16px; display: none; gap: 12px; align-items: center; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); z-index: 100; } .bulk-actions.show { display: flex; } .bulk-actions-text { color: var(--ink); font-size: 0.9rem; font-weight: 500; } .bulk-btn { background: var(--accent); color: white; border: none; border-radius: 8px; padding: 8px 12px; font-size: 0.9rem; cursor: pointer; transition: all 0.2s ease; } .bulk-btn:hover { filter: brightness(1.1); } .bulk-btn.danger { background: var(--danger); } .bulk-btn.cancel { background: var(--muted); } /* Artifact message styles */ .msg.artifact { justify-content: center; } .msg.artifact .bubble { background: linear-gradient(135deg, rgba(47, 111, 235, 0.1) 0%, rgba(99, 102, 241, 0.05) 100%); border-color: var(--accent); max-width: 90%; cursor: pointer; transition: all 0.3s ease; } .msg.artifact .bubble:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(47, 111, 235, 0.2); } .artifact-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; } .artifact-title { font-weight: 600; color: var(--accent); font-size: 1rem; } .artifact-toggle { background: none; border: none; color: var(--accent); cursor: pointer; font-size: 0.8rem; padding: 4px 8px; border-radius: 4px; transition: background 0.2s ease; } .artifact-toggle:hover { background: rgba(47, 111, 235, 0.2); } .artifact-preview { color: var(--muted); font-size: 0.9rem; font-style: italic; line-height: 1.4; border-top: 1px solid rgba(47, 111, 235, 0.2); padding-top: 8px; margin-top: 8px; } .artifact-full { display: none; margin-top: 12px; padding: 12px; background: rgba(255, 255, 255, 0.02); border-radius: 8px; border: 1px solid rgba(47, 111, 235, 0.2); max-height: 300px; overflow-y: auto; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 0.85rem; line-height: 1.4; white-space: pre-wrap; } /* Flashcard animations */ #flashcard-display { display: none; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 20px; min-height: 140px; background: linear-gradient(135deg, rgba(79, 70, 229, 0.1) 0%, rgba(99, 102, 241, 0.05) 100%); border-radius: 16px; padding: 24px; position: relative; overflow: hidden; } #flashcard-display::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.03), transparent); animation: shimmer 2s infinite; } @keyframes shimmer { 0% { left: -100%; } 100% { left: 100%; } } .cool-spinner { width: 60px; height: 60px; position: relative; display: none; } .spinner-ring { position: absolute; width: 100%; height: 100%; border-radius: 50%; border: 3px solid transparent; } .spinner-ring:nth-child(1) { border-top: 3px solid var(--glow-color); animation: spin-outer 1.5s linear infinite; filter: drop-shadow(0 0 8px var(--glow-color)); } .spinner-ring:nth-child(2) { border-bottom: 3px solid #667eea; animation: spin-inner 1s linear infinite reverse; filter: drop-shadow(0 0 6px #667eea); } .spinner-ring:nth-child(3) { border-left: 3px solid #764ba2; animation: spin-middle 2s linear infinite; filter: drop-shadow(0 0 4px #764ba2); } @keyframes spin-outer { 0% { transform: rotate(0deg) scale(1); } 50% { transform: rotate(180deg) scale(1.1); } 100% { transform: rotate(360deg) scale(1); } } @keyframes spin-inner { 0% { transform: rotate(0deg) scale(0.8); } 50% { transform: rotate(-180deg) scale(0.9); } 100% { transform: rotate(-360deg) scale(0.8); } } @keyframes spin-middle { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .spinner-pulse { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 12px; height: 12px; background: var(--glow-color); border-radius: 50%; animation: pulse-glow 1.5s ease-in-out infinite; } @keyframes pulse-glow { 0%, 100% { transform: translate(-50%, -50%) scale(1); box-shadow: 0 0 0 0 rgba(79, 70, 229, 0.7); } 50% { transform: translate(-50%, -50%) scale(1.2); box-shadow: 0 0 0 20px rgba(79, 70, 229, 0); } } #flashcard-question, #flashcard-answer { font-size: 1.1rem; font-weight: 600; color: var(--accent); opacity: 0; transform: translateY(20px); transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); } #flashcard-question.show, #flashcard-answer.show { opacity: 1; transform: translateY(0); } #flashcard-answer { font-size: 1rem; color: var(--ink); font-weight: normal; font-style: italic; background: rgba(255, 255, 255, 0.05); padding: 12px 20px; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.1); } .loading-status { font-size: 0.9rem; color: var(--muted); font-weight: 500; letter-spacing: 0.5px; opacity: 0.8; margin-top: 8px; } .dots { display: inline-block; animation: loading-dots 1.5s infinite; } @keyframes loading-dots { 0%, 20% { content: ''; } 40% { content: '.'; } 60% { content: '..'; } 80%, 100% { content: '...'; } } </style> <div class="main"> <div class="chat-wrap"> <div class="chat" id="chat"> <?php if (empty($chatLog)): ?> <div class="msg" style="justify-content:center;opacity:.8"> <div class="bubble">Ask anything—history, science, how-to, travel tips, you name it.</div> </div> <?php else: foreach ($chatLog as $i => $m): $isUser = $m['role']==='user'; $isArtifact = $m['role']==='artifact'; ?> <?php if ($isArtifact): ?> <div class="msg artifact" data-index="<?= $i ?>"> <input type="checkbox" class="msg-checkbox" onchange="toggleMessageSelection(<?= $i ?>)"> <div class="bubble" onclick="toggleArtifact(<?= $i ?>)"> <div class="badge">Artifact Submitted</div> <div class="artifact-header"> <div class="artifact-title"><?= h($m['title']) ?></div> <button class="artifact-toggle" id="toggle-<?= $i ?>">Expand</button> </div> <div class="artifact-preview"><?= h($m['compactContent']) ?></div> <div class="artifact-full" id="full-<?= $i ?>"><?= h($m['content']) ?></div> </div> </div> <?php else: ?> <div class="msg <?= $isUser?'user':'' ?>" data-index="<?= $i ?>"> <input type="checkbox" class="msg-checkbox" onchange="toggleMessageSelection(<?= $i ?>)"> <div class="bubble"> <div class="badge"><?= $isUser?'You':'Assistant' ?></div> <div class="content"> <?php if ($isUser): ?> <div class="md-p"><?= nl2br(h($m['content'])) ?></div> <?php else: ?> <?= render_markdown_with_code_separators($m['content']) ?> <?php endif; ?> </div> </div> </div> <?php endif; ?> <?php endforeach; endif; ?> </div> </div> </div> <!-- Bulk Actions Bar --> <div class="bulk-actions" id="bulkActions"> <span class="bulk-actions-text" id="selectedCount">0 messages selected</span> <button class="bulk-btn danger" onclick="deleteSelectedMessages()">Delete Selected</button> <button class="bulk-btn" onclick="keepSelectedMessages()">Keep Selected</button> <button class="bulk-btn cancel" onclick="clearSelection()">Cancel</button> </div> <div class="composer"> <div class="composer-inner"> <div id="input-container" style="display:grid;grid-template-columns:1fr auto;gap:8px"> <input type="text" id="questionInput" class="input" placeholder="Type your question..." autofocus> <button class="send" id="sendButton">Send</button> </div> <div id="flashcard-display"> <div class="spinner-container"> <div class="cool-spinner" id="flashcard-spinner"> <div class="spinner-ring"></div> <div class="spinner-ring"></div> <div class="spinner-ring"></div> <div class="spinner-pulse"></div> </div> </div> <div class="loading-status"> Thinking<span class="dots"></span> </div> <div id="flashcard-question"></div> <div id="flashcard-answer"></div> </div> <div class="tokens"> <div id="last-usage"> <?php if ($lastUsage): ?> <strong>Last</strong> — prompt: <?= (int)$lastUsage['prompt'] ?>, completion: <?= (int)$lastUsage['completion'] ?>, total: <?= (int)$lastUsage['total'] ?> <?php else: ?> <strong>Last</strong> — no usage yet <?php endif; ?> </div> <div id="session-totals"> <strong>Session</strong> — prompt: <?= (int)$usageTotals['prompt'] ?>, completion: <?= (int)$usageTotals['completion'] ?>, total: <?= (int)$usageTotals['total'] ?> </div> </div> </div> </div> <script> // Chat functionality document.getElementById('sendButton').addEventListener('click', sendMessage); document.getElementById('questionInput').addEventListener('keydown', function(e){ if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); const questionInput = document.getElementById('questionInput'); const sendButton = document.getElementById('sendButton'); const chatDiv = document.getElementById('chat'); const inputContainer = document.getElementById('input-container'); const flashcardDisplay = document.getElementById('flashcard-display'); const flashcardQuestion = document.getElementById('flashcard-question'); const flashcardAnswer = document.getElementById('flashcard-answer'); const flashcardSpinner = document.getElementById('flashcard-spinner'); // Message selection functionality let selectedMessages = new Set(); function toggleMessageSelection(index) { const messageEl = document.querySelector(`[data-index="${index}"]`); const checkbox = messageEl.querySelector('.msg-checkbox'); if (checkbox.checked) { selectedMessages.add(index); messageEl.classList.add('selected'); } else { selectedMessages.delete(index); messageEl.classList.remove('selected'); } updateBulkActions(); } function updateBulkActions() { const bulkActions = document.getElementById('bulkActions'); const selectedCount = document.getElementById('selectedCount'); if (selectedMessages.size > 0) { bulkActions.classList.add('show'); selectedCount.textContent = `${selectedMessages.size} message${selectedMessages.size === 1 ? '' : 's'} selected`; } else { bulkActions.classList.remove('show'); } } function clearSelection() { selectedMessages.clear(); document.querySelectorAll('.msg-checkbox').forEach(cb => cb.checked = false); document.querySelectorAll('.msg.selected').forEach(msg => msg.classList.remove('selected')); updateBulkActions(); } function deleteSelectedMessages() { if (selectedMessages.size === 0) return; if (confirm(`Delete ${selectedMessages.size} selected message${selectedMessages.size === 1 ? '' : 's'}? This cannot be undone.`)) { // Send delete request to backend fetch('manage_messages.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'delete', indices: Array.from(selectedMessages) }) }).then(response => response.json()) .then(data => { if (data.success) { // Remove messages from UI selectedMessages.forEach(index => { const messageEl = document.querySelector(`[data-index="${index}"]`); if (messageEl) messageEl.remove(); }); // Re-index all remaining messages to match backend const remainingMessages = document.querySelectorAll('.msg'); remainingMessages.forEach((msg, newIndex) => { msg.setAttribute('data-index', newIndex); const checkbox = msg.querySelector('.msg-checkbox'); if (checkbox) { checkbox.setAttribute('onchange', `toggleMessageSelection(${newIndex})`); } // Update any artifact toggle handlers const bubble = msg.querySelector('.bubble[onclick]'); if (bubble && bubble.onclick) { const onclickStr = bubble.getAttribute('onclick'); if (onclickStr && onclickStr.includes('toggleArtifact')) { bubble.setAttribute('onclick', `toggleArtifact(${newIndex})`); } } }); clearSelection(); } else { alert('Error deleting messages: ' + (data.error || 'Unknown error')); } }).catch(error => { alert('Error deleting messages: ' + error.message); }); } } function keepSelectedMessages() { if (selectedMessages.size === 0) return; const totalMessages = document.querySelectorAll('.msg').length; const deleteCount = totalMessages - selectedMessages.size; if (confirm(`Keep only ${selectedMessages.size} selected message${selectedMessages.size === 1 ? '' : 's'} and delete ${deleteCount} other${deleteCount === 1 ? '' : 's'}? This cannot be undone.`)) { // Get all message indices const allMessages = document.querySelectorAll('.msg'); const allIndices = Array.from(allMessages).map((_, index) => index); // Find indices to delete (everything NOT selected) const indicesToDelete = allIndices.filter(index => !selectedMessages.has(index)); // Send delete request for unselected messages fetch('manage_messages.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'delete', indices: indicesToDelete }) }).then(response => response.json()) .then(data => { if (data.success) { // Remove unselected messages from UI indicesToDelete.forEach(index => { const messageEl = document.querySelector(`[data-index="${index}"]`); if (messageEl) messageEl.remove(); }); // Re-index all remaining messages to match backend const remainingMessages = document.querySelectorAll('.msg'); remainingMessages.forEach((msg, newIndex) => { msg.setAttribute('data-index', newIndex); const checkbox = msg.querySelector('.msg-checkbox'); if (checkbox) { checkbox.setAttribute('onchange', `toggleMessageSelection(${newIndex})`); } // Update any artifact toggle handlers const bubble = msg.querySelector('.bubble[onclick]'); if (bubble && bubble.onclick) { const onclickStr = bubble.getAttribute('onclick'); if (onclickStr && onclickStr.includes('toggleArtifact')) { bubble.setAttribute('onclick', `toggleArtifact(${newIndex})`); } } }); clearSelection(); } else { alert('Error keeping messages: ' + (data.error || 'Unknown error')); } }).catch(error => { alert('Error keeping messages: ' + error.message); }); } } // Artifact toggle functionality function toggleArtifact(index) { const fullDiv = document.getElementById('full-' + index); const toggleBtn = document.getElementById('toggle-' + index); if (fullDiv && toggleBtn) { if (fullDiv.style.display === 'none' || fullDiv.style.display === '') { fullDiv.style.display = 'block'; toggleBtn.textContent = 'Collapse'; } else { fullDiv.style.display = 'none'; toggleBtn.textContent = 'Expand'; } } } // Make toggleArtifact available globally window.toggleArtifact = toggleArtifact; // Markdown rendering function renderMarkdown(text) { let html = text; html = html.replace(/```([a-zA-Z0-9_\-]+)?\s*\n([\s\S]*?)```/g, (match, lang, code) => { return ` <div class="code-sep"></div> <div class="codeblock"> ${lang ? `<div class="code-lang">${lang}</div>` : ''} <pre><code>${code.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</code></pre> </div> `; }); html = html.replace(/\n/g, '<br>'); return `<div class="md-p">${html}</div>`; } // Flashcard system let flashcardTimer = null; let flashcardsData = []; async function loadFlashcards() { try { const response = await fetch('flashcards.json'); if (!response.ok) throw new Error(`Failed to fetch flashcards. Status: ${response.status}`); flashcardsData = await response.json(); } catch (e) { console.error("Error loading flashcards:", e); flashcardsData = []; } } function startFlashcardAnimation() { inputContainer.style.display = 'none'; flashcardDisplay.style.display = 'flex'; flashcardSpinner.style.display = 'block'; flashcardQuestion.classList.remove('show'); flashcardAnswer.classList.remove('show'); flashcardQuestion.textContent = ''; flashcardAnswer.textContent = ''; if (flashcardsData.length > 0) { function showNextFlashcard() { const randomIndex = Math.floor(Math.random() * flashcardsData.length); const flashcard = flashcardsData[randomIndex]; flashcardQuestion.classList.remove('show'); flashcardAnswer.classList.remove('show'); flashcardSpinner.style.display = 'block'; setTimeout(() => { flashcardQuestion.textContent = flashcard.question; flashcardQuestion.classList.add('show'); }, 1200); setTimeout(() => { flashcardQuestion.classList.remove('show'); flashcardAnswer.textContent = flashcard.answer; flashcardAnswer.classList.add('show'); }, 3500); setTimeout(() => { flashcardAnswer.classList.remove('show'); }, 4800); } showNextFlashcard(); flashcardTimer = setInterval(showNextFlashcard, 5000); } } function stopFlashcardAnimation() { clearInterval(flashcardTimer); flashcardDisplay.style.display = 'none'; inputContainer.style.display = 'grid'; flashcardSpinner.style.display = 'none'; flashcardQuestion.classList.remove('show'); flashcardAnswer.classList.remove('show'); flashcardQuestion.textContent = ''; flashcardAnswer.textContent = ''; } async function sendMessage() { const question = questionInput.value.trim(); if (question === '') return; return sendMessageWithContent(question); } async function sendMessageWithContent(question) { // Show user's message immediately with proper index const currentIndex = document.querySelectorAll('.msg').length; const userMessageHTML = `<div class="msg user" data-index="${currentIndex}"><input type="checkbox" class="msg-checkbox" onchange="toggleMessageSelection(${currentIndex})"><div class="bubble"><div class="badge">You</div><div class="content"><div class="md-p">${question.replace(/\n/g, '<br>')}</div></div></div></div>`; chatDiv.innerHTML += userMessageHTML; autoScroll(); // Start the loading animation startFlashcardAnimation(); // Disable UI questionInput.disabled = true; sendButton.disabled = true; questionInput.value = ''; try { // Get system prompt based on current setting let systemPrompt = ''; if (currentInitialPrompt !== 'none') { const promptOptions = { 'helpful': 'You are a helpful, knowledgeable, and friendly AI assistant. Provide clear, accurate, and detailed responses. Always be respectful and aim to be as useful as possible.', 'creative': 'You are a creative and imaginative AI assistant. Help with brainstorming, creative writing, artistic ideas, and innovative solutions. Be inspiring and think outside the box while remaining practical.', 'technical': 'You are a technical expert and programming assistant. Provide precise, well-structured code examples, explain technical concepts clearly, and offer best practices. Focus on accuracy and efficiency.' }; systemPrompt = promptOptions[currentInitialPrompt] || ''; } const response = await fetch('api.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ question, model: document.getElementById('modelSelect')?.value || 'deepseek-chat', maxTokens: parseInt(document.getElementById('maxTokensSelect')?.value || '800'), temperature: parseFloat(document.getElementById('temperatureInput')?.value || '0.7'), system: systemPrompt, includeArtifacts: true // Tell API to include artifacts in context }) }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); if (data.error) { throw new Error(data.error); } if (data.warning) { console.warn('API Warning:', data.warning); } // Append assistant's message with proper index const nextIndex = document.querySelectorAll('.msg').length; const assistantMessageHTML = ` <div class="msg" data-index="${nextIndex}"> <input type="checkbox" class="msg-checkbox" onchange="toggleMessageSelection(${nextIndex})"> <div class="bubble"> <div class="badge">Assistant</div> <div class="content"> ${renderMarkdown(data.answer)} </div> </div> </div>`; chatDiv.innerHTML += assistantMessageHTML; // Update token counters if (data.usage) { document.getElementById('last-usage').innerHTML = `<strong>Last</strong> — prompt: ${data.usage.prompt_tokens}, completion: ${data.usage.completion_tokens}, total: ${data.usage.total_tokens}`; document.getElementById('session-totals').innerHTML = `<strong>Session</strong> — prompt: ${data.usage.total_prompt}, completion: ${data.usage.total_completion}, total: ${data.usage.total_tokens_cumulative}`; } } catch (error) { console.error('Fetch error:', error); const errorMessageHTML = `<div class="msg"><div class="bubble" style="background:#3b1f28;border-color:#5a2836;color:#ffccd5;font-style:normal"><strong>Error:</strong> ${error.message}</div></div>`; chatDiv.innerHTML += errorMessageHTML; } finally { stopFlashcardAnimation(); questionInput.disabled = false; sendButton.disabled = false; questionInput.focus(); autoScroll(); } } // Save current settings to session async function saveCurrentSettings() { const model = document.getElementById('modelSelect')?.value || 'deepseek-chat'; const maxTokens = parseInt(document.getElementById('maxTokensSelect')?.value || '800'); const temperature = parseFloat(document.getElementById('temperatureInput')?.value || '0.7'); try { await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `action=update_settings&model=${encodeURIComponent(model)}&max_tokens=${maxTokens}&temperature=${temperature}` }); } catch (error) { console.log('Settings save failed, continuing with request...'); } } // Auto-scroll when page loads and check for pending artifacts window.addEventListener('load', function() { loadFlashcards(); autoScroll(); // Check for pending artifacts via AJAX const urlParams = new URLSearchParams(window.location.search); if (urlParams.get('artifact_submitted') === '1') { // Check if there's a pending artifact to load fetch('check_pending_artifact.php') .then(response => response.json()) .then(data => { if (data.pending) { // Add the artifact to the chat dynamically const nextIndex = document.querySelectorAll('.msg').length; const artifactHTML = ` <div class="msg artifact" data-index="${nextIndex}"> <input type="checkbox" class="msg-checkbox" onchange="toggleMessageSelection(${nextIndex})"> <div class="bubble" onclick="toggleArtifact('pending')"> <div class="badge">Artifact Submitted</div> <div class="artifact-header"> <div class="artifact-title">${data.artifact.title}</div> <button class="artifact-toggle" id="toggle-pending">Expand</button> </div> <div class="artifact-preview">${data.artifact.compactContent}</div> <div class="artifact-full" id="full-pending" style="display: none;">${data.artifact.content}</div> </div> </div> `; chatDiv.innerHTML += artifactHTML; autoScroll(); // Auto-send the acknowledgment request without showing it first if (data.autoSend && data.userMessage) { setTimeout(() => { // Send the message directly without displaying it first sendMessageWithContent(data.userMessage); }, 500); } } }) .catch(error => console.error('Error checking pending artifact:', error)); // Clean up the URL window.history.replaceState({}, document.title, window.location.pathname); } }); </script> </body> </html>