refactor: ui.js, chat.js, history.js in 13 modulare Dateien aufteilen
Die monolithische ui.js (1534 Zeilen) zusammen mit chat.js (612) und history.js (626) in fokussierte Module aufgeteilt: Neue Dateien aus ui.js: - ui-helpers.js (126) - DOM-Utilities, $, esc, truncate, scrollDown, Toast - markdown.js (143) - renderMarkdown, wrapKreaGalleries, LaTeX/Code-Schutz - cards.js (90) - buildSessionCard, buildAgentCard - msg-actions.js (208) - Delete, Mark, Share, Reply-Button, Timestamp - tool-ui.js (332) - Tool-Boxes, Diffs, Todo-Liste, Visibility - modals.js (190) - System-Prompt, Command-Output, Krea-Image Modals - stream-ui.js (124) - Stream-Bubble, Review-Streaming - bubbles.js (312) - User/Assistant/Error/Warning/Choice/Review Bubbles Neue Dateien aus chat.js: - reply.js (54) - Reply/Quote-Handling mit pendingReply State - files.js (110) - File-Upload, Workspace-Refs mit pendingFiles/Refs State Neue Datei aus history.js: - archive.js (173) - Archiv-Loading mit loadedArchiveCount State Verbleibend: - chat.js (448) - Stream-Events, sendMessage, handleChoiceClick, Plan-Mode - history.js (464) - History laden, Polling, Message-Rendering Kein Code verändert, nur verschoben. Alle 94 Funktionen verifiziert. Globale Variablen in die Module verschoben, wo sie primär genutzt werden.
This commit is contained in:
1 parent
a82d8cfb05
commit
0dcc05ebf9
15 files changed
+1885
-1873
No files matched your search
@@ -0,0 +1,173 @@
|
||||
// ── Archive Loading ──
|
||||
|
||||
let loadedArchiveCount = 0;
|
||||
|
||||
function getOrCreateArchiveContainer() {
|
||||
let container = chatEl().querySelector('.archive-container');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.className = 'archive-container';
|
||||
chatEl().prepend(container);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
function getOrCreateArchiveDivider() {
|
||||
let divider = chatEl().querySelector('.archive-divider');
|
||||
if (!divider) {
|
||||
divider = document.createElement('div');
|
||||
divider.className = 'msg system-info archive-divider';
|
||||
divider.textContent = '── Aktuelle Konversation ──';
|
||||
// Nach dem Container, vor den aktiven Messages
|
||||
const container = getOrCreateArchiveContainer();
|
||||
container.after(divider);
|
||||
}
|
||||
return divider;
|
||||
}
|
||||
|
||||
async function loadLatestArchive() {
|
||||
try {
|
||||
const archives = await apiListArchives(getCurrentSessionId());
|
||||
if (archives.length <= loadedArchiveCount) return;
|
||||
|
||||
const latest = archives[loadedArchiveCount];
|
||||
loadedArchiveCount++;
|
||||
|
||||
removeArchiveLoadButton();
|
||||
|
||||
const data = await apiGetArchive(latest.archiveId);
|
||||
const container = getOrCreateArchiveContainer();
|
||||
getOrCreateArchiveDivider();
|
||||
|
||||
// Archiv-Block zusammenbauen
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
const sep = document.createElement('div');
|
||||
sep.className = 'msg system-info';
|
||||
sep.innerHTML = `<i class="fa fa-archive"></i> Archiv vom ${new Date(latest.createdAt).toLocaleString('de-AT')} (${latest.chainCount} Chains, ${latest.messageCount} Messages)`;
|
||||
fragment.appendChild(sep);
|
||||
|
||||
const archiveImageIds = [];
|
||||
for (const entry of data) {
|
||||
const mergedMsgs = _mergeConsecutiveAssistantTexts(entry.messages);
|
||||
for (const msg of mergedMsgs) {
|
||||
const div = document.createElement('div');
|
||||
switch (msg.type) {
|
||||
case 'text': {
|
||||
const isReviewMsg = msg.content && (msg.content.startsWith('[Review:') || msg.content.startsWith('[Review-Feedback') || msg.content.startsWith('[Finalizer:') || msg.content.startsWith('[Preparer:'));
|
||||
if (msg.role === 'user' && !isReviewMsg) {
|
||||
div.className = 'msg user markdown archived' + (msg.isMarked ? ' marked' : '');
|
||||
if (msg.deletedOn) div.classList.add(isDeletedVisible() ? 'msg-deleted' : 'msg-deleted-hidden');
|
||||
div.dataset.role = 'user';
|
||||
div.dataset.timestamp = msg.createdAt || '';
|
||||
if (msg.id) div.dataset.messageId = msg.id;
|
||||
renderUserContent(div, msg.content);
|
||||
_addTimestamp(div, msg.createdAt);
|
||||
_addReplyButton(div);
|
||||
_addDeleteButton(div, msg.id, msg.deletedOn);
|
||||
_addMarkButton(div, msg.id, msg.isMarked);
|
||||
_addShareButton(div, msg.id);
|
||||
} else if (isReviewMsg) {
|
||||
div.className = 'msg review-bubble markdown archived' + (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);
|
||||
} else if (msg.role === 'assistant') {
|
||||
div.className = 'msg assistant markdown archived' + (msg.isMarked ? ' marked' : '');
|
||||
if (msg.deletedOn) div.classList.add(isDeletedVisible() ? 'msg-deleted' : 'msg-deleted-hidden');
|
||||
div.dataset.role = 'assistant';
|
||||
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);
|
||||
}
|
||||
if (msg.role === 'user' && msg.attachmentCount > 0) {
|
||||
if (/\[Bild: [^\]]+\]/.test(msg.content)) archiveImageIds.push(msg.id);
|
||||
let names = [];
|
||||
try { if (msg.attachmentNames) names = JSON.parse(msg.attachmentNames); } 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> ${msg.attachmentCount} Datei${msg.attachmentCount > 1 ? 'en' : ''}` + (nameList ? `: ${esc(nameList)}` : '');
|
||||
ind.style.cursor = 'pointer';
|
||||
ind.title = 'Klicken zum Anzeigen';
|
||||
ind.addEventListener('click', () => loadAndShowAttachments(msg.id, ind));
|
||||
div.appendChild(ind);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'tool_use':
|
||||
if (!isToolVisible(msg.toolName)) continue;
|
||||
div.className = 'tool-box archived';
|
||||
renderToolBoxContent(div, msg.toolName, msg.content, null, false);
|
||||
break;
|
||||
case 'web_search_result': {
|
||||
if (!isToolVisible('web_search')) continue;
|
||||
var archSrc = [];
|
||||
try { archSrc = JSON.parse(msg.content || '[]'); } catch(e) {}
|
||||
if (archSrc.length === 0) continue; // Leere Suchergebnisse überspringen
|
||||
div.className = 'tool-box archived';
|
||||
div.dataset.toolName = 'web_search';
|
||||
renderToolBoxContent(div, 'web_search', null, null, false, archSrc);
|
||||
break;
|
||||
}
|
||||
case 'tool_result': {
|
||||
// An vorherigen tool-box anhängen
|
||||
const prevBoxes = fragment.querySelectorAll ? fragment.querySelectorAll('.tool-box') : [];
|
||||
const lastToolBox = prevBoxes[prevBoxes.length - 1];
|
||||
if (lastToolBox && !lastToolBox.querySelector('.tool-result')) {
|
||||
const rd = document.createElement('div');
|
||||
rd.className = 'tool-result' + (msg.isError ? ' tool-error' : '');
|
||||
rd.textContent = truncate(msg.content || '', 300);
|
||||
lastToolBox.appendChild(rd);
|
||||
}
|
||||
continue; // kein separates div
|
||||
}
|
||||
case 'error':
|
||||
div.className = 'msg system-info archived';
|
||||
div.style.color = 'var(--dangerous)';
|
||||
div.textContent = (msg.content || '').trim();
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
fragment.appendChild(div);
|
||||
}
|
||||
// Chain-Meta-Badges nach jeder Chain einfügen
|
||||
if (entry.chain && isChainMetaVisible(entry.chain)) {
|
||||
const badges = buildMetaBadges(entry.chain);
|
||||
const metaDiv = document.createElement('div');
|
||||
metaDiv.className = 'chain-meta archived';
|
||||
metaDiv.innerHTML = badges.map(b => '<span class="chain-meta-badge">' + esc(b) + '</span>').join('');
|
||||
fragment.appendChild(metaDiv);
|
||||
}
|
||||
}
|
||||
|
||||
// Älteres Archiv vor dem bisherigen Inhalt einfügen
|
||||
container.prepend(fragment);
|
||||
|
||||
// Bild-Attachments automatisch laden
|
||||
for (const mid of archiveImageIds) _autoLoadImageAttachments(mid);
|
||||
|
||||
// Button ganz oben, falls es noch ältere Archive gibt
|
||||
if (archives.length > loadedArchiveCount) {
|
||||
showArchiveLoadButton();
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error('Archiv laden fehlgeschlagen:', e);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user