Refactor: JavaScript-Code-Duplikate eliminiert (-227 Zeilen)
Neue Helper-Funktionen extrahiert: - _runStream(): SSE-Stream-Loop aus sendMessage() + handleChoiceClick() (chat.js) - _createMessageBubble() + _addBubbleActions(): Bubble-Erstellung aus 6+ Stellen (bubbles.js) - createAttachmentIndicator(): Attachment-Anzeige aus bubbles/history/archive (bubbles.js) - _renderMessagesIntoFragment(): Shared zwischen loadOlderMessages + loadLatestArchive (history.js) - _isReviewMessage(): Review/Finalizer/Preparer-Check aus 8+ Stellen (history.js) - _renderChoiceBubbleInFragment(): Choice-Bubble-Rendering extrahiert (history.js) - _buildDiffLines(): Diff-Zeilen aus buildEditDiff + buildWritePreview (tool-ui.js) - _buildAvatarTransformStyle(): Avatar-Transform aus buildSessionCard + buildAgentCard (cards.js)
This commit is contained in:
1 parent
b4c844b7c2
commit
b56b83ad1e
7 files changed
+272
-499
No files matched your search
+54
-47
@@ -100,41 +100,69 @@ function _renderUserTextContent(container, raw, inlineImages) {
|
||||
parseFluentEmojis(wrap);
|
||||
}
|
||||
|
||||
// ── Message Bubble Helpers ──
|
||||
|
||||
/**
|
||||
* Creates a message bubble div with common setup (className, dataset, deletedOn, action buttons).
|
||||
* @param {object} opts - { role, extraClass, timestamp, messageId, isMarked, deletedOn, streamed, archived }
|
||||
*/
|
||||
function _createMessageBubble(opts) {
|
||||
const div = document.createElement('div');
|
||||
const classes = ['msg', opts.extraClass || opts.role, 'markdown'];
|
||||
if (opts.isMarked) classes.push('marked');
|
||||
if (opts.archived) classes.push('archived');
|
||||
if (opts.streamed) classes.push('streamed');
|
||||
div.className = classes.join(' ');
|
||||
if (opts.deletedOn) div.classList.add(isDeletedVisible() ? 'msg-deleted' : 'msg-deleted-hidden');
|
||||
div.dataset.role = opts.role || '';
|
||||
div.dataset.timestamp = opts.timestamp || new Date().toISOString();
|
||||
if (opts.messageId) div.dataset.messageId = opts.messageId;
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds standard action buttons (timestamp, reply, delete, mark, share) to a bubble.
|
||||
*/
|
||||
function _addBubbleActions(div, opts) {
|
||||
_addTimestamp(div, opts.timestamp);
|
||||
_addReplyButton(div);
|
||||
_addDeleteButton(div, opts.messageId, opts.deletedOn);
|
||||
_addMarkButton(div, opts.messageId, opts.isMarked);
|
||||
_addShareButton(div, opts.messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an attachment indicator element for a message.
|
||||
*/
|
||||
function createAttachmentIndicator(messageId, count, namesJson) {
|
||||
let names = [];
|
||||
try { if (namesJson) names = JSON.parse(namesJson); } catch {}
|
||||
const nameList = names.filter(n => n).join(', ');
|
||||
const ind = document.createElement('div');
|
||||
ind.className = 'attachment-indicator';
|
||||
ind.innerHTML = `<i class="fa fa-paperclip"></i> ${count} Datei${count > 1 ? 'en' : ''}` + (nameList ? `: ${esc(nameList)}` : '');
|
||||
ind.style.cursor = 'pointer';
|
||||
ind.title = 'Klicken zum Anzeigen';
|
||||
ind.addEventListener('click', () => loadAndShowAttachments(messageId, ind));
|
||||
return ind;
|
||||
}
|
||||
|
||||
// ── Message Bubbles ──
|
||||
|
||||
function addUserBubble(text, streamed = false, timestamp = '', messageId = null, isMarked = false, deletedOn = null, inlineImages = null) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'msg user markdown' + (isMarked ? ' marked' : '');
|
||||
if (deletedOn) div.classList.add(isDeletedVisible() ? 'msg-deleted' : 'msg-deleted-hidden');
|
||||
div.dataset.role = 'user';
|
||||
div.dataset.timestamp = timestamp || new Date().toISOString();
|
||||
if (messageId) div.dataset.messageId = messageId;
|
||||
if (streamed) div.classList.add('streamed');
|
||||
const div = _createMessageBubble({ role: 'user', timestamp, messageId, isMarked, deletedOn, streamed });
|
||||
renderUserContent(div, text, inlineImages);
|
||||
_addTimestamp(div, timestamp);
|
||||
_addReplyButton(div);
|
||||
_addDeleteButton(div, messageId, deletedOn);
|
||||
_addMarkButton(div, messageId, isMarked);
|
||||
_addShareButton(div, messageId);
|
||||
_addBubbleActions(div, { timestamp, messageId, deletedOn, isMarked });
|
||||
chatEl().appendChild(div);
|
||||
scrollDown();
|
||||
}
|
||||
|
||||
function addAssistantBubble(text, timestamp = '', messageId = null, isMarked = false, deletedOn = null) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'msg assistant markdown' + (isMarked ? ' marked' : '');
|
||||
if (deletedOn) div.classList.add(isDeletedVisible() ? 'msg-deleted' : 'msg-deleted-hidden');
|
||||
div.dataset.role = 'assistant';
|
||||
div.dataset.timestamp = timestamp || new Date().toISOString();
|
||||
if (messageId) div.dataset.messageId = messageId;
|
||||
const div = _createMessageBubble({ role: 'assistant', timestamp, messageId, isMarked, deletedOn });
|
||||
div.innerHTML = renderMarkdown(text);
|
||||
parseFluentEmojis(div);
|
||||
wrapKreaGalleries(div);
|
||||
_addTimestamp(div, timestamp);
|
||||
_addReplyButton(div);
|
||||
_addDeleteButton(div, messageId, deletedOn);
|
||||
_addMarkButton(div, messageId, isMarked);
|
||||
_addShareButton(div, messageId);
|
||||
_addBubbleActions(div, { timestamp, messageId, deletedOn, isMarked });
|
||||
chatEl().appendChild(div);
|
||||
scrollDown();
|
||||
return div;
|
||||
@@ -235,19 +263,10 @@ function addChoiceBubbleStatic(toolCallId, question, choices, selectedChoice, ti
|
||||
// ── Review Bubbles (Static / History) ──
|
||||
|
||||
function addReviewBubbleStatic(text, timestamp, messageId, isMarked, deletedOn) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'msg review-bubble markdown' + (isMarked ? ' marked' : '');
|
||||
if (deletedOn) div.classList.add(isDeletedVisible() ? 'msg-deleted' : 'msg-deleted-hidden');
|
||||
div.dataset.role = 'assistant';
|
||||
div.dataset.timestamp = timestamp || '';
|
||||
if (messageId) div.dataset.messageId = messageId;
|
||||
const div = _createMessageBubble({ role: 'assistant', extraClass: 'review-bubble', timestamp, messageId, isMarked, deletedOn });
|
||||
div.innerHTML = renderMarkdown(text);
|
||||
parseFluentEmojis(div);
|
||||
_addTimestamp(div, timestamp);
|
||||
_addReplyButton(div);
|
||||
_addDeleteButton(div, messageId, deletedOn);
|
||||
_addMarkButton(div, messageId, isMarked);
|
||||
_addShareButton(div, messageId);
|
||||
_addBubbleActions(div, { timestamp, messageId, deletedOn, isMarked });
|
||||
chatEl().appendChild(div);
|
||||
scrollDown();
|
||||
return div;
|
||||
@@ -259,19 +278,7 @@ function addAttachmentIndicator(messageId, count, namesJson) {
|
||||
const chat = chatEl();
|
||||
const lastMsg = chat.lastElementChild;
|
||||
if (!lastMsg) return;
|
||||
|
||||
let names = [];
|
||||
try { if (namesJson) names = JSON.parse(namesJson); } catch {}
|
||||
|
||||
const indicator = document.createElement('div');
|
||||
indicator.className = 'attachment-indicator';
|
||||
|
||||
const nameList = names.filter(n => n).join(', ');
|
||||
indicator.innerHTML = `<i class="fa fa-paperclip"></i> ${count} Datei${count > 1 ? 'en' : ''}` + (nameList ? `: ${esc(nameList)}` : '');
|
||||
indicator.style.cursor = 'pointer';
|
||||
indicator.title = 'Klicken zum Anzeigen';
|
||||
indicator.addEventListener('click', () => loadAndShowAttachments(messageId, indicator));
|
||||
lastMsg.appendChild(indicator);
|
||||
lastMsg.appendChild(createAttachmentIndicator(messageId, count, namesJson));
|
||||
}
|
||||
|
||||
async function loadAndShowAttachments(messageId, indicator) {
|
||||
|
||||
Reference in new issue
Block a user