refactor(js): dritte Refactoring-Iteration – weitere Duplikate eliminiert

Zusammengeführt/dedupliziert:
- renderMessages() nutzt jetzt _renderMessagesIntoFragment() statt eigenen switch/case
- addChoiceBubble() + addChoiceBubbleStatic() → _createChoiceBubble() Helper
- addErrorBubble() + addWarningBubble() → _addSystemBubble() Helper
- Chain-Meta-Erstellung (3x) → _createChainMetaDiv() Helper
- Agent-Picker-Setup (4x) → setupAgentPicker() Helper in session-page
- more-Click-Handler (2x) → initMoreButton() Helper in ui-helpers
- showLoginLinkModal() nutzt jetzt _createModal()
- showMessage() in server-settings → showToast()

Verwaiste Funktionen entfernt:
- addAssistantBubble(), addReviewBubbleStatic(), addChoiceBubbleStatic()
- addAttachmentIndicator(), addToolBoxStatic(), appendToolResult()
- serverSettingsMessage HTML-Element

Netto: -261 Zeilen
This commit is contained in:
Meleki committed 2026-03-07 21:58:36 +01:00
1 parent 1a2c407538
commit e4ccaf9dee
11 files changed
+96 -357

No files matched your search

+29 -84
View File
@@ -157,130 +157,75 @@ function addUserBubble(text, streamed = false, timestamp = '', messageId = null,
scrollDown();
}
function addAssistantBubble(text, timestamp = '', messageId = null, isMarked = false, deletedOn = null) {
const div = _createMessageBubble({ role: 'assistant', timestamp, messageId, isMarked, deletedOn });
div.innerHTML = renderMarkdown(text);
parseFluentEmojis(div);
wrapKreaGalleries(div);
_addBubbleActions(div, { timestamp, messageId, deletedOn, isMarked });
function _addSystemBubble(text, icon, color, streamed) {
const div = document.createElement('div');
div.className = 'msg system-info';
if (streamed) div.classList.add('streamed');
div.style.color = color;
div.innerHTML = '<i class="fa ' + icon + '"></i> ' + esc((text || '').trim());
chatEl().appendChild(div);
scrollDown();
return div;
}
function addErrorBubble(text, streamed = false) {
const div = document.createElement('div');
div.className = 'msg system-info';
if (streamed) div.classList.add('streamed');
div.style.color = 'var(--dangerous)';
div.innerHTML = '<i class="fa fa-times-circle"></i> ' + esc((text || '').trim());
chatEl().appendChild(div);
scrollDown();
return _addSystemBubble(text, 'fa-times-circle', 'var(--dangerous)', streamed);
}
function addWarningBubble(text, streamed = false) {
const div = document.createElement('div');
div.className = 'msg system-info';
if (streamed) div.classList.add('streamed');
div.style.color = 'var(--warning)';
div.innerHTML = '<i class="fa fa-exclamation-triangle"></i> ' + esc((text || '').trim());
chatEl().appendChild(div);
scrollDown();
return _addSystemBubble(text, 'fa-exclamation-triangle', 'var(--warning)', streamed);
}
// ── Choice Bubbles (ask_user_choice) ──
// Wird live aus dem SSE-Stream aufgerufen
function addChoiceBubble(toolCallId, question, choices, streamed = true) {
// Guard: choices muss ein Array sein
if (!Array.isArray(choices)) choices = [String(choices)];
/**
* Creates a choice bubble element (for both live stream and history rendering).
* @param {object} opts - { toolCallId, question, choices, selectedChoice, timestamp, messageId, streamed }
*/
function _createChoiceBubble(opts) {
if (!Array.isArray(opts.choices)) opts.choices = [String(opts.choices)];
const answered = !!opts.selectedChoice;
const div = document.createElement('div');
div.className = 'msg choice-bubble';
if (streamed) div.classList.add('streamed');
div.dataset.toolCallId = toolCallId;
div.className = 'msg choice-bubble' + (answered ? ' choice-answered' : '');
if (opts.streamed) div.classList.add('streamed');
div.dataset.toolCallId = opts.toolCallId || '';
if (opts.messageId) div.dataset.messageId = opts.messageId;
const questionDiv = document.createElement('div');
questionDiv.className = 'choice-question';
questionDiv.innerHTML = renderMarkdown(question);
questionDiv.innerHTML = renderMarkdown(opts.question);
parseFluentEmojis(questionDiv);
div.appendChild(questionDiv);
const buttonsDiv = document.createElement('div');
buttonsDiv.className = 'choice-buttons';
for (const choice of choices) {
for (const choice of opts.choices) {
const btn = document.createElement('button');
btn.className = 'choice-btn';
btn.textContent = choice;
btn.addEventListener('click', () => handleChoiceClick(toolCallId, choice, div));
buttonsDiv.appendChild(btn);
}
div.appendChild(buttonsDiv);
chatEl().appendChild(div);
scrollDown();
return div;
}
// Wird für History-Rendering aufgerufen (bereits beantwortete Choices)
function addChoiceBubbleStatic(toolCallId, question, choices, selectedChoice, timestamp, messageId) {
// Guard: choices muss ein Array sein
if (!Array.isArray(choices)) choices = [String(choices)];
const div = document.createElement('div');
div.className = 'msg choice-bubble choice-answered';
div.dataset.toolCallId = toolCallId;
if (messageId) div.dataset.messageId = messageId;
const questionDiv = document.createElement('div');
questionDiv.className = 'choice-question';
questionDiv.innerHTML = renderMarkdown(question);
parseFluentEmojis(questionDiv);
div.appendChild(questionDiv);
const buttonsDiv = document.createElement('div');
buttonsDiv.className = 'choice-buttons';
for (const choice of choices) {
const btn = document.createElement('button');
btn.className = 'choice-btn';
btn.disabled = true;
btn.textContent = choice;
if (choice === selectedChoice) {
btn.classList.add('choice-selected');
if (answered) {
btn.disabled = true;
btn.classList.add(choice === opts.selectedChoice ? 'choice-selected' : 'choice-unselected');
} else {
btn.classList.add('choice-unselected');
btn.addEventListener('click', () => handleChoiceClick(opts.toolCallId, choice, div));
}
buttonsDiv.appendChild(btn);
}
div.appendChild(buttonsDiv);
if (timestamp) _addTimestamp(div, timestamp);
chatEl().appendChild(div);
scrollDown();
if (opts.timestamp) _addTimestamp(div, opts.timestamp);
return div;
}
// ── Review Bubbles (Static / History) ──
function addReviewBubbleStatic(text, timestamp, messageId, isMarked, deletedOn) {
const div = _createMessageBubble({ role: 'assistant', extraClass: 'review-bubble', timestamp, messageId, isMarked, deletedOn });
div.innerHTML = renderMarkdown(text);
parseFluentEmojis(div);
_addBubbleActions(div, { timestamp, messageId, deletedOn, isMarked });
// Wird live aus dem SSE-Stream aufgerufen
function addChoiceBubble(toolCallId, question, choices, streamed = true) {
const div = _createChoiceBubble({ toolCallId, question, choices, streamed });
chatEl().appendChild(div);
scrollDown();
return div;
}
// ── Attachment Indicator ──
function addAttachmentIndicator(messageId, count, namesJson) {
const chat = chatEl();
const lastMsg = chat.lastElementChild;
if (!lastMsg) return;
lastMsg.appendChild(createAttachmentIndicator(messageId, count, namesJson));
}
async function loadAndShowAttachments(messageId, indicator) {
if (indicator.dataset.loaded) return;
indicator.dataset.loaded = '1';