// ── Tool Call Visibility ── const MODIFICATION_TOOLS = ['write_file', 'edit_file', 'delete_file', 'memory_write', 'remote_execute']; 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', remote_execute: 'Remote-Befehl', memory_search: 'Erinnerungen durchsuchen', send_to_session: 'Nachricht an Session senden' }; 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 = '' + esc(displayName) + ' '; 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 = ' ' + esc(domain) + '' + (s.title ? '' + esc(s.title) + '' : ''); 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 _buildDiffLines(container, lines, type) { const cssClass = type === 'added' ? 'diff-added' : 'diff-removed'; const markerChar = type === 'added' ? '+' : '-'; for (const line of lines) { const row = document.createElement('div'); row.className = 'diff-line ' + cssClass; const marker = document.createElement('span'); marker.className = 'diff-marker'; marker.textContent = markerChar; const content = document.createElement('span'); content.className = 'diff-content'; content.textContent = line; row.appendChild(marker); row.appendChild(content); container.appendChild(row); } } function buildEditDiff(oldText, newText) { const container = document.createElement('div'); container.className = 'tool-diff'; _buildDiffLines(container, oldText.split('\n'), 'removed'); _buildDiffLines(container, newText.split('\n'), 'added'); return container; } function buildWritePreview(content) { const container = document.createElement('div'); container.className = 'tool-diff'; _buildDiffLines(container, content.split('\n'), 'added'); return container; } function buildTodoList(result, isError) { const container = document.createElement('div'); container.className = 'todo-list' + (isError ? ' tool-error' : ''); const lines = result.split('\n').map(l => l.replace(/\r$/, '')).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(); }