Protokoll #27282

ID27282
Zeitstempel2025-12-28 03:00:13.243285
Clientroot
IP145.224.72.140
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens13,049 (Input: 2,662, Output: 10,387)
Dauer90 ms
Request-Zeit2025-12-28 03:00:13.243285
Response-Zeit2025-12-28 03:00:13.333714

Request

{
    "event": "PreToolUse",
    "tool_name": "Edit",
    "tool_input": {
        "file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/View\/chat\/index.php",
        "old_string": "<script>\nconst sidebar = document.getElementById('sidebar');\nconst overlay = document.getElementById('overlay');\nconst toggle = document.getElementById('toggle');\nconst messages = document.getElementById('messages');\nconst form = document.getElementById('chatForm');\nconst sendBtn = document.getElementById('sendBtn');\nconst html = document.documentElement;\nconst configPanel = document.getElementById('configPanel');\nconst configPanelToggle = document.getElementById('configPanelToggle');\nconst configPanelClose = document.getElementById('configPanelClose');\n\n\/\/ ========== THEME ==========\nconst savedTheme = localStorage.getItem('chat-theme') || 'light';\nhtml.setAttribute('data-theme', savedTheme);\nupdateThemeUI();\n\nfunction updateThemeUI() {\n    const theme = html.getAttribute('data-theme');\n    const icon = document.getElementById('configThemeIcon');\n    const text = document.getElementById('configThemeText');\n    if (icon) icon.textContent = theme === 'dark' ? '☀' : '☽';\n    if (text) text.textContent = theme === 'dark' ? 'Dark Mode' : 'Light Mode';\n}\n\ndocument.getElementById('configThemeToggle')?.addEventListener('click', () => {\n    const next = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';\n    html.setAttribute('data-theme', next);\n    localStorage.setItem('chat-theme', next);\n    updateThemeUI();\n});\n\n\/\/ ========== SIDEBAR ==========\ntoggle.addEventListener('click', (e) => {\n    e.stopPropagation();\n    sidebar.classList.toggle('chat-sidebar--open');\n    overlay.classList.toggle('chat-overlay--visible');\n});\n\noverlay.addEventListener('click', () => {\n    sidebar.classList.remove('chat-sidebar--open');\n    overlay.classList.remove('chat-overlay--visible');\n});\n\n\/\/ ========== CONFIG PANEL ==========\nconfigPanelToggle.addEventListener('click', () => {\n    configPanel.classList.toggle('config-panel--open');\n    configPanelToggle.setAttribute('aria-expanded', configPanel.classList.contains('config-panel--open'));\n});\n\nconfigPanelClose.addEventListener('click', () => {\n    configPanel.classList.remove('config-panel--open');\n    configPanelToggle.setAttribute('aria-expanded', 'false');\n});\n\n\/\/ ========== SYNC CONFIG PANEL TO HIDDEN INPUTS ==========\nfunction syncToHidden(configId, hiddenId) {\n    const config = document.getElementById(configId);\n    const hidden = document.getElementById(hiddenId);\n    if (config && hidden) {\n        config.addEventListener('change', function() {\n            hidden.value = this.value;\n            \/\/ Visual feedback\n            this.classList.add('is-saving');\n            setTimeout(() => {\n                this.classList.remove('is-saving');\n                this.classList.add('is-saved');\n                setTimeout(() => this.classList.remove('is-saved'), 600);\n            }, 100);\n            \/\/ Save to localStorage\n            localStorage.setItem('chat-' + hiddenId, this.value);\n        });\n        \/\/ Load from localStorage\n        const saved = localStorage.getItem('chat-' + hiddenId);\n        if (saved) {\n            const option = config.querySelector('option[value=\"' + saved + '\"]');\n            if (option) {\n                config.value = saved;\n                hidden.value = saved;\n            }\n        }\n    }\n}\n\nsyncToHidden('configModel', 'hiddenModel');\nsyncToHidden('configContextLimit', 'hiddenContextLimit');\nsyncToHidden('configMaxTokens', 'hiddenMaxTokens');\nsyncToHidden('configSystemPrompt', 'hiddenSystemPrompt');\nsyncToHidden('configStructure', 'hiddenStructure');\nsyncToHidden('configAuthorProfile', 'hiddenAuthorProfile');\n\n\/\/ ========== COLLECTIONS SYNC ==========\nfunction syncCollections() {\n    const container = document.getElementById('hiddenCollections');\n    container.innerHTML = '';\n    document.querySelectorAll('#configCollections input[type=\"checkbox\"]:checked').forEach(cb => {\n        const input = document.createElement('input');\n        input.type = 'hidden';\n        input.name = 'collections[]';\n        input.value = cb.value;\n        container.appendChild(input);\n    });\n}\nsyncCollections();\ndocument.querySelectorAll('#configCollections input[type=\"checkbox\"]').forEach(cb => {\n    cb.addEventListener('change', syncCollections);\n});\n\n\/\/ ========== TEMPERATURE ==========\nconst tempSlider = document.getElementById('configTemperature');\nconst tempValue = document.getElementById('tempValuePanel');\nconst hiddenTemp = document.getElementById('hiddenTemperature');\n\ntempSlider?.addEventListener('input', () => {\n    const val = parseFloat(tempSlider.value).toFixed(1);\n    tempValue.textContent = val;\n    hiddenTemp.value = val;\n    updatePresetHighlight();\n});\n\nfunction updatePresetHighlight() {\n    const currentTemp = parseFloat(tempSlider.value);\n    document.querySelectorAll('.config-panel__preset').forEach(btn => {\n        const btnTemp = parseFloat(btn.dataset.temp);\n        btn.classList.toggle('config-panel__preset--active', Math.abs(btnTemp - currentTemp) < 0.05);\n    });\n}\n\n\/\/ Preset buttons click handler\ndocument.querySelectorAll('.config-panel__preset').forEach(btn => {\n    btn.addEventListener('click', () => {\n        const temp = parseFloat(btn.dataset.temp);\n        const tokens = parseInt(btn.dataset.tokens);\n        tempSlider.value = temp;\n        tempValue.textContent = temp.toFixed(1);\n        hiddenTemp.value = temp;\n        document.getElementById('configMaxTokens').value = tokens;\n        document.getElementById('hiddenMaxTokens').value = tokens;\n        updatePresetHighlight();\n        \/\/ Visual feedback\n        btn.classList.add('is-saving');\n        setTimeout(() => {\n            btn.classList.remove('is-saving');\n            btn.classList.add('is-saved');\n            setTimeout(() => btn.classList.remove('is-saved'), 600);\n        }, 100);\n    });\n});\n\n\/\/ Streaming Form Handler with SSE\nform.addEventListener('submit', async (e) => {\n    e.preventDefault();\n\n    const messageInput = form.querySelector('input[name=\"message\"]');\n    const question = messageInput.value.trim();\n    if (!question) return;\n\n    sendBtn.disabled = true;\n    sendBtn.classList.add('chat-send--loading');\n\n    const messagesInner = document.querySelector('#messages .chat-messages__inner');\n\n    \/\/ Remove welcome if present\n    const welcome = messagesInner.querySelector('.chat-welcome');\n    if (welcome) welcome.remove();\n\n    \/\/ Add user message immediately\n    const userMsg = document.createElement('div');\n    userMsg.className = 'chat-msg chat-msg--user';\n    userMsg.innerHTML = '<div class=\"chat-msg__content\">' + escapeHtml(question) + '<\/div>';\n    messagesInner.appendChild(userMsg);\n\n    \/\/ Add progress container\n    const progressContainer = document.createElement('div');\n    progressContainer.className = 'chat-progress';\n    progressContainer.innerHTML = '<div class=\"chat-progress__header\"><span class=\"chat-progress__spinner\"><\/span><span class=\"chat-progress__current\">Starte...<\/span><\/div><div class=\"chat-progress__log\"><\/div>';\n    messagesInner.appendChild(progressContainer);\n    const progressHeader = progressContainer.querySelector('.chat-progress__current');\n    const progressLog = progressContainer.querySelector('.chat-progress__log');\n\n    messages.scrollTop = messages.scrollHeight;\n\n    \/\/ Collect form data\n    const formData = new FormData(form);\n    formData.append('_csrf_token', '<?= $csrfToken ?>');\n\n    \/\/ Use fetch with streaming\n    try {\n        const response = await fetch('\/chat\/<?= $session['uuid'] ?? '' ?>\/message\/stream', {\n            method: 'POST',\n            body: formData,\n            headers: {\n                'Accept': 'text\/event-stream'\n            }\n        });\n\n        if (!response.ok) {\n            throw new Error('HTTP ' + response.status);\n        }\n\n        if (!response.body) {\n            throw new Error('ReadableStream not supported');\n        }\n\n        const reader = response.body.getReader();\n        const decoder = new TextDecoder();\n        let buffer = '';\n        let chunkCount = 0;\n\n        console.log('SSE: Starting to read stream...');\n\n        while (true) {\n            const {value, done} = await reader.read();\n            if (done) {\n                console.log('SSE: Stream ended after', chunkCount, 'chunks');\n                break;\n            }\n\n            chunkCount++;\n            const chunk = decoder.decode(value, {stream: true});\n            console.log('SSE CHUNK #' + chunkCount + ' (' + chunk.length + ' bytes):', chunk.substring(0, 100));\n\n            buffer += chunk;\n            const lines = buffer.split('\\n');\n            buffer = lines.pop() || '';\n\n            for (const line of lines) {\n                if (line.trim() === '' || line.startsWith(':')) continue;\n                console.log('SSE LINE:', line);\n                if (line.startsWith('event: ')) {\n                    const eventType = line.substring(7);\n                    continue;\n                }\n                if (line.startsWith('data: ')) {\n                    try {\n                        const data = JSON.parse(line.substring(6));\n                        console.log('SSE DATA:', data);\n\n                        if (data.ts && data.msg && data.step) {\n                            \/\/ Check if this is a start event or completion event\n                            const isComplete = data.step.endsWith('_done') || data.step === 'complete' || data.step === 'error';\n\n                            if (isComplete) {\n                                \/\/ Completion event - add to log with duration\n                                const logEntry = document.createElement('div');\n                                logEntry.className = 'chat-progress__entry';\n                                let duration = '';\n                                if (data.ms !== null) {\n                                    duration = '<span class=\"chat-progress__duration\">' + data.ms + 'ms<\/span>';\n                                }\n                                logEntry.innerHTML = '<span class=\"chat-progress__time\">' + data.ts + '<\/span>' +\n      ... [TRUNCATED-fc4effe6744f9d00]",
        "new_string": "<script src=\"\/js\/chat.js\"><\/script>\n<script>\ninitChatForm('<?= $session['uuid'] ?? '' ?>', '<?= $csrfToken ?>');\ninitConfigEditors('<?= $csrfToken ?>');\n<\/script>"
    }
}

Response

{
    "tool_response": {
        "filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/View\/chat\/index.php",
        "oldString": "<script>\nconst sidebar = document.getElementById('sidebar');\nconst overlay = document.getElementById('overlay');\nconst toggle = document.getElementById('toggle');\nconst messages = document.getElementById('messages');\nconst form = document.getElementById('chatForm');\nconst sendBtn = document.getElementById('sendBtn');\nconst html = document.documentElement;\nconst configPanel = document.getElementById('configPanel');\nconst configPanelToggle = document.getElementById('configPanelToggle');\nconst configPanelClose = document.getElementById('configPanelClose');\n\n\/\/ ========== THEME ==========\nconst savedTheme = localStorage.getItem('chat-theme') || 'light';\nhtml.setAttribute('data-theme', savedTheme);\nupdateThemeUI();\n\nfunction updateThemeUI() {\n    const theme = html.getAttribute('data-theme');\n    const icon = document.getElementById('configThemeIcon');\n    const text = document.getElementById('configThemeText');\n    if (icon) icon.textContent = theme === 'dark' ? '☀' : '☽';\n    if (text) text.textContent = theme === 'dark' ? 'Dark Mode' : 'Light Mode';\n}\n\ndocument.getElementById('configThemeToggle')?.addEventListener('click', () => {\n    const next = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';\n    html.setAttribute('data-theme', next);\n    localStorage.setItem('chat-theme', next);\n    updateThemeUI();\n});\n\n\/\/ ========== SIDEBAR ==========\ntoggle.addEventListener('click', (e) => {\n    e.stopPropagation();\n    sidebar.classList.toggle('chat-sidebar--open');\n    overlay.classList.toggle('chat-overlay--visible');\n});\n\noverlay.addEventListener('click', () => {\n    sidebar.classList.remove('chat-sidebar--open');\n    overlay.classList.remove('chat-overlay--visible');\n});\n\n\/\/ ========== CONFIG PANEL ==========\nconfigPanelToggle.addEventListener('click', () => {\n    configPanel.classList.toggle('config-panel--open');\n    configPanelToggle.setAttribute('aria-expanded', configPanel.classList.contains('config-panel--open'));\n});\n\nconfigPanelClose.addEventListener('click', () => {\n    configPanel.classList.remove('config-panel--open');\n    configPanelToggle.setAttribute('aria-expanded', 'false');\n});\n\n\/\/ ========== SYNC CONFIG PANEL TO HIDDEN INPUTS ==========\nfunction syncToHidden(configId, hiddenId) {\n    const config = document.getElementById(configId);\n    const hidden = document.getElementById(hiddenId);\n    if (config && hidden) {\n        config.addEventListener('change', function() {\n            hidden.value = this.value;\n            \/\/ Visual feedback\n            this.classList.add('is-saving');\n            setTimeout(() => {\n                this.classList.remove('is-saving');\n                this.classList.add('is-saved');\n                setTimeout(() => this.classList.remove('is-saved'), 600);\n            }, 100);\n            \/\/ Save to localStorage\n            localStorage.setItem('chat-' + hiddenId, this.value);\n        });\n        \/\/ Load from localStorage\n        const saved = localStorage.getItem('chat-' + hiddenId);\n        if (saved) {\n            const option = config.querySelector('option[value=\"' + saved + '\"]');\n            if (option) {\n                config.value = saved;\n                hidden.value = saved;\n            }\n        }\n    }\n}\n\nsyncToHidden('configModel', 'hiddenModel');\nsyncToHidden('configContextLimit', 'hiddenContextLimit');\nsyncToHidden('configMaxTokens', 'hiddenMaxTokens');\nsyncToHidden('configSystemPrompt', 'hiddenSystemPrompt');\nsyncToHidden('configStructure', 'hiddenStructure');\nsyncToHidden('configAuthorProfile', 'hiddenAuthorProfile');\n\n\/\/ ========== COLLECTIONS SYNC ==========\nfunction syncCollections() {\n    const container = document.getElementById('hiddenCollections');\n    container.innerHTML = '';\n    document.querySelectorAll('#configCollections input[type=\"checkbox\"]:checked').forEach(cb => {\n        const input = document.createElement('input');\n        input.type = 'hidden';\n        input.name = 'collections[]';\n        input.value = cb.value;\n        container.appendChild(input);\n    });\n}\nsyncCollections();\ndocument.querySelectorAll('#configCollections input[type=\"checkbox\"]').forEach(cb => {\n    cb.addEventListener('change', syncCollections);\n});\n\n\/\/ ========== TEMPERATURE ==========\nconst tempSlider = document.getElementById('configTemperature');\nconst tempValue = document.getElementById('tempValuePanel');\nconst hiddenTemp = document.getElementById('hiddenTemperature');\n\ntempSlider?.addEventListener('input', () => {\n    const val = parseFloat(tempSlider.value).toFixed(1);\n    tempValue.textContent = val;\n    hiddenTemp.value = val;\n    updatePresetHighlight();\n});\n\nfunction updatePresetHighlight() {\n    const currentTemp = parseFloat(tempSlider.value);\n    document.querySelectorAll('.config-panel__preset').forEach(btn => {\n        const btnTemp = parseFloat(btn.dataset.temp);\n        btn.classList.toggle('config-panel__preset--active', Math.abs(btnTemp - currentTemp) < 0.05);\n    });\n}\n\n\/\/ Preset buttons click handler\ndocument.querySelectorAll('.config-panel__preset').forEach(btn => {\n    btn.addEventListener('click', () => {\n        const temp = parseFloat(btn.dataset.temp);\n        const tokens = parseInt(btn.dataset.tokens);\n        tempSlider.value = temp;\n        tempValue.textContent = temp.toFixed(1);\n        hiddenTemp.value = temp;\n        document.getElementById('configMaxTokens').value = tokens;\n        document.getElementById('hiddenMaxTokens').value = tokens;\n        updatePresetHighlight();\n        \/\/ Visual feedback\n        btn.classList.add('is-saving');\n        setTimeout(() => {\n            btn.classList.remove('is-saving');\n            btn.classList.add('is-saved');\n            setTimeout(() => btn.classList.remove('is-saved'), 600);\n        }, 100);\n    });\n});\n\n\/\/ Streaming Form Handler with SSE\nform.addEventListener('submit', async (e) => {\n    e.preventDefault();\n\n    const messageInput = form.querySelector('input[name=\"message\"]');\n    const question = messageInput.value.trim();\n    if (!question) return;\n\n    sendBtn.disabled = true;\n    sendBtn.classList.add('chat-send--loading');\n\n    const messagesInner = document.querySelector('#messages .chat-messages__inner');\n\n    \/\/ Remove welcome if present\n    const welcome = messagesInner.querySelector('.chat-welcome');\n    if (welcome) welcome.remove();\n\n    \/\/ Add user message immediately\n    const userMsg = document.createElement('div');\n    userMsg.className = 'chat-msg chat-msg--user';\n    userMsg.innerHTML = '<div class=\"chat-msg__content\">' + escapeHtml(question) + '<\/div>';\n    messagesInner.appendChild(userMsg);\n\n    \/\/ Add progress container\n    const progressContainer = document.createElement('div');\n    progressContainer.className = 'chat-progress';\n    progressContainer.innerHTML = '<div class=\"chat-progress__header\"><span class=\"chat-progress__spinner\"><\/span><span class=\"chat-progress__current\">Starte...<\/span><\/div><div class=\"chat-progress__log\"><\/div>';\n    messagesInner.appendChild(progressContainer);\n    const progressHeader = progressContainer.querySelector('.chat-progress__current');\n    const progressLog = progressContainer.querySelector('.chat-progress__log');\n\n    messages.scrollTop = messages.scrollHeight;\n\n    \/\/ Collect form data\n    const formData = new FormData(form);\n    formData.append('_csrf_token', '<?= $csrfToken ?>');\n\n    \/\/ Use fetch with streaming\n    try {\n        const response = await fetch('\/chat\/<?= $session['uuid'] ?? '' ?>\/message\/stream', {\n            method: 'POST',\n            body: formData,\n            headers: {\n                'Accept': 'text\/event-stream'\n            }\n        });\n\n        if (!response.ok) {\n            throw new Error('HTTP ' + response.status);\n        }\n\n        if (!response.body) {\n            throw new Error('ReadableStream not supported');\n        }\n\n        const reader = response.body.getReader();\n        const decoder = new TextDecoder();\n        let buffer = '';\n        let chunkCount = 0;\n\n        console.log('SSE: Starting to read stream...');\n\n        while (true) {\n            const {value, done} = await reader.read();\n            if (done) {\n                console.log('SSE: Stream ended after', chunkCount, 'chunks');\n                break;\n            }\n\n            chunkCount++;\n            const chunk = decoder.decode(value, {stream: true});\n            console.log('SSE CHUNK #' + chunkCount + ' (' + chunk.length + ' bytes):', chunk.substring(0, 100));\n\n            buffer += chunk;\n            const lines = buffer.split('\\n');\n            buffer = lines.pop() || '';\n\n            for (const line of lines) {\n                if (line.trim() === '' || line.startsWith(':')) continue;\n                console.log('SSE LINE:', line);\n                if (line.startsWith('event: ')) {\n                    const eventType = line.substring(7);\n                    continue;\n                }\n                if (line.startsWith('data: ')) {\n                    try {\n                        const data = JSON.parse(line.substring(6));\n                        console.log('SSE DATA:', data);\n\n                        if (data.ts && data.msg && data.step) {\n                            \/\/ Check if this is a start event or completion event\n                            const isComplete = data.step.endsWith('_done') || data.step === 'complete' || data.step === 'error';\n\n                            if (isComplete) {\n                                \/\/ Completion event - add to log with duration\n                                const logEntry = document.createElement('div');\n                                logEntry.className = 'chat-progress__entry';\n                                let duration = '';\n                                if (data.ms !== null) {\n                                    duration = '<span class=\"chat-progress__duration\">' + data.ms + 'ms<\/span>';\n                                }\n                                logEntry.innerHTML = '<span class=\"chat-progress__time\">' + data.ts + '<\/span>' +\n      ... [TRUNCATED-fc4effe6744f9d00]",
        "newString": "<script src=\"\/js\/chat.js\"><\/script>\n<script>\ninitChatForm('<?= $session['uuid'] ?? '' ?>', '<?= $csrfToken ?>');\ninitConfigEditors('<?= $csrfToken ?>');\n<\/script>",
        "originalFile": "<?php\n\/**\n * Chat - Redesign\n * Clean, minimalist, professional\n *\/\n$currentModel = $session['model'] ?? $defaultModel ?? 'claude-opus-4-5-20251101';\n$selected = json_decode($session['collections'] ?? '[\"documents\"]', true) ?: ['documents'];\n$currentLimit = (int) ($session['context_limit'] ?? 5);\n$currentProfileId = (int) ($session['author_profile_id'] ?? 0);\n$currentPromptId = (int) ($session['system_prompt_id'] ?? 1);\n$currentTemperature = (float) ($session['temperature'] ?? 0.5);\n$currentMaxTokens = (int) ($session['max_tokens'] ?? 4096);\n?>\n<!DOCTYPE html>\n<html lang=\"de\" data-theme=\"light\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title><?= htmlspecialchars($session['title'] ?? 'KI-Chat') ?> - Campus<\/title>\n    <link rel=\"icon\" type=\"image\/png\" href=\"https:\/\/campus-am-see.de\/wp-content\/uploads\/menu-logo.png\">\n    <link rel=\"stylesheet\" href=\"\/css\/chat-redesign.css\">\n    <script src=\"\/js\/htmx.min.js\"><\/script>\n<\/head>\n<body>\n<div class=\"chat-layout\">\n    <!-- Sidebar -->\n    <aside class=\"chat-sidebar\" id=\"sidebar\">\n        <div class=\"chat-sidebar__header\">\n            <a href=\"\/chat\" class=\"chat-sidebar__new\">+ Neuer Chat<\/a>\n            <button class=\"chat-sidebar__delete-all\" hx-delete=\"\/chat\" hx-headers='{\"X-CSRF-TOKEN\": \"<?= $csrfToken ?>\"}' hx-confirm=\"Alle <?= count($sessions ?? []) ?> Chats löschen?\" title=\"Alle löschen\">&times; Alle<\/button>\n        <\/div>\n        <div class=\"chat-sidebar__list\" id=\"session-list\">\n            <?php foreach ($sessions ?? [] as $s):\n                $totalTokens = (int) ($s['total_input_tokens'] ?? 0) + (int) ($s['total_output_tokens'] ?? 0);\n                $totalCost = ((int) ($s['total_input_tokens'] ?? 0) * 0.000015) + ((int) ($s['total_output_tokens'] ?? 0) * 0.000075);\n                $isOllama = str_starts_with($s['model'] ?? '', 'ollama:');\n                $createdAt = $s['created_at'] ?? null;\n                $dateStr = $createdAt ? (new DateTime($createdAt))->format('d.m. H:i') : '';\n                ?>\n            <a href=\"\/chat\/<?= $s['uuid'] ?>\"\n               class=\"chat-session <?= ($session['uuid'] ?? '') === $s['uuid'] ? 'chat-session--active' : '' ?>\"\n               data-uuid=\"<?= $s['uuid'] ?>\">\n                <div class=\"chat-session__title\" id=\"title-<?= $s['uuid'] ?>\"><?= htmlspecialchars($s['title'] ?? 'Neuer Chat') ?><\/div>\n                <div class=\"chat-session__meta\">\n                    <span class=\"chat-session__date\"><?= $dateStr ?><\/span>\n                    <span><?= $s['message_count'] ?? 0 ?> Nachr.<\/span>\n                    <?php if (!$isOllama && $totalTokens > 0): ?>\n                    <span class=\"chat-session__cost\">~$<?= number_format($totalCost, 2) ?><\/span>\n                    <?php elseif ($isOllama): ?>\n                    <span class=\"chat-session__local\">lokal<\/span>\n                    <?php endif; ?>\n                <\/div>\n                <div class=\"chat-session__actions\">\n                    <button class=\"chat-session__edit\" onclick=\"event.preventDefault(); event.stopPropagation(); editTitle('<?= $s['uuid'] ?>');\" title=\"Bearbeiten\">&#9998;<\/button>\n                    <button class=\"chat-session__delete\" hx-delete=\"\/chat\/<?= $s['uuid'] ?>\" hx-headers='{\"X-CSRF-TOKEN\": \"<?= $csrfToken ?>\"}' hx-confirm=\"Session löschen?\" onclick=\"event.preventDefault(); event.stopPropagation();\">&times;<\/button>\n                <\/div>\n            <\/a>\n            <?php endforeach; ?>\n            <?php if (empty($sessions)): ?>\n            <div class=\"chat-session chat-session--empty\">Keine Sessions<\/div>\n            <?php endif; ?>\n        <\/div>\n    <\/aside>\n\n    <!-- Config Panel (50% Screen) -->\n    <aside class=\"config-panel\" id=\"configPanel\">\n        <div class=\"config-panel__header\">\n            <span class=\"config-panel__title\">Einstellungen<\/span>\n            <button type=\"button\" class=\"config-panel__close\" id=\"configPanelClose\" aria-label=\"Panel schliessen\">&times;<\/button>\n        <\/div>\n\n        <div class=\"config-panel__body\">\n            <!-- Modell -->\n            <div class=\"config-panel__group\">\n                <label for=\"configModel\" class=\"config-panel__label\">Modell<\/label>\n                <select id=\"configModel\" class=\"config-panel__select\" aria-label=\"Modell waehlen\">\n                    <optgroup label=\"Anthropic\">\n                        <?php foreach ($models ?? [] as $modelId => $modelLabel): ?>\n                        <?php if (!str_starts_with($modelId, 'ollama:')): ?>\n                        <option value=\"<?= htmlspecialchars($modelId) ?>\" <?= $currentModel === $modelId ? 'selected' : '' ?>><?= htmlspecialchars($modelLabel) ?><\/option>\n                        <?php endif; ?>\n                        <?php endforeach; ?>\n                    <\/optgroup>\n                    <optgroup label=\"Ollama (lokal)\">\n                        <?php foreach ($models ?? [] as $modelId => $modelLabel): ?>\n                        <?php if (str_starts_with($modelId, 'ollama:')): ?>\n                        <option value=\"<?= htmlspecialchars($modelId) ?>\" <?= $currentModel === $modelId ? 'selected' : '' ?>><?= htmlspecialchars($modelLabel) ?><\/option>\n                        <?php endif; ?>\n                        <?php endforeach; ?>\n                    <\/optgroup>\n                <\/select>\n            <\/div>\n\n            <!-- Quellen -->\n            <div class=\"config-panel__group\">\n                <label for=\"configContextLimit\" class=\"config-panel__label\">Quellen<\/label>\n                <select id=\"configContextLimit\" class=\"config-panel__select\" aria-label=\"Anzahl Quellen\">\n                    <option value=\"3\" <?= $currentLimit === 3 ? 'selected' : '' ?>>3 Quellen<\/option>\n                    <option value=\"5\" <?= $currentLimit === 5 ? 'selected' : '' ?>>5 Quellen<\/option>\n                    <option value=\"10\" <?= $currentLimit === 10 ? 'selected' : '' ?>>10 Quellen<\/option>\n                    <option value=\"15\" <?= $currentLimit === 15 ? 'selected' : '' ?>>15 Quellen<\/option>\n                <\/select>\n            <\/div>\n\n            <!-- Collections -->\n            <div class=\"config-panel__group\">\n                <label class=\"config-panel__label\">Sammlungen<\/label>\n                <div class=\"config-panel__collections\" id=\"configCollections\">\n                    <?php foreach ($collections ?? [] as $col):\n                        $colId = $col['collection_id'];\n                        $isSelected = in_array($colId, $selected, true);\n                        $points = (int) ($col['points_count'] ?? 0);\n                        ?>\n                    <label class=\"config-panel__checkbox\">\n                        <input type=\"checkbox\" name=\"collections[]\" value=\"<?= htmlspecialchars($colId) ?>\" <?= $isSelected ? 'checked' : '' ?>>\n                        <?= htmlspecialchars($col['display_name']) ?> <span class=\"config-panel__count\">(<?= number_format($points) ?>)<\/span>\n                    <\/label>\n                    <?php endforeach; ?>\n                <\/div>\n            <\/div>\n\n            <!-- Temperatur & Tokens -->\n            <div class=\"config-panel__group\">\n                <label class=\"config-panel__label\">Temperatur: <span id=\"tempValuePanel\"><?= number_format($currentTemperature, 1) ?><\/span><\/label>\n                <input type=\"range\" id=\"configTemperature\" class=\"config-panel__slider\" min=\"0\" max=\"1\" step=\"0.1\" value=\"<?= $currentTemperature ?>\">\n                <div class=\"config-panel__presets\">\n                    <button type=\"button\" class=\"config-panel__preset<?= $currentTemperature == 0.3 ? ' config-panel__preset--active' : '' ?>\" data-temp=\"0.3\" data-tokens=\"2048\">Präzise<\/button>\n                    <button type=\"button\" class=\"config-panel__preset<?= $currentTemperature == 0.5 ? ' config-panel__preset--active' : '' ?>\" data-temp=\"0.5\" data-tokens=\"4096\">Ausgewogen<\/button>\n                    <button type=\"button\" class=\"config-panel__preset<?= $currentTemperature == 0.9 ? ' config-panel__preset--active' : '' ?>\" data-temp=\"0.9\" data-tokens=\"4096\">Kreativ<\/button>\n                <\/div>\n            <\/div>\n\n            <!-- Max Tokens -->\n            <div class=\"config-panel__group\">\n                <label for=\"configMaxTokens\" class=\"config-panel__label\">Max Tokens<\/label>\n                <select id=\"configMaxTokens\" class=\"config-panel__select\">\n                    <option value=\"1024\" <?= $currentMaxTokens === 1024 ? 'selected' : '' ?>>1024<\/option>\n                    <option value=\"2048\" <?= $currentMaxTokens === 2048 ? 'selected' : '' ?>>2048<\/option>\n                    <option value=\"4096\" <?= $currentMaxTokens === 4096 ? 'selected' : '' ?>>4096<\/option>\n                    <option value=\"8192\" <?= $currentMaxTokens === 8192 ? 'selected' : '' ?>>8192<\/option>\n                <\/select>\n            <\/div>\n\n            <!-- Qualitätsprüfung -->\n            <div class=\"config-panel__group\">\n                <label class=\"config-panel__checkbox config-panel__checkbox--large\">\n                    <input type=\"checkbox\" id=\"configQualityCheck\" name=\"quality_check\" value=\"1\">\n                    Qualitätsprüfung (LLM-Validierung)\n                <\/label>\n            <\/div>\n\n            <hr class=\"config-panel__divider\">\n\n            <!-- System Prompt -->\n            <div class=\"config-panel__group\">\n                <label for=\"configSystemPrompt\" class=\"config-panel__label\">System Prompt<\/label>\n                <select id=\"configSystemPrompt\" class=\"config-panel__select\" aria-label=\"System Prompt waehlen\">\n                    <?php foreach ($systemPrompts ?? [] as $prompt): ?>\n                    <option value=\"<?= $prompt['id'] ?>\" <?= $currentPromptId === (int) $prompt['id'] ? 'selected' : '' ?>><?= htmlspecialchars($prompt['name']) ?><\/option>\n                    <?php endforeach; ?>\n                <\/select>\n                <button type=\"button\" class=\"config-panel__toggle\" data-config-type=\"system_prompt\" aria-ex... [TRUNCATED-05f85715aca4faba]",
        "structuredPatch": [
            {
                "oldStart": 348,
                "oldLines": 446,
                "newStart": 348,
                "newLines": 10,
                "lines": [
                    "     <\/main>",
                    " <\/div>",
                    " ",
                    "+<script src=\"\/js\/chat.js\"><\/script>",
                    " <script>",
                    "-const sidebar = document.getElementById('sidebar');",
                    "-const overlay = document.getElementById('overlay');",
                    "-const toggle = document.getElementById('toggle');",
                    "-const messages = document.getElementById('messages');",
                    "-const form = document.getElementById('chatForm');",
                    "-const sendBtn = document.getElementById('sendBtn');",
                    "-const html = document.documentElement;",
                    "-const configPanel = document.getElementById('configPanel');",
                    "-const configPanelToggle = document.getElementById('configPanelToggle');",
                    "-const configPanelClose = document.getElementById('configPanelClose');",
                    "-",
                    "-\/\/ ========== THEME ==========",
                    "-const savedTheme = localStorage.getItem('chat-theme') || 'light';",
                    "-html.setAttribute('data-theme', savedTheme);",
                    "-updateThemeUI();",
                    "-",
                    "-function updateThemeUI() {",
                    "-    const theme = html.getAttribute('data-theme');",
                    "-    const icon = document.getElementById('configThemeIcon');",
                    "-    const text = document.getElementById('configThemeText');",
                    "-    if (icon) icon.textContent = theme === 'dark' ? '☀' : '☽';",
                    "-    if (text) text.textContent = theme === 'dark' ? 'Dark Mode' : 'Light Mode';",
                    "-}",
                    "-",
                    "-document.getElementById('configThemeToggle')?.addEventListener('click', () => {",
                    "-    const next = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';",
                    "-    html.setAttribute('data-theme', next);",
                    "-    localStorage.setItem('chat-theme', next);",
                    "-    updateThemeUI();",
                    "-});",
                    "-",
                    "-\/\/ ========== SIDEBAR ==========",
                    "-toggle.addEventListener('click', (e) => {",
                    "-    e.stopPropagation();",
                    "-    sidebar.classList.toggle('chat-sidebar--open');",
                    "-    overlay.classList.toggle('chat-overlay--visible');",
                    "-});",
                    "-",
                    "-overlay.addEventListener('click', () => {",
                    "-    sidebar.classList.remove('chat-sidebar--open');",
                    "-    overlay.classList.remove('chat-overlay--visible');",
                    "-});",
                    "-",
                    "-\/\/ ========== CONFIG PANEL ==========",
                    "-configPanelToggle.addEventListener('click', () => {",
                    "-    configPanel.classList.toggle('config-panel--open');",
                    "-    configPanelToggle.setAttribute('aria-expanded', configPanel.classList.contains('config-panel--open'));",
                    "-});",
                    "-",
                    "-configPanelClose.addEventListener('click', () => {",
                    "-    configPanel.classList.remove('config-panel--open');",
                    "-    configPanelToggle.setAttribute('aria-expanded', 'false');",
                    "-});",
                    "-",
                    "-\/\/ ========== SYNC CONFIG PANEL TO HIDDEN INPUTS ==========",
                    "-function syncToHidden(configId, hiddenId) {",
                    "-    const config = document.getElementById(configId);",
                    "-    const hidden = document.getElementById(hiddenId);",
                    "-    if (config && hidden) {",
                    "-        config.addEventListener('change', function() {",
                    "-            hidden.value = this.value;",
                    "-            \/\/ Visual feedback",
                    "-            this.classList.add('is-saving');",
                    "-            setTimeout(() => {",
                    "-                this.classList.remove('is-saving');",
                    "-                this.classList.add('is-saved');",
                    "-                setTimeout(() => this.classList.remove('is-saved'), 600);",
                    "-            }, 100);",
                    "-            \/\/ Save to localStorage",
                    "-            localStorage.setItem('chat-' + hiddenId, this.value);",
                    "-        });",
                    "-        \/\/ Load from localStorage",
                    "-        const saved = localStorage.getItem('chat-' + hiddenId);",
                    "-        if (saved) {",
                    "-            const option = config.querySelector('option[value=\"' + saved + '\"]');",
                    "-            if (option) {",
                    "-                config.value = saved;",
                    "-                hidden.value = saved;",
                    "-            }",
                    "-        }",
                    "-    }",
                    "-}",
                    "-",
                    "-syncToHidden('configModel', 'hiddenModel');",
                    "-syncToHidden('configContextLimit', 'hiddenContextLimit');",
                    "-syncToHidden('configMaxTokens', 'hiddenMaxTokens');",
                    "-syncToHidden('configSystemPrompt', 'hiddenSystemPrompt');",
                    "-syncToHidden('configStructure', 'hiddenStructure');",
                    "-syncToHidden('configAuthorProfile', 'hiddenAuthorProfile');",
                    "-",
                    "-\/\/ ========== COLLECTIONS SYNC ==========",
                    "-function syncCollections() {",
                    "-    const container = document.getElementById('hiddenCollections');",
                    "-    container.innerHTML = '';",
                    "-    document.querySelectorAll('#configCollections input[type=\"checkbox\"]:checked').forEach(cb => {",
                    "-        const input = document.createElement('input');",
                    "-        input.type = 'hidden';",
                    "-        input.name = 'collections[]';",
                    "-        input.value = cb.value;",
                    "-        container.appendChild(input);",
                    "-    });",
                    "-}",
                    "-syncCollections();",
                    "-document.querySelectorAll('#configCollections input[type=\"checkbox\"]').forEach(cb => {",
                    "-    cb.addEventListener('change', syncCollections);",
                    "-});",
                    "-",
                    "-\/\/ ========== TEMPERATURE ==========",
                    "-const tempSlider = document.getElementById('configTemperature');",
                    "-const tempValue = document.getElementById('tempValuePanel');",
                    "-const hiddenTemp = document.getElementById('hiddenTemperature');",
                    "-",
                    "-tempSlider?.addEventListener('input', () => {",
                    "-    const val = parseFloat(tempSlider.value).toFixed(1);",
                    "-    tempValue.textContent = val;",
                    "-    hiddenTemp.value = val;",
                    "-    updatePresetHighlight();",
                    "-});",
                    "-",
                    "-function updatePresetHighlight() {",
                    "-    const currentTemp = parseFloat(tempSlider.value);",
                    "-    document.querySelectorAll('.config-panel__preset').forEach(btn => {",
                    "-        const btnTemp = parseFloat(btn.dataset.temp);",
                    "-        btn.classList.toggle('config-panel__preset--active', Math.abs(btnTemp - currentTemp) < 0.05);",
                    "-    });",
                    "-}",
                    "-",
                    "-\/\/ Preset buttons click handler",
                    "-document.querySelectorAll('.config-panel__preset').forEach(btn => {",
                    "-    btn.addEventListener('click', () => {",
                    "-        const temp = parseFloat(btn.dataset.temp);",
                    "-        const tokens = parseInt(btn.dataset.tokens);",
                    "-        tempSlider.value = temp;",
                    "-        tempValue.textContent = temp.toFixed(1);",
                    "-        hiddenTemp.value = temp;",
                    "-        document.getElementById('configMaxTokens').value = tokens;",
                    "-        document.getElementById('hiddenMaxTokens').value = tokens;",
                    "-        updatePresetHighlight();",
                    "-        \/\/ Visual feedback",
                    "-        btn.classList.add('is-saving');",
                    "-        setTimeout(() => {",
                    "-            btn.classList.remove('is-saving');",
                    "-            btn.classList.add('is-saved');",
                    "-            setTimeout(() => btn.classList.remove('is-saved'), 600);",
                    "-        }, 100);",
                    "-    });",
                    "-});",
                    "-",
                    "-\/\/ Streaming Form Handler with SSE",
                    "-form.addEventListener('submit', async (e) => {",
                    "-    e.preventDefault();",
                    "-",
                    "-    const messageInput = form.querySelector('input[name=\"message\"]');",
                    "-    const question = messageInput.value.trim();",
                    "-    if (!question) return;",
                    "-",
                    "-    sendBtn.disabled = true;",
                    "-    sendBtn.classList.add('chat-send--loading');",
                    "-",
                    "-    const messagesInner = document.querySelector('#messages .chat-messages__inner');",
                    "-",
                    "-    \/\/ Remove welcome if present",
                    "-    const welcome = messagesInner.querySelector('.chat-welcome');",
                    "-    if (welcome) welcome.remove();",
                    "-",
                    "-    \/\/ Add user message immediately",
                    "-    const userMsg = document.createElement('div');",
                    "-    userMsg.className = 'chat-msg chat-msg--user';",
                    "-    userMsg.innerHTML = '<div class=\"chat-msg__content\">' + escapeHtml(question) + '<\/div>';",
                    "-    messagesInner.appendChild(userMsg);",
                    "-",
                    "-    \/\/ Add progress container",
                    "-    const progressContainer = document.createElement('div');",
                    "-    progressContainer.className = 'chat-progress';",
                    "-    progressContainer.innerHTML = '<div class=\"chat-progress__header\"><span class=\"chat-progress__spinner\"><\/span><span class=\"chat-progress__current\">Starte...<\/span><\/div><div class=\"chat-progress__log\"><\/div>';",
                    "-    messagesInner.appendChild(progressContainer);",
                    "-    const progressHeader = progressContainer.querySelector('.chat-progress__current');",
                    "-    const progressLog = progressContainer.querySelector('.chat-progress__log');",
                    "-",
                    "-    messages.scrollTop = messages.scrollHeight;",
                    "-",
                    "-    \/\/ Collect form data",
                    "-    const formData = new FormData(form);",
                    "-    formData.append('_csrf_token', '<?= $csrfToken ?>');",
                    "-",
                    "-    \/\/ Use fetch with streaming",
                    "-    try {",
                    "-        const response = await fetch('\/chat\/<?= $session['uuid'] ?? '' ?>\/message\/stream', {",
                    "-            method: 'POST',",
                    "-            body: formData,",
                    "-            headers: {",
                    "-                'Accept': 'text\/event-stream'",
                    "-            }",
                    "-        });",
                    "-",
                    "-        if (!response.ok) {",
                    "-            throw new Error('HTTP ' + response.status);",
                    "-        }",
                    "-",
                    "-        if (!response.body) {",
                    "-            throw new Error('ReadableStream not supported');",
                    "-        }",
                    "-",
                    "-        const reader = response.body.getReader();",
                    "-        const decoder = new TextDecoder();",
                    "-        let buffer = '';",
                    "-        let chunkCount = 0;",
                    "-",
                    "-        console.log('SSE: Starting to read stream...');",
                    "-",
                    "-        while (true) {",
                    "-            const {value, done} = await reader.read();",
                    "-            if (done) {",
                    "-                console.log('SSE: Stream ended after', chunkCount, 'chunks');",
                    "-                break;",
                    "-            }",
                    "-",
                    "-            chunkCount++;",
                    "-            const chunk = decoder.decode(value, {stream: true});",
                    "-            console.log('SSE CHUNK #' + chunkCount + ' (' + chunk.length + ' bytes):', chunk.substring(0, 100));",
                    "-",
                    "-            buffer += chunk;",
                    "-            const lines = buffer.split('\\n');",
                    "-            buffer = lines.pop() || '';",
                    "-",
                    "-            for (const line of lines) {",
                    "-                if (line.trim() === '' || line.startsWith(':')) continue;",
                    "-                console.log('SSE LINE:', line);",
                    "-                if (line.startsWith('event: ')) {",
                    "-                    const eventType = line.substring(7);",
                    "-                    continue;",
                    "-                }",
                    "-                if (line.startsWith('data: ')) {",
                    "-                    try {",
                    "-                        const data = JSON.parse(line.substring(6));",
                    "-                        console.log('SSE DATA:', data);",
                    "-",
                    "-                        if (data.ts && data.msg && data.step) {",
                    "-                            \/\/ Check if this is a start event or completion event",
                    "-                            const isComplete = data.step.endsWith('_done') || data.step === 'complete' || data.step === 'error';",
                    "-",
                    "-                            if (isComplete) {",
                    "-                                \/\/ Completion event - add to log with duration",
                    "-                                const logEntry = document.createElement('div');",
                    "-                                logEntry.className = 'chat-progress__entry';",
                    "-                                let duration = '';",
                    "-                                if (data.ms !== null) {",
                    "-                                    duration = '<span class=\"chat-progress__duration\">' + data.ms + 'ms<\/span>';",
                    "-                                }",
                    "-                                logEntry.innerHTML = '<span class=\"chat-progress__time\">' + data.ts + '<\/span>' +",
                    "-                                    '<span class=\"chat-progress__msg\">' + escapeHtml(data.msg) + '<\/span>' + duration;",
                    "-                                progressLog.appendChild(logEntry);",
                    "-                            } else {",
                    "-                                \/\/ Start event - update header with current step",
                    "-                                progressHeader.textContent = data.msg;",
                    "-                            }",
                    "-                            messages.scrollTop = messages.scrollHeight;",
                    "-                        }",
                    "-",
                    "-                        if (data.html) {",
                    "-                            \/\/ Done event - keep progress, add response after",
                    "-                            userMsg.remove();",
                    "-                            \/\/ Mark progress as complete",
                    "-                            progressContainer.classList.add('chat-progress--done');",
                    "-                            const spinner = progressContainer.querySelector('.chat-progress__spinner');",
                    "-                            if (spinner) spinner.remove();",
                    "-                            progressHeader.textContent = 'Abgeschlossen';",
                    "-                            \/\/ Add response after progress",
                    "-                            messagesInner.insertAdjacentHTML('beforeend', data.html);",
                    "-                            messages.scrollTop = messages.scrollHeight;",
                    "-                        }",
                    "-",
                    "-                        if (data.error) {",
                    "-                            \/\/ Error event",
                    "-                            progressContainer.innerHTML = '<div class=\"chat-error\">' + escapeHtml(data.error) + '<\/div>';",
                    "-                        }",
                    "-                    } catch (e) {",
                    "-                        console.error('Parse error:', e, line);",
                    "-                    }",
                    "-                }",
                    "-            }",
                    "-        }",
                    "-    } catch (err) {",
                    "-        progressContainer.innerHTML = '<div class=\"chat-error\">Verbindungsfehler: ' + escapeHtml(err.message) + '<\/div>';",
                    "-    }",
                    "-",
                    "-    messageInput.value = '';",
                    "-    messageInput.focus();",
                    "-    sendBtn.disabled = false;",
                    "-    sendBtn.classList.remove('chat-send--loading');",
                    "-    htmx.ajax('GET', '\/chat\/sessions?current=<?= $session['uuid'] ?? '' ?>', '#session-list');",
                    "-});",
                    "-",
                    "-function escapeHtml(text) {",
                    "-    const div = document.createElement('div');",
                    "-    div.textContent = text;",
                    "-    return div.innerHTML;",
                    "-}",
                    "-",
                    "-\/\/ Scroll to bottom on load",
                    "-messages.scrollTop = messages.scrollHeight;",
                    "-",
                    "-\/\/ Close sidebar on mobile when clicking main",
                    "-document.querySelector('.chat-main').addEventListener('click', (e) => {",
                    "-    if (window.innerWidth <= 768 && !e.target.closest('.chat-toggle')) {",
                    "-        sidebar.classList.remove('chat-sidebar--open');",
                    "-        overlay.classList.remove('chat-overlay--visible');",
                    "-    }",
                    "-});",
                    "-",
                    "-\/\/ Config Panel toggle editors",
                    "-document.querySelectorAll('.config-panel__toggle').forEach(btn => {",
                    "-    btn.addEventListener('click', async function() {",
                    "-        const configType = this.dataset.configType;",
                    "-        const editorId = this.getAttribute('aria-controls');",
                    "-        const editor = document.getElementById(editorId);",
                    "-        const isOpen = !editor.classList.contains('config-panel__editor--hidden');",
                    "-",
                    "-        if (isOpen) {",
                    "-            editor.classList.add('config-panel__editor--hidden');",
                    "-            editor.setAttribute('aria-hidden', 'true');",
                    "-            this.setAttribute('aria-expanded', 'false');",
                    "-        } else {",
                    "-            \/\/ Load content from API",
                    "-            const selectId = configType === 'system_prompt' ? 'configSystemPrompt' :",
                    "-                            configType === 'structure' ? 'configStructure' : 'configAuthorProfile';",
                    "-            const selectedId = document.getElementById(selectId).value;",
                    "-",
                    "-            if (selectedId && selectedId !== '0') {",
                    "-                try {",
                    "-                    const resp = await fetch('\/api\/v1\/config\/' + selectedId);",
                    "-                    const data = await resp.json();",
                    "-                    if (data.content) {",
                    "-                        const textareaId = configType === 'system_prompt' ? 'systemPromptContent' :",
                    "-                                          configType === 'structure' ? 'structureContent' : 'authorProfileContent';",
                    "-                        const versionId = configType === 'system_prompt' ? 'systemPromptVersion' :",
                    "-                                         configType === 'structure' ? 'structureVersion' : 'authorProfileVersion';",
                    "-                        \/\/ Format JSON nicely",
                    "-                        let formatted = data.content;",
                    "-                        try {",
                    "-                            const parsed = JSON.parse(data.content);",
                    "-                            formatted = JSON.stringify(parsed, null, 2);",
                    "-                        } catch (e) {}",
                    "-                        document.getElementById(textareaId).value = formatted;",
                    "-                        document.getElementById(versionId).textContent = 'v' + (data.version || '1.0');",
                    "-                    }",
                    "-                } catch (e) {",
                    "-                    console.error('Config load error:', e);",
                    "-                }",
                    "-            }",
                    "-",
                    "-            editor.classList.remove('config-panel__editor--hidden');",
                    "-            editor.setAttribute('aria-hidden', 'false');",
                    "-            this.setAttribute('aria-expanded', 'true');",
                    "-        }",
                    "-    });",
                    "-});",
                    "-",
                    "-\/\/ Config Panel save",
                    "-document.querySelectorAll('.config-panel__save').forEach(btn => {",
                    "-    btn.addEventListener('click', async function() {",
                    "-        const configType = this.dataset.configType;",
                    "-        const selectId = configType === 'system_prompt' ? 'configSystemPrompt' :",
                    "-                        configType === 'structure' ? 'configStructure' : 'configAuthorProfile';",
                    "-        const textareaId = configType === 'system_prompt' ? 'systemPromptContent' :",
                    "-                          configType === 'structure' ? 'structureContent' : 'authorProfileContent';",
                    "-        const versionId = configType === 'system_prompt' ? 'systemPromptVersion' :",
                    "-                         configType === 'structure' ? 'structureVersion' : 'authorProfileVersion';",
                    "-",
                    "-        const selectedId = document.getElementById(selectId).value;",
                    "-        const content = document.getElementById(textareaId).value;",
                    "-",
                    "-        if (!selectedId || selectedId === '0') {",
                    "-            alert('Bitte waehle zuerst eine Konfiguration aus.');",
                    "-            return;",
                    "-        }",
                    "-",
                    "-        try {",
                    "-            const formData = new FormData();",
                    "-            formData.append('content', content);",
                    "-            formData.append('_csrf_token', '<?= $csrfToken ?>');",
                    "-",
                    "-            const resp = await fetch('\/api\/v1\/config\/' + selectedId, {",
                    "-                method: 'POST',",
                    "-                body: formData",
                    "-            });",
                    "-            const data = await resp.json();",
                    "-",
                    "-            if (data.success) {",
                    "-                document.getElementById(versionId).textContent = 'v' + data.version;",
                    "-                this.textContent = 'Gespeichert!';",
                    "-                setTimeout(() => { this.textContent = 'Speichern'; }, 2000);",
                    "-            } else {",
                    "-                alert('Fehler: ' + (data.error || 'Unbekannt'));",
                    "-            }",
                    "-        } catch (e) {",
                    "-            console.error('Config save error:', e);",
                    "-            alert('Speichern fehlgeschlagen');",
                    "-        }",
                    "-    });",
                    "-});",
                    "-",
                    "-\/\/ Inline Title Edit",
                    "-function editTitle(uuid) {",
                    "-    const titleEl = document.getElementById('title-' + uuid);",
                    "-    if (!titleEl) return;",
                    "-    const currentTitle = titleEl.textContent;",
                    "-    const input = document.createElement('input');",
                    "-    input.type = 'text';",
                    "-    input.value = currentTitle;",
                    "-    input.className = 'chat-session__input';",
                    "-    input.maxLength = 100;",
                    "-    titleEl.innerHTML = '';",
                    "-    titleEl.appendChild(input);",
                    "-    input.focus();",
                    "-    input.select();",
                    "-",
                    "-    function save() {",
                    "-        const newTitle = input.value.trim() || 'Neuer Chat';",
                    "-        titleEl.textContent = newTitle;",
                    "-        fetch('\/chat\/' + uuid + '\/title', {",
                    "-            method: 'POST',",
                    "-            headers: { 'Content-Type': 'application\/x-www-form-urlencoded' },",
                    "-            body: 'title=' + encodeURIComponent(newTitle) + '&_csrf_token=<?= $csrfToken ?>'",
                    "-        }).then(r => r.text()).then(html => {",
                    "-            titleEl.textContent = html || newTitle;",
                    "-            const pageTitle = document.getElementById('page-title');",
                    "-            if (pageTitle && window.location.pathname.includes(uuid)) {",
                    "-                pageTitle.textContent = html || newTitle;",
                    "-            }",
                    "-        });",
                    "-    }",
                    "-",
                    "-    input.addEventListener('blur', save);",
                    "-    input.addEventListener('keydown', (e) => {",
                    "-        if (e.key === 'Enter') { e.preventDefault(); input.blur(); }",
                    "-        if (e.key === 'Escape') { titleEl.textContent = currentTitle; }",
                    "-    });",
                    "-}",
                    "+initChatForm('<?= $session['uuid'] ?? '' ?>', '<?= $csrfToken ?>');",
                    "+initConfigEditors('<?= $csrfToken ?>');",
                    " <\/script>",
                    " <\/body>",
                    " <\/html>"
                ]
            }
        ],
        "userModified": false,
        "replaceAll": false
    }
}
← Vorheriger Zur Liste Nächster →