Zusammengeführt/dedupliziert: - renderMessages() nutzt jetzt _renderMessagesIntoFragment() statt eigenen switch/case - addChoiceBubble() + addChoiceBubbleStatic() → _createChoiceBubble() Helper - addErrorBubble() + addWarningBubble() → _addSystemBubble() Helper - Chain-Meta-Erstellung (3x) → _createChainMetaDiv() Helper - Agent-Picker-Setup (4x) → setupAgentPicker() Helper in session-page - more-Click-Handler (2x) → initMoreButton() Helper in ui-helpers - showLoginLinkModal() nutzt jetzt _createModal() - showMessage() in server-settings → showToast() Verwaiste Funktionen entfernt: - addAssistantBubble(), addReviewBubbleStatic(), addChoiceBubbleStatic() - addAttachmentIndicator(), addToolBoxStatic(), appendToolResult() - serverSettingsMessage HTML-Element Netto: -261 Zeilen
184 lines
7.2 KiB
JavaScript
184 lines
7.2 KiB
JavaScript
// ── Agent Page ──
|
||
|
||
let _agentAvatarEditor = null;
|
||
let _agentTaskMde = null;
|
||
|
||
registerPage('agent', {
|
||
async init(params) {
|
||
const agentId = params.id;
|
||
const isNew = !agentId;
|
||
let agent = null;
|
||
|
||
if (!isNew) {
|
||
agent = await apiGetAgent(agentId);
|
||
if (!agent) return navigateTo('dashboard');
|
||
}
|
||
|
||
const title = document.getElementById('agentPageTitle');
|
||
const nameEl = document.getElementById('agentName');
|
||
const descEl = document.getElementById('agentDescription');
|
||
const modelEl = document.getElementById('agentModel');
|
||
const toolChoiceEl = document.getElementById('agentToolChoice');
|
||
const allowUserChoiceEl = document.getElementById('agentAllowUserChoice');
|
||
const autoCommitEl = document.getElementById('agentAutoCommit');
|
||
const useSoulEl = document.getElementById('agentUseSoul');
|
||
const useIdentityEl = document.getElementById('agentUseIdentity');
|
||
const useUserInfoEl = document.getElementById('agentUseUserInfo');
|
||
const useMemoryEl = document.getElementById('agentUseMemory');
|
||
const webSearchVersionEl = document.getElementById('agentWebSearchVersion');
|
||
const avatarPreview = document.getElementById('avatarPreview');
|
||
const saveBtn = document.getElementById('agentSaveBtn');
|
||
const cancelBtn = document.getElementById('agentCancelBtn');
|
||
const deleteBtn = document.getElementById('agentDeleteBtn');
|
||
const commandsList = document.getElementById('agentCommandsList');
|
||
const addCommandBtn = document.getElementById('agentAddCommandBtn');
|
||
const skillsSection = document.getElementById('agentSkillsSection');
|
||
const skillList = document.getElementById('agentSkillList');
|
||
const more = document.querySelector('.more');
|
||
|
||
// Avatar Editor initialisieren
|
||
_agentAvatarEditor = AvatarEditor.init({
|
||
previewEl: avatarPreview,
|
||
initialImage: (agent && agent.avatar) || null,
|
||
initialScale: (agent && agent.avatarScale) || 1,
|
||
initialOffsetX: (agent && agent.avatarOffsetX) || 0,
|
||
initialOffsetY: (agent && agent.avatarOffsetY) || 0
|
||
});
|
||
|
||
// EasyMDE für Task-Instruction
|
||
const taskEl = document.getElementById('agentTaskInstruction');
|
||
_agentTaskMde = createMDE(taskEl, {
|
||
toolbar: ['bold', 'italic', 'heading', '|', 'quote', 'unordered-list', 'ordered-list', '|', 'link', 'code', '|', 'preview', 'guide'],
|
||
placeholder: 'Spezifische Anweisungen für diesen Agent (Markdown, optional)'
|
||
});
|
||
|
||
if (agent) {
|
||
title.textContent = agent.name;
|
||
nameEl.value = agent.name || '';
|
||
descEl.value = agent.description || '';
|
||
modelEl.value = agent.model || 'sonnet';
|
||
toolChoiceEl.checked = (agent.toolChoice || 'any') === 'any';
|
||
allowUserChoiceEl.checked = agent.allowUserChoice !== false;
|
||
_agentTaskMde.value(agent.taskInstruction || '');
|
||
autoCommitEl.value = agent.autoCommit || '';
|
||
useSoulEl.checked = agent.useSoul !== false;
|
||
useIdentityEl.checked = agent.useIdentity !== false;
|
||
useUserInfoEl.checked = agent.useUserInfo !== false;
|
||
useMemoryEl.checked = agent.useMemory !== false;
|
||
webSearchVersionEl.value = agent.webSearchVersion || '';
|
||
deleteBtn.hidden = false;
|
||
|
||
// Custom Commands laden
|
||
loadCommandsIntoUI(commandsList, agent.customCommands);
|
||
|
||
// Skills laden
|
||
const [allSkills, agentSkills] = await Promise.all([
|
||
apiGetSkills(),
|
||
apiGetAgentSkills(agentId)
|
||
]);
|
||
const assignedSlugs = agentSkills.map(s => s.slug);
|
||
skillsSection.hidden = false;
|
||
renderSkillCheckboxes(skillList, allSkills, assignedSlugs);
|
||
} else {
|
||
// Neue Agents: Skills-Liste auch anzeigen
|
||
const allSkills = await apiGetSkills();
|
||
if (allSkills.length > 0) {
|
||
skillsSection.hidden = false;
|
||
renderSkillCheckboxes(skillList, allSkills, []);
|
||
}
|
||
}
|
||
|
||
// Befehl hinzufügen
|
||
addCommandBtn.addEventListener('click', () => createCommandRow(commandsList));
|
||
|
||
initMoreButton(more);
|
||
|
||
// Speichern
|
||
saveBtn.addEventListener('click', async () => {
|
||
const data = {
|
||
name: nameEl.value.trim() || 'Unbenannt',
|
||
description: descEl.value.trim(),
|
||
model: modelEl.value,
|
||
toolChoice: toolChoiceEl.checked ? 'any' : 'auto',
|
||
allowUserChoice: allowUserChoiceEl.checked,
|
||
autoCommit: autoCommitEl.value,
|
||
useSoul: useSoulEl.checked,
|
||
useIdentity: useIdentityEl.checked,
|
||
useUserInfo: useUserInfoEl.checked,
|
||
useMemory: useMemoryEl.checked,
|
||
webSearchVersion: webSearchVersionEl.value,
|
||
avatar: _agentAvatarEditor.getImage(),
|
||
avatarScale: _agentAvatarEditor.getScale(),
|
||
avatarOffsetX: _agentAvatarEditor.getOffsetX(),
|
||
avatarOffsetY: _agentAvatarEditor.getOffsetY(),
|
||
customCommands: getCommandsFromUI(commandsList),
|
||
taskInstruction: _agentTaskMde ? _agentTaskMde.value().trim() || null : null
|
||
};
|
||
|
||
if (isNew) {
|
||
const created = await apiCreateAgent(data);
|
||
// Skills zuweisen falls welche ausgewählt
|
||
const selectedSlugs = getSelectedSkills(skillList);
|
||
if (selectedSlugs.length > 0) {
|
||
await apiSetAgentSkills(created.agentId, selectedSlugs);
|
||
}
|
||
} else {
|
||
await apiUpdateAgent(agentId, data);
|
||
// Skills aktualisieren
|
||
const selectedSlugs = getSelectedSkills(skillList);
|
||
await apiSetAgentSkills(agentId, selectedSlugs);
|
||
}
|
||
navigateTo('dashboard');
|
||
});
|
||
|
||
// Abbrechen
|
||
cancelBtn.addEventListener('click', () => navigateTo('dashboard'));
|
||
|
||
// Löschen
|
||
deleteBtn.addEventListener('click', async () => {
|
||
if (!confirm('Agent wirklich löschen?')) return;
|
||
await apiDeleteAgent(agentId);
|
||
navigateTo('dashboard');
|
||
});
|
||
},
|
||
destroy() {
|
||
if (_agentTaskMde) {
|
||
try { _agentTaskMde.toTextArea(); } catch (_) {}
|
||
_agentTaskMde = null;
|
||
}
|
||
if (_agentAvatarEditor) {
|
||
_agentAvatarEditor.destroy();
|
||
_agentAvatarEditor = null;
|
||
}
|
||
}
|
||
});
|
||
|
||
function renderSkillCheckboxes(container, allSkills, assignedSlugs) {
|
||
container.innerHTML = '';
|
||
if (allSkills.length === 0) {
|
||
container.innerHTML = '<div class="skill-empty">Keine Skills verfügbar. Lege Skills in <code>data/skills/</code> ab.</div>';
|
||
return;
|
||
}
|
||
for (const skill of allSkills) {
|
||
const checked = assignedSlugs.includes(skill.slug) ? 'checked' : '';
|
||
const disabled = !skill.hasPromptMd ? 'disabled' : '';
|
||
const version = skill.version ? ` <span class="skill-version">v${skill.version}</span>` : '';
|
||
const desc = skill.description ? `<span class="skill-desc">${skill.description}</span>` : '';
|
||
const warning = !skill.hasPromptMd ? '<span class="skill-warning">⚠ PROMPT.md fehlt – Skill nicht verwendbar</span>' : '';
|
||
const row = document.createElement('label');
|
||
row.className = 'skill-item' + (!skill.hasPromptMd ? ' skill-unavailable' : '');
|
||
row.innerHTML = `
|
||
<input type="checkbox" value="${skill.slug}" ${checked} ${disabled}>
|
||
<span class="skill-name">${skill.name}${version}</span>
|
||
${desc}
|
||
${warning}
|
||
`;
|
||
container.appendChild(row);
|
||
}
|
||
}
|
||
|
||
function getSelectedSkills(container) {
|
||
return Array.from(container.querySelectorAll('input[type="checkbox"]:checked'))
|
||
.map(cb => cb.value);
|
||
}
|