Fluent Emoji 3D: Unicode-Emojis durch Microsoft Fluent Emoji ersetzen

- emoji.js: Emoji-Replacement-Engine mit umfassendem Unicode 15.0 Regex
  - String-basiert (replaceEmojisWithFluent) in renderMarkdown()
  - DOM-basiert (parseFluentEmojis) als Post-Processing via TreeWalker
  - CDN: @lobehub/fluent-emoji-3d@1.1.0 via jsdelivr (WebP/3D)
  - Intelligenter Fallback: fe0f-Varianten, dann Original-Unicode
  - Fail-Cache verhindert wiederholte Requests für fehlende Emojis
- User-Nachrichten werden jetzt als Markdown gerendert (renderMarkdown)
  - [Datei: ...] Refs: Platzhalter-Mechanismus vor Markdown-Parsing
  - Reply-Quotes bleiben durch Wrapper-Div erhalten
- parseFluentEmojis an allen Render-Stellen integriert:
  addAssistantBubble, endStreamBubble, _renderUserTextContent,
  _renderSystemPromptBlocks, history._renderMessagesIntoFragment,
  history.loadLatestArchive
- CSS: .fluent-emoji Klasse (1.2em, inline, scale 1.25)
- CSS: white-space:pre-wrap nur noch für .msg:not(.markdown)
This commit is contained in:
Meleki committed 2026-03-03 05:53:31 +01:00
1 parent e2c887aee6
commit 5d132585d5
6 files changed
+156 -33

No files matched your search

+35 -30
View File
@@ -43,6 +43,8 @@ function renderMarkdown(text) {
+ '<img class="krea-thumb" src="' + thumbUrl + '" alt="' + safeFn + '" loading="lazy">'
+ '</span>';
});
// Unicode-Emojis durch Fluent Emoji 3D ersetzen (außerhalb von Code-Blöcken)
html = replaceEmojisWithFluent(html);
// Code-Blöcke wiederherstellen
html = html.replace(/%%CODE(\d+)%%/g, function(m, idx) {
return codeBlocks[parseInt(idx)];
@@ -200,47 +202,47 @@ function renderUserContent(container, text) {
}
function _renderUserTextContent(container, raw) {
// [Datei: path] oder [Datei: path:1-10] erkennen
// [Datei: path] oder [Datei: path:1-10] durch Platzhalter ersetzen (vor Markdown-Parsing)
const pattern = /\[Datei: ([^\]]+)\]/g;
let last = 0;
let match;
const frag = document.createDocumentFragment();
const fileRefs = [];
const mdText = raw.replace(pattern, function(m, ref) {
fileRefs.push(ref);
return '%%FILEREF' + (fileRefs.length - 1) + '%%';
});
while ((match = pattern.exec(raw)) !== null) {
// Text vor dem Tag
if (match.index > last) {
frag.appendChild(document.createTextNode(raw.slice(last, match.index)));
}
// Markdown rendern
let html = renderMarkdown(mdText);
const ref = match[1]; // z.B. "src/foo.cs:10-25"
// Pfad ohne Zeilenangabe extrahieren
// 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+)?$/, '');
const chip = document.createElement('span');
chip.className = 'file-ref-chip';
// Schöne Darstellung: Icon + Kurzpfad
const fileName = filePath.includes('/') ? filePath.substring(filePath.lastIndexOf('/') + 1) : filePath;
const lineInfo = ref.replace(filePath, '').trim(); // z.B. ":10-25"
chip.innerHTML = '<i class="fa fa-file-text-o"></i> ' + esc(fileName + lineInfo);
chip.title = ref;
chip.style.cursor = 'pointer';
chip.addEventListener('click', () => {
openFileExplorer({ sessionId: getCurrentSessionId(), initialFile: filePath });
const lineInfo = ref.replace(filePath, '').trim();
return '<span class="file-ref-chip" data-filepath="' + esc(filePath) + '" title="' + esc(ref) + '" style="cursor:pointer">'
+ '<i class="fa fa-file-text-o"></i> ' + esc(fileName + lineInfo) + '</span>';
});
// In ein Wrapper-Div rendern (damit vorherige Elemente wie Reply-Quotes erhalten bleiben)
const wrap = document.createElement('div');
wrap.className = 'user-text-content';
wrap.innerHTML = html;
container.appendChild(wrap);
// Click-Handler für File-Ref-Chips nachträglich binden
wrap.querySelectorAll('.file-ref-chip[data-filepath]').forEach(function(chip) {
chip.addEventListener('click', function() {
openFileExplorer({ sessionId: getCurrentSessionId(), initialFile: chip.dataset.filepath });
});
frag.appendChild(chip);
last = match.index + match[0].length;
}
});
// Resttext
if (last < raw.length) {
frag.appendChild(document.createTextNode(raw.slice(last)));
}
container.appendChild(frag);
// Fluent Emoji DOM-Parsing
parseFluentEmojis(wrap);
}
function addUserBubble(text, streamed = false, timestamp = '', messageId = null, isMarked = false) {
const div = document.createElement('div');
div.className = 'msg user' + (isMarked ? ' marked' : '');
div.className = 'msg user markdown' + (isMarked ? ' marked' : '');
div.dataset.role = 'user';
div.dataset.timestamp = timestamp || new Date().toISOString();
if (messageId) div.dataset.messageId = messageId;
@@ -259,6 +261,7 @@ function addAssistantBubble(text, timestamp = '', messageId = null, isMarked = f
div.dataset.timestamp = timestamp || new Date().toISOString();
if (messageId) div.dataset.messageId = messageId;
div.innerHTML = renderMarkdown(text);
parseFluentEmojis(div);
wrapKreaGalleries(div);
_addReplyButton(div);
_addMarkButton(div, messageId, isMarked);
@@ -324,6 +327,7 @@ function endStreamBubble() {
if (!_streamText.trim()) _streamDiv.remove();
else {
_streamDiv.innerHTML = renderMarkdown(_streamText);
parseFluentEmojis(_streamDiv);
wrapKreaGalleries(_streamDiv);
}
}
@@ -645,6 +649,7 @@ function _renderSystemPromptBlocks(container, blocks) {
var content = document.createElement('div');
content.className = 'sp-block-content markdown';
content.innerHTML = renderMarkdown(block.content);
parseFluentEmojis(content);
wrapper.appendChild(content);
container.appendChild(wrapper);