Files
gott/wwwroot/js/stream-ui.js
T
Meleki 0dcc05ebf9 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.
2026-03-07 19:32:33 +01:00

125 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ── 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();
}