Die monolithische ui.js (1534 Zeilen) zusammen mit chat.js (612) und history.js (626) in fokussierte Module aufgeteilt: Neue Dateien aus ui.js: - ui-helpers.js (126) - DOM-Utilities, $, esc, truncate, scrollDown, Toast - markdown.js (143) - renderMarkdown, wrapKreaGalleries, LaTeX/Code-Schutz - cards.js (90) - buildSessionCard, buildAgentCard - msg-actions.js (208) - Delete, Mark, Share, Reply-Button, Timestamp - tool-ui.js (332) - Tool-Boxes, Diffs, Todo-Liste, Visibility - modals.js (190) - System-Prompt, Command-Output, Krea-Image Modals - stream-ui.js (124) - Stream-Bubble, Review-Streaming - bubbles.js (312) - User/Assistant/Error/Warning/Choice/Review Bubbles Neue Dateien aus chat.js: - reply.js (54) - Reply/Quote-Handling mit pendingReply State - files.js (110) - File-Upload, Workspace-Refs mit pendingFiles/Refs State Neue Datei aus history.js: - archive.js (173) - Archiv-Loading mit loadedArchiveCount State Verbleibend: - chat.js (448) - Stream-Events, sendMessage, handleChoiceClick, Plan-Mode - history.js (464) - History laden, Polling, Message-Rendering Kein Code verändert, nur verschoben. Alle 94 Funktionen verifiziert. Globale Variablen in die Module verschoben, wo sie primär genutzt werden.
127 lines
3.7 KiB
JavaScript
127 lines
3.7 KiB
JavaScript
// ── 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 = '<i class="fa ' + icon + '"></i> ' + (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(); });
|
|
}
|
|
|
|
// ── 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 = '<i class="fa fa-arrow-up"></i> ' + 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 = '<i class="fa fa-archive"></i> 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());
|
|
}
|