Review-Nachrichten: konsistentes Styling in History, Chat und Shared-Links

- renderMarkdown: [Review: Agent], [Review-Feedback ...], [APPROVED] Tags parsen
  → styled HTML-Header bzw. ausgeblendet (zentral für alle Views)
- history.js: Review-Messages als .review-bubble rendern statt .assistant/.user
  Review-Messages vom Merging ausgenommen
- shared-page.js: Review-Messages erkennen → .review-bubble Klasse
  Merge-Logik schützt Review-Messages vor Zusammenführung
- addReviewBubbleStatic(): Neue Funktion für History-Rendering mit allen
  Buttons (Reply, Delete, Mark, Share)
- chat.css: .review-inline-header, .review-inline-feedback Styles
This commit is contained in:
Meleki committed 2026-03-06 17:31:30 +01:00
1 parent 5fe3f8ac87
commit 6369f7f09c
4 files changed
+94 -14

No files matched your search

+13
View File
@@ -987,6 +987,19 @@
gap: var(--badge-spacing);
}
.review-inline-header {
color: var(--secondary);
margin-right: .3em;
}
.review-inline-feedback {
color: var(--warning);
margin-bottom: .4em;
display: flex;
align-items: center;
gap: var(--badge-spacing);
}
.review-header {
display: flex;
align-items: center;
+34 -8
View File
@@ -157,8 +157,10 @@ function _mergeConsecutiveAssistantTexts(msgs) {
for (const msg of msgs) {
if (msg.type === 'text' && msg.role === 'assistant') {
const prev = result[result.length - 1];
const isReview = msg.content && msg.content.startsWith('[Review:');
const prevIsReview = prev && prev.content && prev.content.startsWith('[Review:');
if (prev && prev.type === 'text' && prev.role === 'assistant'
&& prev.iteration === msg.iteration) {
&& prev.iteration === msg.iteration && !isReview && !prevIsReview) {
// Gleiche Iteration: nahtlos zusammenfügen (Web-Search-Fragmente etc.)
prev.content = (prev.content || '') + ' ' + (msg.content || '');
// IDs/Timestamps: letztes behalten (für Mark-Button etc.)
@@ -178,8 +180,9 @@ function _renderMessagesIntoFragment(fragment, msgs, imageMessageIds) {
for (const msg of msgs) {
const div = document.createElement('div');
switch (msg.type) {
case 'text':
if (msg.role === 'user') {
case 'text': {
const isReviewMsg = msg.content && (msg.content.startsWith('[Review:') || msg.content.startsWith('[Review-Feedback'));
if (msg.role === 'user' && !isReviewMsg) {
div.className = 'msg user markdown' + (msg.isMarked ? ' marked' : '');
if (msg.deletedOn) div.classList.add(isDeletedVisible() ? 'msg-deleted' : 'msg-deleted-hidden');
div.dataset.role = 'user';
@@ -208,6 +211,20 @@ function _renderMessagesIntoFragment(fragment, msgs, imageMessageIds) {
ind.addEventListener('click', () => loadAndShowAttachments(msg.id, ind));
div.appendChild(ind);
}
} else if (isReviewMsg) {
div.className = 'msg review-bubble markdown' + (msg.isMarked ? ' marked' : '');
if (msg.deletedOn) div.classList.add(isDeletedVisible() ? 'msg-deleted' : 'msg-deleted-hidden');
div.dataset.role = msg.role;
div.dataset.timestamp = msg.createdAt || '';
if (msg.id) div.dataset.messageId = msg.id;
div.innerHTML = renderMarkdown(msg.content);
parseFluentEmojis(div);
_addTimestamp(div, msg.createdAt);
_addReplyButton(div);
_addDeleteButton(div, msg.id, msg.deletedOn);
_addMarkButton(div, msg.id, msg.isMarked);
_addShareButton(div, msg.id);
fragment.appendChild(div);
} else if (msg.role === 'assistant') {
div.className = 'msg assistant markdown' + (msg.isMarked ? ' marked' : '');
if (msg.deletedOn) div.classList.add(isDeletedVisible() ? 'msg-deleted' : 'msg-deleted-hidden');
@@ -224,6 +241,7 @@ function _renderMessagesIntoFragment(fragment, msgs, imageMessageIds) {
fragment.appendChild(div);
}
break;
}
case 'tool_use':
if (!isToolVisible(msg.toolName)) continue;
div.className = 'tool-box';
@@ -262,13 +280,21 @@ function renderMessages(msgs, addToConversation) {
switch (msg.type) {
case 'text':
if (msg.role === 'user') {
addUserBubble(msg.content, false, msg.createdAt, msg.id, msg.isMarked, msg.deletedOn);
if (msg.attachmentCount > 0) {
_autoLoadImageAttachments(msg.id);
addAttachmentIndicator(msg.id, msg.attachmentCount, msg.attachmentNames);
if (msg.content && msg.content.startsWith('[Review-Feedback')) {
addReviewBubbleStatic(msg.content, msg.createdAt, msg.id, msg.isMarked, msg.deletedOn);
} else {
addUserBubble(msg.content, false, msg.createdAt, msg.id, msg.isMarked, msg.deletedOn);
if (msg.attachmentCount > 0) {
_autoLoadImageAttachments(msg.id);
addAttachmentIndicator(msg.id, msg.attachmentCount, msg.attachmentNames);
}
}
} else if (msg.role === 'assistant') {
addAssistantBubble(msg.content, msg.createdAt, msg.id, msg.isMarked, msg.deletedOn);
if (msg.content && msg.content.startsWith('[Review:')) {
addReviewBubbleStatic(msg.content, msg.createdAt, msg.id, msg.isMarked, msg.deletedOn);
} else {
addAssistantBubble(msg.content, msg.createdAt, msg.id, msg.isMarked, msg.deletedOn);
}
}
break;
+14 -5
View File
@@ -25,12 +25,18 @@ registerPage('shared', {
messages = _mergeConsecutiveTexts(messages);
for (const msg of messages) {
const c = msg.content || '';
const isReview = c.startsWith('[Review:') || c.startsWith('[Review-Feedback');
const div = document.createElement('div');
div.className = 'msg ' + (msg.role === 'user' ? 'user' : 'assistant') + ' markdown';
if (msg.role === 'user') {
if (isReview) {
div.className = 'msg review-bubble markdown';
} else {
div.className = 'msg ' + (msg.role === 'user' ? 'user' : 'assistant') + ' markdown';
}
if (!isReview && msg.role === 'user') {
renderUserContent(div, msg.content);
} else {
div.innerHTML = renderMarkdown(msg.content);
div.innerHTML = renderMarkdown(c);
parseFluentEmojis(div);
wrapKreaGalleries(div);
}
@@ -56,11 +62,14 @@ registerPage('shared', {
function _mergeConsecutiveTexts(msgs) {
const result = [];
for (const msg of msgs) {
if (msg.role === 'assistant') {
const c = msg.content || '';
const isReview = c.startsWith('[Review:') || c.startsWith('[Review-Feedback');
if (msg.role === 'assistant' && !isReview) {
const prev = result[result.length - 1];
if (prev && prev.role === 'assistant'
&& !(prev.content || '').startsWith('[Review:')
&& prev.iteration === msg.iteration) {
prev.content = (prev.content || '') + ' ' + (msg.content || '');
prev.content = (prev.content || '') + ' ' + c;
if (msg.createdAt) prev.createdAt = msg.createdAt;
continue;
}
+33 -1
View File
@@ -167,7 +167,20 @@ function renderMarkdown(text) {
return '%%CODE' + (codeBlocks.length - 1) + '%%';
});
// 4) [Krea: dateiname.png] → Inline-Thumbnail mit Klick für Vollbild
// 4a) [Review: AgentName] prefix → Review-Header
html = html.replace(/\[Review:\s*([^\]]+)\]\s*/g, function(match, agentName) {
return '<span class="review-inline-header"><i class="fa fa-check-circle-o"></i> <strong>' + esc(agentName.trim()) + '</strong></span> ';
});
// 4b) [Review-Feedback von AgentName (Runde X/Y)]: → Review-Feedback-Header
html = html.replace(/\[Review-Feedback von\s+([^\(]+)\(Runde\s+(\d+)\/(\d+)\)\]:\s*/g, function(match, agentName, round, maxRounds) {
return '<div class="review-inline-feedback"><i class="fa fa-repeat"></i> <strong>Feedback von ' + esc(agentName.trim()) + '</strong> <span class="review-round">Runde ' + esc(round) + '/' + esc(maxRounds) + '</span></div>';
});
// 4c) [APPROVED] ausblenden
html = html.replace(/\[APPROVED\]/g, '');
// 4d) [Krea: dateiname.png] → Inline-Thumbnail mit Klick für Vollbild
html = html.replace(/\[Krea:\s*([^\]]+)\]/g, function(match, filename) {
var fn = filename.trim();
var safeFn = esc(fn);
@@ -640,6 +653,25 @@ function addWarningBubble(text, streamed = false) {
// ── Review Bubbles ──
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;
div.innerHTML = renderMarkdown(text);
parseFluentEmojis(div);
_addTimestamp(div, timestamp);
_addReplyButton(div);
_addDeleteButton(div, messageId, deletedOn);
_addMarkButton(div, messageId, isMarked);
_addShareButton(div, messageId);
chatEl().appendChild(div);
scrollDown();
return div;
}
let _reviewBubble = null;
let _reviewTextEl = null;
let _reviewText = '';