Backend: - AgentRecord um CustomCommands-Feld erweitert - DB-Migration: custom_commands Spalte für agents Tabelle - Agent-CRUD (Create/Update) verarbeitet customCommands Frontend: - commands-ui.js: Shared Utility (createCommandRow, getCommandsFromUI, loadCommandsIntoUI) für Agent- und Session-Page - Agent-Page: Benutzerdefinierte Befehle Sektion mit Typ-Dropdown - Chat-Page: Lädt Agent-Commands + Session-Commands, zeigt beide mit Labels wenn beide vorhanden - Session-Page: Refactored auf shared commands-ui.js Funktionen - CSS: cmd-bar-label Styling für Agent:/Session: Trennung in der Command-Bar
136 lines
4.4 KiB
JavaScript
136 lines
4.4 KiB
JavaScript
// ── Agent Page ──
|
|
|
|
let _agentAvatarData = null;
|
|
let _agentPasteHandler = 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 personaEl = document.getElementById('agentPersona');
|
|
const instructionsEl = document.getElementById('agentInstructions');
|
|
const toolChoiceEl = document.getElementById('agentToolChoice');
|
|
const avatarPreview = document.getElementById('avatarPreview');
|
|
const avatarInput = document.getElementById('avatarInput');
|
|
const avatarBtn = document.getElementById('avatarBtn');
|
|
const avatarClearBtn = document.getElementById('avatarClearBtn');
|
|
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');
|
|
|
|
_agentAvatarData = null;
|
|
|
|
if (agent) {
|
|
title.textContent = agent.name;
|
|
nameEl.value = agent.name || '';
|
|
descEl.value = agent.description || '';
|
|
modelEl.value = agent.model || 'sonnet';
|
|
personaEl.value = agent.persona || '';
|
|
instructionsEl.value = agent.instructions || '';
|
|
toolChoiceEl.checked = (agent.toolChoice || 'any') === 'any';
|
|
_agentAvatarData = agent.avatar || null;
|
|
deleteBtn.hidden = false;
|
|
|
|
if (agent.avatar) {
|
|
avatarPreview.innerHTML = `<img src="${agent.avatar}" alt="Avatar">`;
|
|
}
|
|
|
|
// Custom Commands laden
|
|
loadCommandsIntoUI(commandsList, agent.customCommands);
|
|
}
|
|
|
|
// Befehl hinzufügen
|
|
addCommandBtn.addEventListener('click', () => createCommandRow(commandsList));
|
|
|
|
// Avatar Upload
|
|
avatarBtn.addEventListener('click', () => avatarInput.click());
|
|
avatarInput.addEventListener('change', () => {
|
|
const file = avatarInput.files[0];
|
|
if (!file) return;
|
|
const reader = new FileReader();
|
|
reader.onload = () => {
|
|
_agentAvatarData = reader.result;
|
|
avatarPreview.innerHTML = `<img src="${reader.result}" alt="Avatar">`;
|
|
};
|
|
reader.readAsDataURL(file);
|
|
});
|
|
|
|
avatarClearBtn.addEventListener('click', () => {
|
|
_agentAvatarData = null;
|
|
avatarPreview.innerHTML = '?';
|
|
avatarInput.value = '';
|
|
});
|
|
|
|
// Avatar Paste (Ctrl+V)
|
|
document.addEventListener('paste', _agentPasteHandler = (e) => {
|
|
const items = e.clipboardData?.items;
|
|
if (!items) return;
|
|
for (const item of items) {
|
|
if (item.type.startsWith('image/')) {
|
|
e.preventDefault();
|
|
const file = item.getAsFile();
|
|
if (!file) return;
|
|
const reader = new FileReader();
|
|
reader.onload = () => {
|
|
_agentAvatarData = reader.result;
|
|
avatarPreview.innerHTML = `<img src="${reader.result}" alt="Avatar">`;
|
|
};
|
|
reader.readAsDataURL(file);
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Speichern
|
|
saveBtn.addEventListener('click', async () => {
|
|
const data = {
|
|
name: nameEl.value.trim() || 'Unbenannt',
|
|
description: descEl.value.trim(),
|
|
model: modelEl.value,
|
|
persona: personaEl.value,
|
|
instructions: instructionsEl.value,
|
|
toolChoice: toolChoiceEl.checked ? 'any' : 'auto',
|
|
avatar: _agentAvatarData,
|
|
customCommands: getCommandsFromUI(commandsList)
|
|
};
|
|
|
|
if (isNew) {
|
|
await apiCreateAgent(data);
|
|
} else {
|
|
await apiUpdateAgent(agentId, data);
|
|
}
|
|
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 (_agentPasteHandler) {
|
|
document.removeEventListener('paste', _agentPasteHandler);
|
|
_agentPasteHandler = null;
|
|
}
|
|
_agentAvatarData = null;
|
|
}
|
|
});
|