// ── 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 = ` 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 = ` ${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 => '' + esc(b) + '').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); } }