// ── DOM Helpers ── const $ = (sel) => document.querySelector(sel); const chatEl = () => $('#chat'); const metaEl = () => $('#meta'); function esc(s) { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; } function truncate(s, max) { return s && s.length > max ? s.substring(0, max) + '...' : (s || ''); } function scrollDown() { const chat = chatEl(); if (!chat) return; chat.scrollTop = chat.scrollHeight; // Nach Font-Swap kann sich die Höhe ändern → nochmal scrollen document.fonts.ready.then(() => { chat.scrollTop = chat.scrollHeight; }); } function prettyModelName(model) { if (!model) return '?'; model = model.split('claude-').join(''); if (model == 'sonnet') return 'Sonnet'; if (model == 'sonnet-4-5') return 'Sonnet 4.5'; if (model == 'sonnet-4-6') return 'Sonnet 4.6'; if (model == 'opus') return 'Opus'; if (model == 'opus-4-5') return 'Opus 4.5'; if (model == 'opus-4-6') return 'Opus 4.6'; if (model == 'haiku') return 'Haiku'; if (model == 'haiku-4-5') return 'Haiku 4.5'; return 'Unbekannt (' + model + ')'; } // ── Toast Notifications ── function showToast(message, type) { type = type || 'info'; var container = document.getElementById('toastContainer'); if (!container) { container = document.createElement('div'); container.id = 'toastContainer'; document.body.appendChild(container); } var toast = document.createElement('div'); toast.className = 'toast toast-' + type; var icon = type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-times-circle' : 'fa-info-circle'; toast.innerHTML = ' ' + (message || ''); container.appendChild(toast); // Einblenden requestAnimationFrame(function() { toast.classList.add('toast-visible'); }); // Automatisch ausblenden var timeout = setTimeout(function() { removeToast(toast); }, 4000); toast.addEventListener('click', function() { clearTimeout(timeout); removeToast(toast); }); } function removeToast(toast) { toast.classList.remove('toast-visible'); toast.addEventListener('transitionend', function() { toast.remove(); }); } // ── EasyMDE Helper ── function createMDE(element, options) { options = options || {}; const mde = new EasyMDE({ element: element, initialValue: options.initialValue || '', toolbar: options.toolbar || ['bold', 'italic', 'heading', '|', 'unordered-list', 'ordered-list', '|', 'link', 'code', '|', 'preview'], status: false, spellChecker: false, autoDownloadFontAwesome: false, autofocus: options.autofocus || false, minHeight: options.minHeight || '120px', placeholder: options.placeholder || '', previewClass: ['editor-preview', 'markdown'] }); mde.codemirror.setOption('theme', 'default'); mde.codemirror.getWrapperElement().closest('.EasyMDEContainer').classList.add('fe-mde-wrap'); return mde; } // ── Meta Bar ── function setMeta(text) { metaEl().textContent = text; } function setMetaHtml(html) { metaEl().innerHTML = html; } // ── Older Messages Button ── function showOlderMessagesButton(remainingCount) { removeOlderMessagesButton(); const btn = document.createElement('button'); btn.className = 'archive-load-btn older-messages-btn'; btn.innerHTML = ' ' + remainingCount + ' ältere Nachrichten laden'; btn.addEventListener('click', () => loadOlderMessages()); chatEl().prepend(btn); } function removeOlderMessagesButton() { const btn = chatEl().querySelector('.older-messages-btn'); if (btn) btn.remove(); } // ── Archive Button ── function showArchiveLoadButton() { const existing = chatEl().querySelector('.archive-load-btn:not(.older-messages-btn)'); if (existing) existing.remove(); const btn = document.createElement('button'); btn.className = 'archive-load-btn'; btn.innerHTML = ' Archivierte Nachrichten laden'; btn.addEventListener('click', loadLatestArchive); // Immer ganz oben im Chat (aber nach dem older-messages-btn falls vorhanden) const olderBtn = chatEl().querySelector('.older-messages-btn'); if (olderBtn) { olderBtn.after(btn); } else { chatEl().prepend(btn); } } function removeArchiveLoadButton() { const btns = chatEl().querySelectorAll('.archive-load-btn:not(.older-messages-btn)'); btns.forEach(b => b.remove()); } // ── Streamed Elements ── function removeStreamedElements() { chatEl().querySelectorAll('.streamed').forEach(el => el.remove()); }