refactor: ui.js, chat.js, history.js in 13 modulare Dateien aufteilen
Die monolithische ui.js (1534 Zeilen) zusammen mit chat.js (612) und history.js (626) in fokussierte Module aufgeteilt: Neue Dateien aus ui.js: - ui-helpers.js (126) - DOM-Utilities, $, esc, truncate, scrollDown, Toast - markdown.js (143) - renderMarkdown, wrapKreaGalleries, LaTeX/Code-Schutz - cards.js (90) - buildSessionCard, buildAgentCard - msg-actions.js (208) - Delete, Mark, Share, Reply-Button, Timestamp - tool-ui.js (332) - Tool-Boxes, Diffs, Todo-Liste, Visibility - modals.js (190) - System-Prompt, Command-Output, Krea-Image Modals - stream-ui.js (124) - Stream-Bubble, Review-Streaming - bubbles.js (312) - User/Assistant/Error/Warning/Choice/Review Bubbles Neue Dateien aus chat.js: - reply.js (54) - Reply/Quote-Handling mit pendingReply State - files.js (110) - File-Upload, Workspace-Refs mit pendingFiles/Refs State Neue Datei aus history.js: - archive.js (173) - Archiv-Loading mit loadedArchiveCount State Verbleibend: - chat.js (448) - Stream-Events, sendMessage, handleChoiceClick, Plan-Mode - history.js (464) - History laden, Polling, Message-Rendering Kein Code verändert, nur verschoben. Alle 94 Funktionen verifiziert. Globale Variablen in die Module verschoben, wo sie primär genutzt werden.
This commit is contained in:
1 parent
a82d8cfb05
commit
0dcc05ebf9
15 files changed
+1885
-1873
No files matched your search
@@ -0,0 +1,332 @@
|
||||
// ── Tool Call Visibility ──
|
||||
|
||||
const MODIFICATION_TOOLS = ['write_file', 'edit_file', 'delete_file', 'memory_write'];
|
||||
const TOOL_DISPLAY_NAMES = {
|
||||
write_file: 'Datei schreiben',
|
||||
edit_file: 'Datei bearbeiten',
|
||||
delete_file: 'Datei löschen',
|
||||
memory_write: 'Erinnerungen festhalten',
|
||||
web_search: 'Web-Suche'
|
||||
};
|
||||
|
||||
function isToolVisible(toolName) {
|
||||
const mode = loadSettings().toolCallDisplay;
|
||||
if (mode === 'all') return true;
|
||||
if (mode === 'hidden') return false;
|
||||
// 'modifications' – nur modifizierende Tools + web_search anzeigen
|
||||
if (toolName === 'web_search') return true;
|
||||
return MODIFICATION_TOOLS.includes(toolName);
|
||||
}
|
||||
|
||||
// ── Chain Meta Visibility ──
|
||||
|
||||
function isChainMetaVisible(chainData) {
|
||||
const mode = loadSettings().chainMetaDisplay;
|
||||
if (mode === 'always') return true;
|
||||
if (mode === 'never') return false;
|
||||
// 'sometimes' – nur bei auffälligen Chains anzeigen
|
||||
if (!chainData) return false;
|
||||
const inOut = (chainData.inputTokens || 0) + (chainData.outputTokens || 0);
|
||||
if (inOut >= 10000) return true;
|
||||
if ((chainData.iterations || 0) >= 25) return true;
|
||||
if ((chainData.durationMs || 0) >= 90000) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── Tool Boxes ──
|
||||
|
||||
function addToolBoxLoading(name) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'tool-box streamed';
|
||||
div.dataset.toolName = name;
|
||||
const displayName = TOOL_DISPLAY_NAMES[name] || name;
|
||||
div.innerHTML = '<span class="tool-name">' + esc(displayName) + '</span> <span class="tool-spinner"><i class="fa fa-hourglass-half"></i></span>';
|
||||
chatEl().appendChild(div);
|
||||
scrollDown();
|
||||
return div;
|
||||
}
|
||||
|
||||
function renderToolBoxContent(div, name, inputJson, result, isError, sources) {
|
||||
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 = TOOL_DISPLAY_NAMES[name] || 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;
|
||||
|
||||
// Web-Search Sources – immer sichtbar, kein Toggle
|
||||
if (name === 'web_search' && sources && sources.length > 0) {
|
||||
const sourcesList = document.createElement('div');
|
||||
sourcesList.className = 'web-search-sources';
|
||||
sources.forEach(function(s) {
|
||||
const link = document.createElement('a');
|
||||
link.href = s.url;
|
||||
link.target = '_blank';
|
||||
link.rel = 'noopener noreferrer';
|
||||
link.className = 'web-search-source';
|
||||
var domain = '';
|
||||
try { domain = new URL(s.url).hostname.replace('www.', ''); } catch(e) { domain = s.url; }
|
||||
link.innerHTML = '<i class="fa fa-external-link"></i> <span class="source-domain">' + esc(domain) + '</span>' +
|
||||
(s.title ? '<span class="source-title">' + esc(s.title) + '</span>' : '');
|
||||
sourcesList.appendChild(link);
|
||||
});
|
||||
div.appendChild(sourcesList);
|
||||
}
|
||||
|
||||
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 if (name === 'todo_write') {
|
||||
// Todo-Liste: kein tool-input, standardmäßig sichtbar, custom parser
|
||||
if (result) {
|
||||
const todoList = buildTodoList(result, isError);
|
||||
div.appendChild(todoList);
|
||||
foldTarget = todoList;
|
||||
}
|
||||
|
||||
} 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 || '';
|
||||
resultDiv.hidden = true;
|
||||
div.appendChild(resultDiv);
|
||||
foldTarget = resultDiv;
|
||||
}
|
||||
|
||||
if (foldTarget && name !== 'todo_write') {
|
||||
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 container = document.createElement('div');
|
||||
container.className = 'tool-diff';
|
||||
|
||||
for (const line of lines) {
|
||||
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);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
function buildTodoList(result, isError) {
|
||||
const container = document.createElement('div');
|
||||
container.className = 'todo-list' + (isError ? ' tool-error' : '');
|
||||
|
||||
const lines = result.split('\n').filter(l => l.trim());
|
||||
for (const line of lines) {
|
||||
// Format: ICON [id] [priority] content
|
||||
const m = line.match(/^(\S+)\s+\[([^\]]+)\]\s+(?:\[([^\]]+)\]\s+)?(.+)$/);
|
||||
const row = document.createElement('div');
|
||||
row.className = 'todo-item';
|
||||
|
||||
if (m) {
|
||||
const icon = m[1]; // ✅ / 🔄 / ⬜
|
||||
const id = m[2];
|
||||
const prio = m[3] || null;
|
||||
const text = m[4];
|
||||
|
||||
const statusClass =
|
||||
icon === '✅' ? 'todo-done' :
|
||||
icon === '🔄' ? 'todo-progress' :
|
||||
'todo-pending';
|
||||
row.classList.add(statusClass);
|
||||
|
||||
const iconSpan = document.createElement('span');
|
||||
iconSpan.className = 'todo-icon';
|
||||
iconSpan.textContent = icon;
|
||||
|
||||
const idSpan = document.createElement('span');
|
||||
idSpan.className = 'todo-id';
|
||||
idSpan.textContent = id + '.';
|
||||
|
||||
const textSpan = document.createElement('span');
|
||||
textSpan.className = 'todo-text';
|
||||
textSpan.textContent = text;
|
||||
|
||||
row.appendChild(iconSpan);
|
||||
row.appendChild(idSpan);
|
||||
row.appendChild(textSpan);
|
||||
|
||||
if (prio) {
|
||||
const prioSpan = document.createElement('span');
|
||||
prioSpan.className = 'todo-prio todo-prio-' + prio.toLowerCase();
|
||||
prioSpan.textContent = prio;
|
||||
row.appendChild(prioSpan);
|
||||
}
|
||||
} else {
|
||||
row.textContent = line;
|
||||
}
|
||||
|
||||
container.appendChild(row);
|
||||
}
|
||||
|
||||
parseFluentEmojis(container);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
function finishToolBox(div, data) {
|
||||
if (!div) return;
|
||||
renderToolBoxContent(div, data.name, data.input, data.result, data.isError, data.sources);
|
||||
scrollDown();
|
||||
}
|
||||
|
||||
function addToolBoxStatic(name, input, result, isError, sources) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'tool-box';
|
||||
div.dataset.toolName = name;
|
||||
renderToolBoxContent(div, name, input, result, isError, sources);
|
||||
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 toolName = lastBox.dataset.toolName || '';
|
||||
const isTodoWrite = toolName === 'todo_write';
|
||||
|
||||
if (isTodoWrite) {
|
||||
// Vorhandene todo-list ersetzen oder neu erstellen
|
||||
const existing = lastBox.querySelector('.todo-list');
|
||||
if (existing) existing.remove();
|
||||
if (result && !isError) {
|
||||
const todoList = buildTodoList(result, isError);
|
||||
lastBox.appendChild(todoList);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const existing = lastBox.querySelector('.tool-result');
|
||||
if (existing) {
|
||||
existing.textContent = result || '';
|
||||
if (isError) existing.classList.add('tool-error');
|
||||
} else {
|
||||
const resultDiv = document.createElement('div');
|
||||
resultDiv.className = 'tool-result' + (isError ? ' tool-error' : '');
|
||||
resultDiv.textContent = result || '';
|
||||
resultDiv.hidden = true;
|
||||
lastBox.appendChild(resultDiv);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user