// ── Streaming Bubble ── let _streamDiv = null; let _streamText = ''; function startStreamBubble() { _streamDiv = document.createElement('div'); _streamDiv.className = 'msg assistant markdown streamed'; _streamDiv.innerHTML = '
'; _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 = ' ' + esc(data.agentName || 'Vorbereiter-Agent') + ''; } else if (agentType === 'finalizer') { header.innerHTML = ' ' + esc(data.agentName || 'Finalizer-Agent') + ''; } else { header.innerHTML = ' ' + esc(data.agentName || 'Review-Agent') + ' Runde ' + data.round + '/' + data.maxRounds + ''; } _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(); } let _currentReviewToolBox = null; function addReviewToolUse(data) { if (!_reviewBubble) return; if (!isToolVisible(data.toolName)) { _currentReviewToolBox = null; return; } var toolDiv = document.createElement('div'); toolDiv.className = 'tool-box review-tool-box streamed'; toolDiv.dataset.toolName = data.toolName; var displayName = TOOL_DISPLAY_NAMES[data.toolName] || data.toolName; toolDiv.innerHTML = '' + esc(displayName) + ' '; _reviewBubble.appendChild(toolDiv); _currentReviewToolBox = toolDiv; scrollDown(); } function finishReviewToolBox(data) { if (_currentReviewToolBox) { renderToolBoxContent(_currentReviewToolBox, data.toolName, data.input, data.result, data.isError, data.sources); _currentReviewToolBox = null; 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 = ' Weitergeleitet an Session-Agent'; } else if (agentType === 'preparer' && type === 'query') { status.className += ' review-rejected'; status.innerHTML = ' Rückfrage an User'; } else if (agentType === 'finalizer' || type === 'done') { status.className += ' review-approved'; status.innerHTML = ' Abgeschlossen'; } else if (type === 'approved') { status.className += ' review-approved'; status.innerHTML = ' Approved'; } else { status.className += ' review-rejected'; status.innerHTML = ' Feedback – Worker korrigiert...'; } _reviewBubble.appendChild(status); _reviewBubble = null; _reviewTextEl = null; _reviewText = ''; _currentReviewToolBox = null; scrollDown(); }