refactor: ui.js, chat.js, history.js in 13 modulare Dateien aufteilen

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.
This commit is contained in:
Meleki committed 2026-03-07 19:32:33 +01:00
1 parent a82d8cfb05
commit 0dcc05ebf9
15 files changed
+1885 -1873

No files matched your search

+124
View File
@@ -0,0 +1,124 @@
// ── Streaming Bubble ──
let _streamDiv = null;
let _streamText = '';
function startStreamBubble() {
_streamDiv = document.createElement('div');
_streamDiv.className = 'msg assistant markdown streamed';
_streamDiv.innerHTML = '<p><span class="cursor"></span></p>';
_streamText = '';
chatEl().appendChild(_streamDiv);
scrollDown();
}
function appendToStream(text) {
if (!_streamDiv) startStreamBubble();
_streamText += text;
_streamDiv.innerHTML = renderMarkdown(_streamText);
// Cursor ins letzte Inline/Text-Element einfügen, damit er nicht auf eine neue Zeile rutscht
const cursor = document.createElement('span');
cursor.className = 'cursor';
const lastBlock = _streamDiv.lastElementChild;
if (lastBlock) {
lastBlock.appendChild(cursor);
} else {
_streamDiv.appendChild(cursor);
}
scrollDown();
}
function pauseStreamBubble() {
// Cursor entfernen, aber Bubble + Text behalten (z.B. bei Web-Search-Pause)
if (_streamDiv) {
const cursor = _streamDiv.querySelector('.cursor');
if (cursor) cursor.remove();
}
}
function endStreamBubble() {
if (_streamDiv) {
const cursor = _streamDiv.querySelector('.cursor');
if (cursor) cursor.remove();
if (!_streamText.trim()) _streamDiv.remove();
else {
_streamDiv.innerHTML = renderMarkdown(_streamText);
parseFluentEmojis(_streamDiv);
wrapKreaGalleries(_streamDiv);
}
}
_streamDiv = null;
_streamText = '';
}
// ── Review Bubbles (Streaming) ──
let _reviewBubble = null;
let _reviewTextEl = null;
let _reviewText = '';
function addReviewBubble(type, data, agentType) {
_reviewBubble = document.createElement('div');
_reviewBubble.className = 'msg review-bubble streamed';
if (agentType === 'finalizer') _reviewBubble.classList.add('finalizer-bubble');
if (agentType === 'preparer') _reviewBubble.classList.add('preparer-bubble');
const header = document.createElement('div');
header.className = 'review-header';
if (agentType === 'preparer') {
header.innerHTML = '<i class="fa fa-filter"></i> <strong>' + esc(data.agentName || 'Vorbereiter-Agent') + '</strong>';
} else if (agentType === 'finalizer') {
header.innerHTML = '<i class="fa fa-flag-checkered"></i> <strong>' + esc(data.agentName || 'Finalizer-Agent') + '</strong>';
} else {
header.innerHTML = '<i class="fa fa-check-circle-o"></i> <strong>' + esc(data.agentName || 'Review-Agent') + '</strong> <span class="review-round">Runde ' + data.round + '/' + data.maxRounds + '</span>';
}
_reviewBubble.appendChild(header);
_reviewTextEl = document.createElement('div');
_reviewTextEl.className = 'review-content markdown';
_reviewBubble.appendChild(_reviewTextEl);
_reviewText = '';
chatEl().appendChild(_reviewBubble);
scrollDown();
}
function appendToReviewText(text) {
if (!_reviewBubble) return;
_reviewText += text;
_reviewTextEl.innerHTML = renderMarkdown(_reviewText);
scrollDown();
}
function addReviewToolUse(data) {
if (!_reviewBubble) return;
const toolDiv = document.createElement('div');
toolDiv.className = 'review-tool-use';
toolDiv.innerHTML = '<i class="fa fa-wrench"></i> <span class="tool-name">' + esc(data.toolName) + '</span>';
_reviewBubble.appendChild(toolDiv);
scrollDown();
}
function finalizeReviewBubble(type, data, agentType) {
if (!_reviewBubble) return;
const status = document.createElement('div');
status.className = 'review-status';
if (agentType === 'preparer' && type === 'forwarded') {
status.className += ' review-approved';
status.innerHTML = '<i class="fa fa-arrow-right"></i> Weitergeleitet an Session-Agent';
} else if (agentType === 'preparer' && type === 'query') {
status.className += ' review-rejected';
status.innerHTML = '<i class="fa fa-question-circle"></i> Rückfrage an User';
} else if (agentType === 'finalizer' || type === 'done') {
status.className += ' review-approved';
status.innerHTML = '<i class="fa fa-flag-checkered"></i> Abgeschlossen';
} else if (type === 'approved') {
status.className += ' review-approved';
status.innerHTML = '<i class="fa fa-check"></i> Approved';
} else {
status.className += ' review-rejected';
status.innerHTML = '<i class="fa fa-repeat"></i> Feedback – Worker korrigiert...';
}
_reviewBubble.appendChild(status);
_reviewBubble = null;
_reviewTextEl = null;
_reviewText = '';
scrollDown();
}