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:
1 parent
1a2c407538
commit
e4ccaf9dee
11 files changed
+96
-357
No files matched your search
+29
-84
@@ -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';
|
||||
|
||||
Reference in new issue
Block a user