Frontend Refactoring: HTML/CSS/JS getrennt, modulare Struktur
This commit is contained in:
1 parent
9f2f3fee1f
commit
19f926da05
7 files changed
+534
-294
No files matched your search
@@ -0,0 +1,108 @@
|
||||
// ── Chat State ──
|
||||
|
||||
const messages = [];
|
||||
|
||||
function clearChat() {
|
||||
messages.length = 0;
|
||||
chatEl().innerHTML = '';
|
||||
setMeta('');
|
||||
}
|
||||
|
||||
// ── Send Message ──
|
||||
|
||||
async function sendMessage() {
|
||||
const input = $('#input');
|
||||
const btn = $('#send');
|
||||
const model = $('#model').value;
|
||||
const systemPrompt = $('#systemPrompt').value.trim();
|
||||
const text = input.value.trim();
|
||||
if (!text) return;
|
||||
|
||||
messages.push({ role: 'user', content: text });
|
||||
addUserBubble(text);
|
||||
|
||||
input.value = '';
|
||||
input.style.height = 'auto';
|
||||
btn.disabled = true;
|
||||
setMetaHtml('<span style="color:#7c83ff">⏳ Streaming...</span>');
|
||||
|
||||
let fullText = '';
|
||||
let currentToolBox = null;
|
||||
|
||||
try {
|
||||
const resp = await apiChat(model, messages, systemPrompt);
|
||||
const reader = resp.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
let eventType = null;
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop() || '';
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('event: ')) {
|
||||
eventType = line.substring(7).trim();
|
||||
} else if (line.startsWith('data: ') && eventType) {
|
||||
let data;
|
||||
try { data = JSON.parse(line.substring(6)); } catch { continue; }
|
||||
|
||||
switch (eventType) {
|
||||
case 'text_start':
|
||||
endStreamBubble();
|
||||
startStreamBubble();
|
||||
break;
|
||||
|
||||
case 'text_delta':
|
||||
appendToStream(data.text);
|
||||
fullText += data.text;
|
||||
break;
|
||||
|
||||
case 'text_end':
|
||||
endStreamBubble();
|
||||
break;
|
||||
|
||||
case 'tool_start':
|
||||
endStreamBubble();
|
||||
currentToolBox = addToolBoxLoading(data.name);
|
||||
break;
|
||||
|
||||
case 'tool_end':
|
||||
finishToolBox(currentToolBox, data);
|
||||
currentToolBox = null;
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
endStreamBubble();
|
||||
addErrorBubble(data.error);
|
||||
break;
|
||||
|
||||
case 'done':
|
||||
endStreamBubble();
|
||||
let meta = `Model: ${data.model || model} | In: ${data.inputTokens || 0} | Out: ${data.outputTokens || 0}`;
|
||||
if (data.iterations > 1) meta += ` | Iterations: ${data.iterations}`;
|
||||
if (data.durationMs) meta += ` | ${(data.durationMs / 1000).toFixed(1)}s`;
|
||||
setMeta(meta);
|
||||
break;
|
||||
}
|
||||
eventType = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fullText) {
|
||||
messages.push({ role: 'assistant', content: fullText });
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
endStreamBubble();
|
||||
addErrorBubble('Netzwerk-Fehler: ' + e.message);
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
endStreamBubble();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user