Files
gott/wwwroot/js/pages/chat-page.js
T
Meleki 8c9f54da79 Alle Unicode-Icons durch Font Awesome 4.7 Icons ersetzt
- chat.js: Attachment-Labels, Streaming-Spinner, File/Ref-Previews, Warning/Error-Bubbles
- chat-page.js: Meta-Bar Status (Erfolg/Fehler), Backup-Meldungen
- dashboard.js: Nachrichten/Ordner/Persona-Icons
- session-page.js: Remove-Buttons
- history.js: Archiv-Badge, Attachment-Indicator
- ui.js: Archiv-Button, Warning/Error-Bubble Icons
- file-explorer.js: Verzeichnis/Datei-Icons, Back/Close-Buttons, getFileIcon()
- index.html: Header-Menü, Send-Button, Settings-Button
2026-02-28 19:10:24 +01:00

252 lines
7.8 KiB
JavaScript

// ── Chat Page ──
let _streamingPollInterval = null;
let _recoveryChainId = null;
function startStreamingPoll(sessionId, sendBtn, chainId) {
_recoveryChainId = chainId;
if (_streamingPollInterval) clearInterval(_streamingPollInterval);
_streamingPollInterval = setInterval(async () => {
try {
// Neue Nachrichten laden (inkl. der noch streamenden)
await pollHistory(true);
const status = await apiIsStreaming(sessionId);
if (!status.streaming) {
clearInterval(_streamingPollInterval);
_streamingPollInterval = null;
isStreaming = false;
if (sendBtn) sendBtn.disabled = false;
// Finale Nachrichten nochmal laden
await pollHistory(true);
// Chain-Daten laden für Meta-Info im Chat
const cid = _recoveryChainId || status.chainId;
if (cid) {
const chain = await apiGetChain(cid);
if (chain) addChainMetaBubble(chain, false);
}
setMeta('Bereit');
_recoveryChainId = null;
}
} catch (e) {
console.error('Streaming-Poll fehlgeschlagen:', e);
}
}, 1500);
}
registerPage('chat', {
async init(params) {
const sessionId = params.id;
if (!sessionId) return navigateTo('dashboard');
currentSessionId = sessionId;
const session = await apiGetSession(sessionId);
if (!session) return navigateTo('dashboard');
// Kein Agent = kein Chat
if (!session.agentId) {
navigateTo('session', { id: sessionId });
return;
}
const titleEl = document.getElementById('sessionTitle');
if (titleEl) {
titleEl.textContent = session.name || 'Unbenannt';
}
// Custom Command Buttons
const cmdBar = document.getElementById('customCmdBar');
if (cmdBar) {
cmdBar.innerHTML = '';
let cmds = [];
try { cmds = JSON.parse(session.customCommands || '[]') || []; } catch(e) {}
cmds.forEach(cmd => {
if (!cmd.name || !cmd.command) return;
const btn = document.createElement('button');
btn.className = 'custom-cmd-btn';
btn.textContent = cmd.name;
btn.title = cmd.command;
btn.addEventListener('click', async () => {
btn.disabled = true;
btn.classList.add('running');
try {
await apiExecCommand(sessionId, cmd.command);
} catch(e) {
console.error('Exec failed:', e);
}
setTimeout(() => {
btn.disabled = false;
btn.classList.remove('running');
}, 1000);
});
cmdBar.appendChild(btn);
});
}
// Zurück zur Session-Edit-Seite
const backBtn = document.getElementById('chatBackBtn');
if (backBtn) {
backBtn.addEventListener('click', () => navigateTo('session', { id: sessionId }));
}
// Send
const sendBtn = document.getElementById('send');
if (sendBtn) sendBtn.addEventListener('click', sendMessage);
const inputEl = document.getElementById('input');
if (inputEl) {
inputEl.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
e.preventDefault();
sendMessage();
}
});
inputEl.addEventListener('input', function () {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 150) + 'px';
});
inputEl.dispatchEvent(new Event('input'))
}
// Workspace Explorer
const explorerBtn = document.getElementById('explorerBtn');
if (explorerBtn) {
explorerBtn.addEventListener('click', () => {
openFileExplorer({
sessionId,
onSelect(ref) {
addPendingRef(ref);
document.getElementById('input')?.focus();
}
});
});
}
// File Upload
const fileBtn = document.getElementById('fileBtn');
const fileInput = document.getElementById('fileInput');
if (fileBtn && fileInput) {
fileBtn.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) handleMultipleFiles(e.target.files);
});
}
// Drag & Drop
const chatContainer = document.querySelector('.chat-page');
if (chatContainer) {
chatContainer.addEventListener('dragover', (e) => { e.preventDefault(); e.stopPropagation(); });
chatContainer.addEventListener('drop', (e) => {
e.preventDefault();
e.stopPropagation();
if (e.dataTransfer.files.length > 0) handleMultipleFiles(e.dataTransfer.files);
});
}
// Paste
if (inputEl) {
inputEl.addEventListener('paste', (e) => {
const items = e.clipboardData?.items;
if (!items) return;
for (const item of items) {
if (item.kind === 'file') {
e.preventDefault();
handleFileSelect(item.getAsFile());
return;
}
}
});
}
// Header-Menü (3-Dot)
const chatMenuBtn = document.getElementById('chatMenuBtn');
const chatMenuDropdown = document.getElementById('chatMenuDropdown');
if (chatMenuBtn && chatMenuDropdown) {
chatMenuBtn.addEventListener('click', (e) => {
e.stopPropagation();
chatMenuDropdown.hidden = !chatMenuDropdown.hidden;
});
document.addEventListener('click', this._closeMenu = () => {
chatMenuDropdown.hidden = true;
});
}
// System-Prompt anzeigen
const systemPromptBtn = document.getElementById('systemPromptBtn');
if (systemPromptBtn) systemPromptBtn.addEventListener('click', () => {
if (chatMenuDropdown) chatMenuDropdown.hidden = true;
showSystemPromptModal(sessionId);
});
// Archivieren
const archiveBtn = document.getElementById('archiveBtn');
if (archiveBtn) archiveBtn.addEventListener('click', () => {
if (chatMenuDropdown) chatMenuDropdown.hidden = true;
clearChat();
});
// Datenbank sichern
const backupDbBtn = document.getElementById('backupDbBtn');
if (backupDbBtn) backupDbBtn.addEventListener('click', async () => {
if (chatMenuDropdown) chatMenuDropdown.hidden = true;
backupDbBtn.disabled = true;
try {
await apiBackupDb();
setMetaHtml('<i class="fa fa-check-circle"></i> Backup erstellt und heruntergeladen');
} catch (e) {
console.error('Backup fehlgeschlagen:', e);
setMetaHtml('<i class="fa fa-times-circle"></i> Backup fehlgeschlagen');
} finally {
backupDbBtn.disabled = false;
}
});
// State zurücksetzen und History laden
messages.length = 0;
lastKnownMessageId = 0;
loadedArchiveCount = 0;
isStreaming = false;
await loadHistory();
// Nach Font-Loading nochmal scrollen (Schriftart kann Höhe verändern)
document.fonts.ready.then(() => {
requestAnimationFrame(() => scrollDown());
});
// Prüfen ob Backend gerade noch streamt (z.B. nach Page-Refresh)
try {
const status = await apiIsStreaming(sessionId);
if (status.streaming) {
isStreaming = true;
if (sendBtn) sendBtn.disabled = true;
setMetaHtml('<span><i class="fa fa-hourglass-half"></i> Streaming...</span>');
startStreamingPoll(sessionId, sendBtn, status.chainId);
}
} catch (e) {
console.error('Streaming-Status prüfen fehlgeschlagen:', e);
}
},
destroy() {
if (this._closeMenu) {
document.removeEventListener('click', this._closeMenu);
this._closeMenu = null;
}
if (_streamingPollInterval) {
clearInterval(_streamingPollInterval);
_streamingPollInterval = null;
}
stopHistoryPolling();
messages.length = 0;
lastKnownMessageId = 0;
loadedArchiveCount = 0;
isStreaming = false;
clearPendingFiles();
clearPendingRefs();
}
});