// ── Session Edit Page ── let _sessionAvatarEditor = null; let _sessionContextMde = null; registerPage('session', { async init(params) { const sessionId = params.id; const isNew = !sessionId; let session = null; if (!isNew) { session = await apiGetSession(sessionId); if (!session) return navigateTo('dashboard'); } const titleEl = document.getElementById('sessionPageTitle'); const nameEl = document.getElementById('sessionName'); const descEl = document.getElementById('sessionDescription'); const contextEl = document.getElementById('sessionContext'); const workspaceEl = document.getElementById('sessionWorkspace'); const agentSelect = document.getElementById('agentSelect'); const agentSelectCard = document.getElementById('agentSelectCard'); const preparerAgentSelect = document.getElementById('preparerAgentSelect'); const preparerAgentSelectCard = document.getElementById('preparerAgentSelectCard'); const reviewAgentSelect = document.getElementById('reviewAgentSelect'); const reviewAgentSelectCard = document.getElementById('reviewAgentSelectCard'); const reviewIterationsRow = document.getElementById('reviewIterationsRow'); const maxReviewIterationsEl = document.getElementById('maxReviewIterations'); const finalizerAgentSelect = document.getElementById('finalizerAgentSelect'); const finalizerAgentSelectCard = document.getElementById('finalizerAgentSelectCard'); const imagePreview = document.getElementById('sessionImagePreview'); const saveBtn = document.getElementById('sessionSaveBtn'); const cancelBtn = document.getElementById('sessionCancelBtn'); const deleteBtn = document.getElementById('sessionDeleteBtn'); const commandsList = document.getElementById('customCommandsList'); const addCommandBtn = document.getElementById('addCommandBtn'); const more = document.querySelector('.more'); // Avatar Editor initialisieren _sessionAvatarEditor = AvatarEditor.init({ previewEl: imagePreview, initialImage: (session && session.image) || null, initialScale: (session && session.avatarScale) || 1, initialOffsetX: (session && session.avatarOffsetX) || 0, initialOffsetY: (session && session.avatarOffsetY) || 0 }); if (session) { titleEl.textContent = session.name || 'Unbenannt'; nameEl.value = session.name || ''; descEl.value = session.description || ''; workspaceEl.value = session.workspace || ''; deleteBtn.hidden = false; // Custom Commands laden loadCommandsIntoUI(commandsList, session.customCommands); } else { titleEl.textContent = 'Neue Session'; deleteBtn.hidden = true; } // EasyMDE für Kontext _sessionContextMde = createMDE(contextEl, { initialValue: (session && session.context) || '', placeholder: 'Zusätzlicher Kontext für den System-Prompt (optional)' }); // Befehl hinzufügen addCommandBtn.addEventListener('click', () => createCommandRow(commandsList)); // Telegram-Section nur für existierende Sessions if (!isNew) { initSessionTelegram(sessionId, session); } // Agents laden + Skills parallel const agents = await apiGetAgents(); const agentMap = {}; for (const a of agents) agentMap[a.agentId] = a; const agentSkillsMap = {}; for (const a of agents) { agentSkillsMap[a.agentId] = a.skills || []; } function renderAgentInCard(cardEl, agent, emptyIcon, emptyText) { const inner = agent ? buildAgentCard(agent, agentSkillsMap[agent.agentId] || []) : null; _renderCardPreview(cardEl, inner, emptyIcon, emptyText, 'agent-select-change'); } function updateReviewVisibility() { reviewIterationsRow.hidden = !reviewAgentSelect.value; } // Helper: Agent-Picker für eine Rolle einrichten (Initial-Rendering + Click-Handler) function setupAgentPicker(cardEl, selectEl, initialId, emptyIcon, emptyText, onChange) { if (initialId) { selectEl.value = initialId; renderAgentInCard(cardEl, agentMap[initialId] || null, emptyIcon, emptyText); } else { renderAgentInCard(cardEl, null, emptyIcon, emptyText); } cardEl.addEventListener('click', () => { openAgentPickerModal(agents, agentSkillsMap, selectEl.value, (selectedId) => { selectEl.value = selectedId || ''; renderAgentInCard(cardEl, selectedId ? agentMap[selectedId] : null, emptyIcon, emptyText); if (onChange) onChange(selectedId); }); }); } setupAgentPicker(agentSelectCard, agentSelect, session && session.agentId, 'fa-user-circle-o', 'Kein Agent ausgewählt'); setupAgentPicker(preparerAgentSelectCard, preparerAgentSelect, session && session.preparerAgentId, 'fa-filter', 'Kein Vorbereiter-Agent'); setupAgentPicker(reviewAgentSelectCard, reviewAgentSelect, session && session.reviewAgentId, 'fa-check-circle-o', 'Kein Review-Agent', updateReviewVisibility); if (session && session.reviewAgentId) maxReviewIterationsEl.value = session.maxReviewIterations || 3; updateReviewVisibility(); setupAgentPicker(finalizerAgentSelectCard, finalizerAgentSelect, session && session.finalizerAgentId, 'fa-flag-checkered', 'Kein Finalizer-Agent'); initMoreButton(more); // Workspace Browse const workspaceBrowse = document.getElementById('sessionWorkspaceBrowse'); if (workspaceBrowse) { workspaceBrowse.addEventListener('click', () => { const defaultPath = (currentUser && currentUser.defaultPath) || ''; const currentVal = workspaceEl.value.trim(); // Für den Folder-Browser den absoluten Startpfad zusammenbauen let startPath = '/'; if (defaultPath && currentVal) { startPath = defaultPath.replace(/\/+$/, '') + '/' + currentVal; } else if (defaultPath) { startPath = defaultPath; } else if (currentVal) { startPath = currentVal; } openFolderBrowser({ startPath, basePath: defaultPath || null, onSelect(absolutePath) { // Relativen Pfad zum DefaultPath berechnen if (defaultPath && absolutePath.startsWith(defaultPath)) { let rel = absolutePath.slice(defaultPath.length).replace(/^\/+/, ''); workspaceEl.value = rel || ''; } else { // Kein DefaultPath oder Pfad außerhalb → absoluten Pfad beibehalten (Legacy) workspaceEl.value = absolutePath; } } }); }); } // Speichern saveBtn.addEventListener('click', async () => { const commands = getCommandsFromUI(commandsList); const data = { name: nameEl.value.trim() || 'Unbenannt', description: descEl.value.trim(), image: _sessionAvatarEditor.getImage(), agentId: agentSelect.value || null, reviewAgentId: reviewAgentSelect.value || null, maxReviewIterations: parseInt(maxReviewIterationsEl.value) || 3, finalizerAgentId: finalizerAgentSelect.value || null, preparerAgentId: preparerAgentSelect.value || null, context: _sessionContextMde.value().trim() || null, workspace: workspaceEl.value.trim() || null, customCommands: commands, avatarScale: _sessionAvatarEditor.getScale(), avatarOffsetX: _sessionAvatarEditor.getOffsetX(), avatarOffsetY: _sessionAvatarEditor.getOffsetY() }; if (isNew) { await apiCreateSession(data); } else { await apiUpdateSession(sessionId, data); // Telegram-Config mitspeichern try { var tgBotId = document.getElementById('telegramBotSelect').value || ''; var tgChatId = document.getElementById('sessionTelegramChatId').value.trim(); await apiSessionTelegramSave(sessionId, tgBotId, tgChatId); } catch(e) { showToast('Session gespeichert, aber Telegram-Fehler: ' + e.message, 'error'); return; } } navigateTo('dashboard'); }); // Abbrechen cancelBtn.addEventListener('click', () => navigateTo('dashboard')); // Löschen deleteBtn.addEventListener('click', async () => { if (!confirm('Session wirklich löschen?')) return; await apiDeleteSession(sessionId); navigateTo('dashboard'); }); }, destroy() { if (_sessionContextMde) { _sessionContextMde.toTextArea(); _sessionContextMde = null; } if (_sessionAvatarEditor) { _sessionAvatarEditor.destroy(); _sessionAvatarEditor = null; } } }); function openAgentPickerModal(agents, agentSkillsMap, currentId, onSelect) { const items = agents.map(a => { a._pickerId = a.agentId; return a; }); _openPickerModal('Agent auswählen', items, currentId, onSelect, { noneLabel: 'Kein Agent', buildCard: (a) => buildAgentCard(a, agentSkillsMap[a.agentId] || []) }); } function initSessionTelegram(sessionId, session) { var botSelectCard = document.getElementById('telegramBotSelectCard'); var botSelect = document.getElementById('telegramBotSelect'); var chatIdInput = document.getElementById('sessionTelegramChatId'); var unlinkBtn = document.getElementById('sessionTelegramUnlink'); // Unlink-Button unlinkBtn.addEventListener('click', function() { if (!chatIdInput.value) return; if (!confirm('Telegram-Verknüpfung wirklich lösen?')) return; chatIdInput.value = ''; showToast('Chat-ID entfernt. Speichern nicht vergessen.', 'info'); }); // Config laden apiSessionTelegramGet(sessionId).then(function(config) { if (config.chatId) chatIdInput.value = config.chatId; if (config.telegramBotId) botSelect.value = config.telegramBotId; // Bot-Picker initialisieren initTelegramBotPicker(botSelectCard, botSelect, config.telegramBotId); }).catch(function() { initTelegramBotPicker(botSelectCard, botSelect, null); }); } async function initTelegramBotPicker(cardEl, selectEl, currentBotId) { var bots = []; try { var result = await apiGetAvailableTelegramBots(); if (Array.isArray(result)) bots = result; } catch(e) { /* ignore */ } var botMap = {}; for (var i = 0; i < bots.length; i++) botMap[bots[i].botId] = bots[i]; function renderBotInCard(bot) { var inner = bot ? buildTelegramBotCard(bot) : null; _renderCardPreview(cardEl, inner, 'fa-paper-plane', 'Kein Bot zugewiesen', 'tg-bot-select-change'); } if (currentBotId && botMap[currentBotId]) { selectEl.value = currentBotId; renderBotInCard(botMap[currentBotId]); } else { renderBotInCard(null); } cardEl.addEventListener('click', function() { openTelegramBotPickerModal(bots, selectEl.value, function(selectedBotId) { selectEl.value = selectedBotId || ''; renderBotInCard(selectedBotId ? botMap[selectedBotId] : null); }); }); }