refactor: Chat-Payload vereinfacht + Inline-Bilder statt Platzhalter
Backend: - ChatEndpoint empfängt nur noch content + sessionId (nicht mehr die gesamte History) - History wird komplett aus DB aufgebaut via BuildHistoryFromDb - Gelöschte Nachrichten (deleted_on) werden aus History gefiltert - Leere Nachrichten werden übersprungen (kein API 400 mehr) - BuildHistoryFromDb optimiert: nur Text für ältere Chains, Tool-Calls nur für aktuelle Chain - GetChainMessages: includeDeleted-Parameter (Default: false) - Archiv-Export nutzt includeDeleted: true Frontend: - messages[]-Array komplett entfernt (kein Client-seitiger History-State mehr) - apiChat() sendet nur content + sessionId - Inline-Bilder: [Bild: name] wird durch <img> ersetzt (live mit DataURL, History per API-Nachladen) - _autoLoadImageAttachments() für History/Archiv/ältere Nachrichten - Klick auf Inline-Bild öffnet Vollbild-Modal
This commit is contained in:
1 parent
51db01752f
commit
8fc29c82d9
12 files changed
+222
-106
No files matched your search
+36
-8
@@ -242,7 +242,7 @@ function _addReplyButton(div) {
|
||||
|
||||
// ── Message Bubbles ──
|
||||
|
||||
function renderUserContent(container, text) {
|
||||
function renderUserContent(container, text, inlineImages) {
|
||||
const raw = (text || '').trim();
|
||||
|
||||
// Reply-Zitat erkennen und als visuellen Block rendern
|
||||
@@ -267,19 +267,27 @@ function renderUserContent(container, text) {
|
||||
|
||||
// Rest als normalen Text weiterverarbeiten
|
||||
if (restText) {
|
||||
_renderUserTextContent(container, restText);
|
||||
_renderUserTextContent(container, restText, inlineImages);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_renderUserTextContent(container, raw);
|
||||
_renderUserTextContent(container, raw, inlineImages);
|
||||
}
|
||||
|
||||
function _renderUserTextContent(container, raw) {
|
||||
function _renderUserTextContent(container, raw, inlineImages) {
|
||||
// [Bild: name] durch Platzhalter ersetzen
|
||||
const imagePattern = /\[Bild: ([^\]]+)\]/g;
|
||||
const imageRefs = [];
|
||||
let mdText = raw.replace(imagePattern, function(m, name) {
|
||||
imageRefs.push(name);
|
||||
return '%%IMGREF' + (imageRefs.length - 1) + '%%';
|
||||
});
|
||||
|
||||
// [Datei: path] oder [Datei: path:1-10] durch Platzhalter ersetzen (vor Markdown-Parsing)
|
||||
const pattern = /\[Datei: ([^\]]+)\]/g;
|
||||
const fileRefs = [];
|
||||
const mdText = raw.replace(pattern, function(m, ref) {
|
||||
mdText = mdText.replace(pattern, function(m, ref) {
|
||||
fileRefs.push(ref);
|
||||
return '%%FILEREF' + (fileRefs.length - 1) + '%%';
|
||||
});
|
||||
@@ -287,7 +295,20 @@ function _renderUserTextContent(container, raw) {
|
||||
// Markdown rendern
|
||||
let html = renderMarkdown(mdText);
|
||||
|
||||
// Platzhalter durch File-Ref-Chips ersetzen (als HTML)
|
||||
// Bild-Platzhalter durch Inline-Bilder ersetzen
|
||||
html = html.replace(/%%IMGREF(\d+)%%/g, function(m, idx) {
|
||||
const name = imageRefs[parseInt(idx)];
|
||||
// Wenn Inline-Bild-Daten vorhanden (beim Senden), direkt anzeigen
|
||||
const img = inlineImages && inlineImages.find(function(i) { return i.name === name; });
|
||||
if (img) {
|
||||
return '<img class="inline-user-image" src="' + esc(img.dataUrl) + '" alt="' + esc(name) + '" title="' + esc(name) + '">';
|
||||
}
|
||||
// Sonst: Platzhalter mit data-Attribut für späteres Laden
|
||||
return '<span class="inline-image-placeholder" data-filename="' + esc(name) + '">'
|
||||
+ '<i class="fa fa-image"></i> ' + esc(name) + '</span>';
|
||||
});
|
||||
|
||||
// Datei-Platzhalter durch File-Ref-Chips ersetzen (als HTML)
|
||||
html = html.replace(/%%FILEREF(\d+)%%/g, function(m, idx) {
|
||||
const ref = fileRefs[parseInt(idx)];
|
||||
const filePath = ref.replace(/:\d+(-\d+)?$/, '');
|
||||
@@ -310,11 +331,18 @@ function _renderUserTextContent(container, raw) {
|
||||
});
|
||||
});
|
||||
|
||||
// Click-Handler für Inline-Bilder (Vollbild)
|
||||
wrap.querySelectorAll('.inline-user-image').forEach(function(img) {
|
||||
img.addEventListener('click', function() {
|
||||
showKreaImageModal(img.alt || 'Bild', img.src);
|
||||
});
|
||||
});
|
||||
|
||||
// Fluent Emoji DOM-Parsing
|
||||
parseFluentEmojis(wrap);
|
||||
}
|
||||
|
||||
function addUserBubble(text, streamed = false, timestamp = '', messageId = null, isMarked = false, deletedOn = null) {
|
||||
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');
|
||||
@@ -322,7 +350,7 @@ function addUserBubble(text, streamed = false, timestamp = '', messageId = null,
|
||||
div.dataset.timestamp = timestamp || new Date().toISOString();
|
||||
if (messageId) div.dataset.messageId = messageId;
|
||||
if (streamed) div.classList.add('streamed');
|
||||
renderUserContent(div, text);
|
||||
renderUserContent(div, text, inlineImages);
|
||||
_addReplyButton(div);
|
||||
_addDeleteButton(div, messageId, deletedOn);
|
||||
_addMarkButton(div, messageId, isMarked);
|
||||
|
||||
Reference in new issue
Block a user