177 lines
5.1 KiB
JavaScript
177 lines
5.1 KiB
JavaScript
// ── History Loading ──
|
|
|
|
let lastKnownMessageId = 0;
|
|
let historyPollInterval = null;
|
|
let loadedArchiveCount = 0;
|
|
|
|
async function loadHistory() {
|
|
try {
|
|
const sid = getCurrentSessionId();
|
|
const archives = await apiListArchives(sid);
|
|
if (archives.length > 0) {
|
|
showArchiveLoadButton();
|
|
}
|
|
|
|
const data = await apiHistory(sid);
|
|
data.reverse();
|
|
|
|
for (const entry of data) {
|
|
renderMessages(entry.messages, true);
|
|
}
|
|
|
|
scrollDown();
|
|
startHistoryPolling();
|
|
} catch (e) {
|
|
console.error('History laden fehlgeschlagen:', e);
|
|
}
|
|
}
|
|
|
|
function renderMessages(msgs, addToConversation) {
|
|
for (const msg of msgs) {
|
|
if (msg.id > lastKnownMessageId) lastKnownMessageId = msg.id;
|
|
|
|
switch (msg.type) {
|
|
case 'text':
|
|
if (msg.role === 'user') {
|
|
addUserBubble(msg.content);
|
|
if (msg.attachmentCount > 0) addAttachmentIndicator(msg.id, msg.attachmentCount);
|
|
if (addToConversation) messages.push({ role: 'user', content: msg.content });
|
|
} else if (msg.role === 'assistant') {
|
|
addAssistantBubble(msg.content);
|
|
if (addToConversation) messages.push({ role: 'assistant', content: msg.content });
|
|
}
|
|
break;
|
|
|
|
case 'tool_use':
|
|
addToolBoxStatic(msg.toolName, msg.content, null, false);
|
|
break;
|
|
|
|
case 'tool_result':
|
|
addToolBoxStatic(null, null, msg.content, msg.isError);
|
|
break;
|
|
|
|
case 'error':
|
|
addErrorBubble(msg.content);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── Archive Loading ──
|
|
|
|
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.textContent = `📦 Archiv vom ${new Date(latest.createdAt).toLocaleString('de-AT')} (${latest.chainCount} Chains, ${latest.messageCount} Messages)`;
|
|
fragment.appendChild(sep);
|
|
|
|
for (const entry of data) {
|
|
for (const msg of entry.messages) {
|
|
const div = document.createElement('div');
|
|
switch (msg.type) {
|
|
case 'text':
|
|
div.className = msg.role === 'user' ? 'msg user archived' : 'msg assistant archived';
|
|
div.textContent = msg.content;
|
|
break;
|
|
case 'tool_use':
|
|
div.className = 'tool-box archived';
|
|
div.innerHTML = '<span class="tool-name">' + esc(msg.toolName) + '</span>'
|
|
+ '<span class="tool-input">' + esc(truncate(msg.content, 120)) + '</span>';
|
|
break;
|
|
case 'tool_result':
|
|
div.className = 'tool-box archived';
|
|
div.innerHTML = '<div class="tool-result ' + (msg.isError ? 'tool-error' : '') + '">'
|
|
+ esc(truncate(msg.content, 300)) + '</div>';
|
|
break;
|
|
case 'error':
|
|
div.className = 'msg system-info archived';
|
|
div.style.color = '#ff6b6b';
|
|
div.textContent = msg.content;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
fragment.appendChild(div);
|
|
}
|
|
}
|
|
|
|
// Älteres Archiv vor dem bisherigen Inhalt einfügen
|
|
container.prepend(fragment);
|
|
|
|
// Button ganz oben, falls es noch ältere Archive gibt
|
|
if (archives.length > loadedArchiveCount) {
|
|
showArchiveLoadButton();
|
|
}
|
|
|
|
} catch (e) {
|
|
console.error('Archiv laden fehlgeschlagen:', e);
|
|
}
|
|
}
|
|
|
|
// ── Polling ──
|
|
|
|
function startHistoryPolling(intervalMs = 3000) {
|
|
if (historyPollInterval) return;
|
|
historyPollInterval = setInterval(pollHistory, intervalMs);
|
|
}
|
|
|
|
function stopHistoryPolling() {
|
|
if (historyPollInterval) {
|
|
clearInterval(historyPollInterval);
|
|
historyPollInterval = null;
|
|
}
|
|
}
|
|
|
|
async function pollHistory() {
|
|
if (isStreaming) return;
|
|
|
|
try {
|
|
const newMessages = await apiHistoryPoll(getCurrentSessionId(), lastKnownMessageId);
|
|
if (newMessages.length > 0) {
|
|
renderMessages(newMessages, true);
|
|
scrollDown();
|
|
}
|
|
} catch (e) {
|
|
console.error('History poll fehlgeschlagen:', e);
|
|
}
|
|
}
|