Avatar: Ctrl+V paste + serverseitige Skalierung auf max 256px; Tool-Boxes: Diff-View für edit/write_file, Foldout für alle Tools, stop_reason im Chat; ImageSharp 3.1.12
This commit is contained in:
1 parent
18b2feef4a
commit
e662548d79
13 files changed
+416
-42
No files matched your search
+193
-10
@@ -58,6 +58,16 @@ function addErrorBubble(text, streamed = false) {
|
||||
scrollDown();
|
||||
}
|
||||
|
||||
function addWarningBubble(text, streamed = false) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'msg system-info';
|
||||
if (streamed) div.classList.add('streamed');
|
||||
div.style.color = '#f0a500';
|
||||
div.textContent = (text || '').trim();
|
||||
chatEl().appendChild(div);
|
||||
scrollDown();
|
||||
}
|
||||
|
||||
// ── Streaming Bubble ──
|
||||
|
||||
let _streamDiv = null;
|
||||
@@ -104,28 +114,201 @@ function addToolBoxLoading(name) {
|
||||
return div;
|
||||
}
|
||||
|
||||
function renderToolBoxContent(div, name, inputJson, result, isError) {
|
||||
let parsedInput = null;
|
||||
try { parsedInput = JSON.parse(inputJson); } catch {}
|
||||
|
||||
const header = document.createElement('div');
|
||||
header.className = 'tool-box-header';
|
||||
|
||||
const nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'tool-name';
|
||||
nameSpan.textContent = name;
|
||||
header.appendChild(nameSpan);
|
||||
|
||||
if (isError) {
|
||||
const errSpan = document.createElement('span');
|
||||
errSpan.className = 'tool-error-badge';
|
||||
errSpan.textContent = 'error';
|
||||
header.appendChild(errSpan);
|
||||
}
|
||||
|
||||
div.innerHTML = '';
|
||||
div.appendChild(header);
|
||||
|
||||
let foldTarget = null;
|
||||
|
||||
if (name === 'edit_file' && parsedInput && parsedInput.path) {
|
||||
const pathSpan = document.createElement('span');
|
||||
pathSpan.className = 'tool-input';
|
||||
pathSpan.textContent = parsedInput.path;
|
||||
header.appendChild(pathSpan);
|
||||
|
||||
const diff = buildEditDiff(parsedInput.old_text || '', parsedInput.new_text || '');
|
||||
diff.hidden = true;
|
||||
div.appendChild(diff);
|
||||
foldTarget = diff;
|
||||
|
||||
} else if (name === 'write_file' && parsedInput && parsedInput.path) {
|
||||
const pathSpan = document.createElement('span');
|
||||
pathSpan.className = 'tool-input';
|
||||
pathSpan.textContent = parsedInput.path;
|
||||
header.appendChild(pathSpan);
|
||||
|
||||
const content = parsedInput.content || '';
|
||||
const lines = content.split('\n');
|
||||
const lineCount = lines.length;
|
||||
const charCount = content.length;
|
||||
|
||||
const meta = document.createElement('span');
|
||||
meta.className = 'tool-write-meta';
|
||||
meta.textContent = lineCount + ' Zeilen, ' + charCount + ' Zeichen';
|
||||
header.appendChild(meta);
|
||||
|
||||
const preview = buildWritePreview(content);
|
||||
preview.hidden = true;
|
||||
div.appendChild(preview);
|
||||
foldTarget = preview;
|
||||
|
||||
} else {
|
||||
// Standard: path/command als input anzeigen
|
||||
let inputLabel = '';
|
||||
if (parsedInput) {
|
||||
inputLabel = parsedInput.path || parsedInput.command || JSON.stringify(parsedInput).substring(0, 120);
|
||||
} else {
|
||||
inputLabel = truncate(inputJson, 120);
|
||||
}
|
||||
if (inputLabel) {
|
||||
const inputSpan = document.createElement('span');
|
||||
inputSpan.className = 'tool-input';
|
||||
inputSpan.textContent = inputLabel;
|
||||
header.appendChild(inputSpan);
|
||||
}
|
||||
|
||||
const resultDiv = document.createElement('div');
|
||||
resultDiv.className = 'tool-result' + (isError ? ' tool-error' : '');
|
||||
resultDiv.textContent = result ? truncate(result, 300) : '';
|
||||
resultDiv.hidden = true;
|
||||
div.appendChild(resultDiv);
|
||||
foldTarget = resultDiv;
|
||||
}
|
||||
|
||||
if (foldTarget) {
|
||||
header.style.cursor = 'pointer';
|
||||
header.addEventListener('click', () => {
|
||||
foldTarget.hidden = !foldTarget.hidden;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function buildEditDiff(oldText, newText) {
|
||||
const oldLines = oldText.split('\n');
|
||||
const newLines = newText.split('\n');
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.className = 'tool-diff';
|
||||
|
||||
// Entfernte Zeilen (-)
|
||||
for (const line of oldLines) {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'diff-line diff-removed';
|
||||
const marker = document.createElement('span');
|
||||
marker.className = 'diff-marker';
|
||||
marker.textContent = '-';
|
||||
const content = document.createElement('span');
|
||||
content.className = 'diff-content';
|
||||
content.textContent = line;
|
||||
row.appendChild(marker);
|
||||
row.appendChild(content);
|
||||
container.appendChild(row);
|
||||
}
|
||||
|
||||
// Hinzugefügte Zeilen (+)
|
||||
for (const line of newLines) {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'diff-line diff-added';
|
||||
const marker = document.createElement('span');
|
||||
marker.className = 'diff-marker';
|
||||
marker.textContent = '+';
|
||||
const content = document.createElement('span');
|
||||
content.className = 'diff-content';
|
||||
content.textContent = line;
|
||||
row.appendChild(marker);
|
||||
row.appendChild(content);
|
||||
container.appendChild(row);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
function buildWritePreview(content) {
|
||||
const lines = content.split('\n');
|
||||
const maxLines = 20;
|
||||
const shown = lines.slice(0, maxLines);
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.className = 'tool-diff';
|
||||
|
||||
for (const line of shown) {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'diff-line diff-added';
|
||||
const marker = document.createElement('span');
|
||||
marker.className = 'diff-marker';
|
||||
marker.textContent = '+';
|
||||
const contentSpan = document.createElement('span');
|
||||
contentSpan.className = 'diff-content';
|
||||
contentSpan.textContent = line;
|
||||
row.appendChild(marker);
|
||||
row.appendChild(contentSpan);
|
||||
container.appendChild(row);
|
||||
}
|
||||
|
||||
if (lines.length > maxLines) {
|
||||
const more = document.createElement('div');
|
||||
more.className = 'diff-more';
|
||||
more.textContent = '… ' + (lines.length - maxLines) + ' weitere Zeilen';
|
||||
container.appendChild(more);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
function finishToolBox(div, data) {
|
||||
if (!div) return;
|
||||
div.innerHTML = '<span class="tool-name">' + esc(data.name) + '</span>'
|
||||
+ '<span class="tool-input">' + esc(truncate(data.input, 120)) + '</span>'
|
||||
+ '<div class="tool-result ' + (data.isError ? 'tool-error' : '') + '">'
|
||||
+ esc(truncate(data.result, 300)) + '</div>';
|
||||
renderToolBoxContent(div, data.name, data.input, data.result, data.isError);
|
||||
scrollDown();
|
||||
}
|
||||
|
||||
function addToolBoxStatic(name, input, result, isError) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'tool-box';
|
||||
div.innerHTML = '<span class="tool-name">' + esc(name) + '</span>'
|
||||
+ '<span class="tool-input">' + esc(truncate(input, 120)) + '</span>';
|
||||
if (result) {
|
||||
div.innerHTML += '<div class="tool-result ' + (isError ? 'tool-error' : '') + '">'
|
||||
+ esc(truncate(result, 300)) + '</div>';
|
||||
}
|
||||
renderToolBoxContent(div, name, input, result, isError);
|
||||
chatEl().appendChild(div);
|
||||
scrollDown();
|
||||
}
|
||||
|
||||
function appendToolResult(result, isError) {
|
||||
// Letzten tool-box im Chat finden und Result in die bereits vorhandene .tool-result-Div schreiben
|
||||
const boxes = chatEl().querySelectorAll('.tool-box');
|
||||
const lastBox = boxes[boxes.length - 1];
|
||||
if (!lastBox) return;
|
||||
|
||||
// Wenn Diff vorhanden (edit_file/write_file) → kein Result anhängen
|
||||
if (lastBox.querySelector('.tool-diff')) return;
|
||||
|
||||
const existing = lastBox.querySelector('.tool-result');
|
||||
if (existing) {
|
||||
existing.textContent = truncate(result, 300);
|
||||
if (isError) existing.classList.add('tool-error');
|
||||
} else {
|
||||
const resultDiv = document.createElement('div');
|
||||
resultDiv.className = 'tool-result' + (isError ? ' tool-error' : '');
|
||||
resultDiv.textContent = truncate(result, 300);
|
||||
resultDiv.hidden = true;
|
||||
lastBox.appendChild(resultDiv);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Streamed Elements ──
|
||||
|
||||
function removeStreamedElements() {
|
||||
|
||||
Reference in new issue
Block a user