refactor(frontend): Duplikate in JS eliminieren – Runde 2
Neue Helper-Funktionen: - _openCardMenu(trigger, dropdown): Card-Menü-Positionierung (4x in dashboard.js dedupliziert) - _createModal(title, extraClasses): Modal-Boilerplate (3 Modals in modals.js dedupliziert) - _openPickerModal(title, items, currentId, onSelect, opts): Picker-Modal (Agent + Session zusammengeführt) - createMDE(element, opts): EasyMDE-Initialisierung (3x in agent/session/profile-page dedupliziert) Weitere Bereinigungen: - _formatReplyTimestamp() entfernt, durch _formatTimestamp() ersetzt (reply.js + chat.js) - showFormMessage/showPageMessage in admin-page.js durch showToast() ersetzt - Inline-Nachrichten (profileMessage, profilePasswordMessage, adminMessage, adminFormMessage) durch Toast-Notifications ersetzt, zugehörige HTML-Elemente entfernt - profile-page.js: messageDiv/passwordMessageDiv-Logik durch showToast() ersetzt 11 Dateien, -210 Zeilen netto
This commit is contained in:
1 parent
b56b83ad1e
commit
1a2c407538
11 files changed
+177
-387
No files matched your search
+3
-4
@@ -442,7 +442,7 @@
|
|||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button id="profileSaveBtn">Speichern</button>
|
<button id="profileSaveBtn">Speichern</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-message" id="profileMessage" hidden></div>
|
|
||||||
<h2>Passwort ändern</h2>
|
<h2>Passwort ändern</h2>
|
||||||
<label>Aktuelles Passwort</label>
|
<label>Aktuelles Passwort</label>
|
||||||
<input type="password" id="profileCurrentPassword" placeholder="Aktuelles Passwort" autocomplete="current-password">
|
<input type="password" id="profileCurrentPassword" placeholder="Aktuelles Passwort" autocomplete="current-password">
|
||||||
@@ -453,7 +453,7 @@
|
|||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button id="profilePasswordBtn">Passwort ändern</button>
|
<button id="profilePasswordBtn">Passwort ändern</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-message" id="profilePasswordMessage" hidden></div>
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button id="profileLogoutBtn" class="danger-btn">Abmelden</button>
|
<button id="profileLogoutBtn" class="danger-btn">Abmelden</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -494,7 +494,7 @@
|
|||||||
<button id="adminNewUserBtn" class="section-add-btn"><i class="fa fa-plus"></i> Neuer Benutzer</button>
|
<button id="adminNewUserBtn" class="section-add-btn"><i class="fa fa-plus"></i> Neuer Benutzer</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="adminUserList" class="admin-user-list"></div>
|
<div id="adminUserList" class="admin-user-list"></div>
|
||||||
<div id="adminMessage" class="profile-message" hidden></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Inline-Formular zum Erstellen/Bearbeiten -->
|
<!-- Inline-Formular zum Erstellen/Bearbeiten -->
|
||||||
@@ -520,7 +520,6 @@
|
|||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button id="adminFormSaveBtn">Erstellen</button>
|
<button id="adminFormSaveBtn">Erstellen</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="adminFormMessage" class="profile-message" hidden></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -297,7 +297,7 @@ async function sendMessage() {
|
|||||||
let msgText = text;
|
let msgText = text;
|
||||||
if (pendingReply) {
|
if (pendingReply) {
|
||||||
const roleLabel = pendingReply.role === 'user' ? 'User' : 'Assistant';
|
const roleLabel = pendingReply.role === 'user' ? 'User' : 'Assistant';
|
||||||
const timeLabel = _formatReplyTimestamp(pendingReply.timestamp);
|
const timeLabel = _formatTimestamp(pendingReply.timestamp) || 'Jetzt';
|
||||||
const quoteText = pendingReply.text.split('\n').map(l => '> ' + l).join('\n');
|
const quoteText = pendingReply.text.split('\n').map(l => '> ' + l).join('\n');
|
||||||
const quoteHeader = `> [Zitat: ${timeLabel}, ${roleLabel}]:`;
|
const quoteHeader = `> [Zitat: ${timeLabel}, ${roleLabel}]:`;
|
||||||
msgText = quoteHeader + '\n' + quoteText + '\n\n' + (text || '');
|
msgText = quoteHeader + '\n' + quoteText + '\n\n' + (text || '');
|
||||||
|
|||||||
+100
-107
@@ -1,3 +1,43 @@
|
|||||||
|
// ── Modal Helper ──
|
||||||
|
|
||||||
|
function _createModal(titleText, extraClasses) {
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.className = 'sp-overlay' + (extraClasses && extraClasses.overlay ? ' ' + extraClasses.overlay : '');
|
||||||
|
|
||||||
|
const modal = document.createElement('div');
|
||||||
|
modal.className = 'sp-modal' + (extraClasses && extraClasses.modal ? ' ' + extraClasses.modal : '');
|
||||||
|
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.className = 'sp-header';
|
||||||
|
const title = document.createElement('span');
|
||||||
|
title.textContent = titleText;
|
||||||
|
const closeBtn = document.createElement('button');
|
||||||
|
closeBtn.innerHTML = '<i class="fa fa-times"></i>';
|
||||||
|
closeBtn.className = 'sp-close icon-btn';
|
||||||
|
header.appendChild(title);
|
||||||
|
header.appendChild(closeBtn);
|
||||||
|
|
||||||
|
const body = document.createElement('div');
|
||||||
|
body.className = 'sp-body' + (extraClasses && extraClasses.body ? ' ' + extraClasses.body : '');
|
||||||
|
|
||||||
|
modal.appendChild(header);
|
||||||
|
modal.appendChild(body);
|
||||||
|
overlay.appendChild(modal);
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
|
||||||
|
const close = () => overlay.remove();
|
||||||
|
closeBtn.addEventListener('click', close);
|
||||||
|
overlay.addEventListener('click', (e) => {
|
||||||
|
if (e.target === overlay) close();
|
||||||
|
});
|
||||||
|
const onKey = (e) => {
|
||||||
|
if (e.key === 'Escape') { close(); document.removeEventListener('keydown', onKey); }
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', onKey);
|
||||||
|
|
||||||
|
return { overlay, modal, body, close };
|
||||||
|
}
|
||||||
|
|
||||||
// ── System-Prompt Modal ──
|
// ── System-Prompt Modal ──
|
||||||
|
|
||||||
function _renderSystemPromptBlocks(container, blocks) {
|
function _renderSystemPromptBlocks(container, blocks) {
|
||||||
@@ -24,47 +64,9 @@ function _renderSystemPromptBlocks(container, blocks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function showSystemPromptModal(sessionId) {
|
async function showSystemPromptModal(sessionId) {
|
||||||
// Overlay
|
const { body } = _createModal('System-Prompt', { body: 'markdown' });
|
||||||
const overlay = document.createElement('div');
|
|
||||||
overlay.className = 'sp-overlay';
|
|
||||||
|
|
||||||
// Modal
|
|
||||||
const modal = document.createElement('div');
|
|
||||||
modal.className = 'sp-modal';
|
|
||||||
|
|
||||||
// Header
|
|
||||||
const header = document.createElement('div');
|
|
||||||
header.className = 'sp-header';
|
|
||||||
const title = document.createElement('span');
|
|
||||||
title.textContent = 'System-Prompt';
|
|
||||||
const closeBtn = document.createElement('button');
|
|
||||||
closeBtn.innerHTML = '<i class="fa fa-times"></i>';
|
|
||||||
closeBtn.className = 'sp-close icon-btn';
|
|
||||||
header.appendChild(title);
|
|
||||||
header.appendChild(closeBtn);
|
|
||||||
|
|
||||||
// Body
|
|
||||||
const body = document.createElement('div');
|
|
||||||
body.className = 'sp-body markdown';
|
|
||||||
body.innerHTML = '<div class="sp-loading">Laden...</div>';
|
body.innerHTML = '<div class="sp-loading">Laden...</div>';
|
||||||
|
|
||||||
modal.appendChild(header);
|
|
||||||
modal.appendChild(body);
|
|
||||||
overlay.appendChild(modal);
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
// Schließen
|
|
||||||
const close = () => overlay.remove();
|
|
||||||
closeBtn.addEventListener('click', close);
|
|
||||||
overlay.addEventListener('click', (e) => {
|
|
||||||
if (e.target === overlay) close();
|
|
||||||
});
|
|
||||||
const onKey = (e) => {
|
|
||||||
if (e.key === 'Escape') { close(); document.removeEventListener('keydown', onKey); }
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', onKey);
|
|
||||||
|
|
||||||
// Laden
|
|
||||||
try {
|
try {
|
||||||
const blocks = await apiGetSystemPromptBlocks(sessionId, typeof _planModeActive !== 'undefined' && _planModeActive);
|
const blocks = await apiGetSystemPromptBlocks(sessionId, typeof _planModeActive !== 'undefined' && _planModeActive);
|
||||||
body.innerHTML = '';
|
body.innerHTML = '';
|
||||||
@@ -77,30 +79,11 @@ async function showSystemPromptModal(sessionId) {
|
|||||||
// ── Command Output Modal ──
|
// ── Command Output Modal ──
|
||||||
|
|
||||||
function showCommandOutputModal(name, command, result) {
|
function showCommandOutputModal(name, command, result) {
|
||||||
const overlay = document.createElement('div');
|
const { body } = _createModal(name, { modal: 'cmd-output-modal' });
|
||||||
overlay.className = 'sp-overlay';
|
|
||||||
|
|
||||||
const modal = document.createElement('div');
|
|
||||||
modal.className = 'sp-modal cmd-output-modal';
|
|
||||||
|
|
||||||
// Header
|
|
||||||
const header = document.createElement('div');
|
|
||||||
header.className = 'sp-header';
|
|
||||||
const title = document.createElement('span');
|
|
||||||
title.textContent = name;
|
|
||||||
const closeBtn = document.createElement('button');
|
|
||||||
closeBtn.innerHTML = '<i class="fa fa-times"></i>';
|
|
||||||
closeBtn.className = 'sp-close icon-btn';
|
|
||||||
header.appendChild(title);
|
|
||||||
header.appendChild(closeBtn);
|
|
||||||
|
|
||||||
// Body
|
|
||||||
const body = document.createElement('div');
|
|
||||||
body.className = 'sp-body';
|
|
||||||
|
|
||||||
const exitCode = result.exitCode != null ? result.exitCode : '?';
|
const exitCode = result.exitCode != null ? result.exitCode : '?';
|
||||||
const isError = exitCode !== 0;
|
const isError = exitCode !== 0;
|
||||||
const statusText = result.status === 'timeout' ? 'Timeout' : (isError ? `Exit ${exitCode}` : `Exit ${exitCode}`);
|
const statusText = result.status === 'timeout' ? 'Timeout' : `Exit ${exitCode}`;
|
||||||
|
|
||||||
const meta = document.createElement('div');
|
const meta = document.createElement('div');
|
||||||
meta.className = 'cmd-output-meta';
|
meta.className = 'cmd-output-meta';
|
||||||
@@ -111,72 +94,82 @@ function showCommandOutputModal(name, command, result) {
|
|||||||
output.className = 'cmd-output-content';
|
output.className = 'cmd-output-content';
|
||||||
output.textContent = result.output || '(kein Output)';
|
output.textContent = result.output || '(kein Output)';
|
||||||
body.appendChild(output);
|
body.appendChild(output);
|
||||||
|
|
||||||
modal.appendChild(header);
|
|
||||||
modal.appendChild(body);
|
|
||||||
overlay.appendChild(modal);
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
// Schließen
|
|
||||||
const close = () => overlay.remove();
|
|
||||||
closeBtn.addEventListener('click', close);
|
|
||||||
overlay.addEventListener('click', (e) => {
|
|
||||||
if (e.target === overlay) close();
|
|
||||||
});
|
|
||||||
const onKey = (e) => {
|
|
||||||
if (e.key === 'Escape') { close(); document.removeEventListener('keydown', onKey); }
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', onKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Krea Image Preview Modal ──
|
// ── Krea Image Preview Modal ──
|
||||||
|
|
||||||
function showKreaImageModal(title, fullUrl) {
|
function showKreaImageModal(titleText, fullUrl) {
|
||||||
var overlay = document.createElement('div');
|
const { body } = _createModal(titleText, { overlay: 'krea-overlay', modal: 'krea-modal', body: 'krea-modal-body' });
|
||||||
overlay.className = 'sp-overlay krea-overlay';
|
|
||||||
|
|
||||||
var modal = document.createElement('div');
|
|
||||||
modal.className = 'sp-modal krea-modal';
|
|
||||||
|
|
||||||
// Header
|
|
||||||
var header = document.createElement('div');
|
|
||||||
header.className = 'sp-header';
|
|
||||||
var titleEl = document.createElement('span');
|
|
||||||
titleEl.textContent = title;
|
|
||||||
var closeBtn = document.createElement('button');
|
|
||||||
closeBtn.innerHTML = '<i class="fa fa-times"></i>';
|
|
||||||
closeBtn.className = 'sp-close icon-btn';
|
|
||||||
header.appendChild(titleEl);
|
|
||||||
header.appendChild(closeBtn);
|
|
||||||
|
|
||||||
// Body
|
|
||||||
var body = document.createElement('div');
|
|
||||||
body.className = 'sp-body krea-modal-body';
|
|
||||||
|
|
||||||
var img = document.createElement('img');
|
var img = document.createElement('img');
|
||||||
img.src = fullUrl;
|
img.src = fullUrl;
|
||||||
img.className = 'krea-modal-image';
|
img.className = 'krea-modal-image';
|
||||||
img.alt = title;
|
img.alt = titleText;
|
||||||
img.addEventListener('error', function() {
|
img.addEventListener('error', function() {
|
||||||
body.innerHTML = '<div class="sp-loading" style="color:var(--dangerous)">Bild konnte nicht geladen werden</div>';
|
body.innerHTML = '<div class="sp-loading" style="color:var(--dangerous)">Bild konnte nicht geladen werden</div>';
|
||||||
});
|
});
|
||||||
|
|
||||||
body.appendChild(img);
|
body.appendChild(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Picker Modal Helper ──
|
||||||
|
|
||||||
|
function _openPickerModal(title, items, currentId, onSelect, options) {
|
||||||
|
options = options || {};
|
||||||
|
const noneLabel = options.noneLabel || 'Keine Auswahl';
|
||||||
|
const noneIcon = options.noneIcon || 'fa-ban';
|
||||||
|
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.className = 'agent-picker-overlay';
|
||||||
|
|
||||||
|
const modal = document.createElement('div');
|
||||||
|
modal.className = 'agent-picker-modal';
|
||||||
|
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.className = 'agent-picker-header';
|
||||||
|
header.innerHTML = `<span>${esc(title)}</span><button class="icon-btn agent-picker-close"><i class="fa fa-times"></i></button>`;
|
||||||
modal.appendChild(header);
|
modal.appendChild(header);
|
||||||
|
|
||||||
|
const body = document.createElement('div');
|
||||||
|
body.className = 'agent-picker-body';
|
||||||
|
|
||||||
|
// "Keine" Option
|
||||||
|
const noneCard = document.createElement('div');
|
||||||
|
noneCard.className = 'card agent-picker-item' + (!currentId ? ' selected' : '');
|
||||||
|
noneCard.innerHTML = `
|
||||||
|
<div class="card-image" style="display:flex;align-items:center;justify-content:center"><i class="fa ${esc(noneIcon)}" style="font-size:2em;opacity:var(--semi-transparent)"></i></div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="card-title-row">
|
||||||
|
<span class="card-title">${esc(noneLabel)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
noneCard.addEventListener('click', () => {
|
||||||
|
onSelect(null);
|
||||||
|
overlay.remove();
|
||||||
|
});
|
||||||
|
body.appendChild(noneCard);
|
||||||
|
|
||||||
|
// Items
|
||||||
|
for (const item of items) {
|
||||||
|
const cardEl = options.buildCard(item);
|
||||||
|
cardEl.classList.add('agent-picker-item');
|
||||||
|
if (item._pickerId === currentId) cardEl.classList.add('selected');
|
||||||
|
cardEl.addEventListener('click', () => {
|
||||||
|
onSelect(item._pickerId);
|
||||||
|
overlay.remove();
|
||||||
|
});
|
||||||
|
body.appendChild(cardEl);
|
||||||
|
}
|
||||||
|
|
||||||
modal.appendChild(body);
|
modal.appendChild(body);
|
||||||
overlay.appendChild(modal);
|
overlay.appendChild(modal);
|
||||||
document.body.appendChild(overlay);
|
document.body.appendChild(overlay);
|
||||||
|
|
||||||
// Schließen
|
header.querySelector('.agent-picker-close').addEventListener('click', () => overlay.remove());
|
||||||
var close = function() { overlay.remove(); };
|
overlay.addEventListener('click', (e) => {
|
||||||
closeBtn.addEventListener('click', close);
|
if (e.target === overlay) overlay.remove();
|
||||||
overlay.addEventListener('click', function(e) {
|
|
||||||
if (e.target === overlay) close();
|
|
||||||
});
|
});
|
||||||
var onKey = function(e) {
|
|
||||||
if (e.key === 'Escape') { close(); document.removeEventListener('keydown', onKey); }
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', onKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event-Delegation für Krea-Thumbnails im Chat
|
// Event-Delegation für Krea-Thumbnails im Chat
|
||||||
|
|||||||
@@ -22,26 +22,6 @@ registerPage('admin', {
|
|||||||
var defaultPathBrowse = document.getElementById('adminDefaultPathBrowse');
|
var defaultPathBrowse = document.getElementById('adminDefaultPathBrowse');
|
||||||
var isAdminCheckbox = document.getElementById('adminIsAdmin');
|
var isAdminCheckbox = document.getElementById('adminIsAdmin');
|
||||||
var saveBtn = document.getElementById('adminFormSaveBtn');
|
var saveBtn = document.getElementById('adminFormSaveBtn');
|
||||||
var formMessage = document.getElementById('adminFormMessage');
|
|
||||||
var pageMessage = document.getElementById('adminMessage');
|
|
||||||
|
|
||||||
// ── Hilfsfunktionen ──
|
|
||||||
|
|
||||||
function showFormMessage(text, isError) {
|
|
||||||
formMessage.textContent = text;
|
|
||||||
formMessage.className = 'profile-message ' + (isError ? 'profile-message-error' : 'profile-message-success');
|
|
||||||
formMessage.hidden = false;
|
|
||||||
if (!isError) {
|
|
||||||
setTimeout(function() { formMessage.hidden = true; }, 3000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showPageMessage(text, isError) {
|
|
||||||
pageMessage.textContent = text;
|
|
||||||
pageMessage.className = 'profile-message ' + (isError ? 'profile-message-error' : 'profile-message-success');
|
|
||||||
pageMessage.hidden = false;
|
|
||||||
setTimeout(function() { pageMessage.hidden = true; }, 4000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderUsers() {
|
function renderUsers() {
|
||||||
list.innerHTML = '';
|
list.innerHTML = '';
|
||||||
@@ -111,7 +91,7 @@ registerPage('admin', {
|
|||||||
users = await apiAdminGetUsers();
|
users = await apiAdminGetUsers();
|
||||||
renderUsers();
|
renderUsers();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showPageMessage(err.message, true);
|
showToast(err.message, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +108,6 @@ registerPage('admin', {
|
|||||||
defaultPathInput.value = user.defaultPath || '';
|
defaultPathInput.value = user.defaultPath || '';
|
||||||
isAdminCheckbox.checked = user.isAdmin;
|
isAdminCheckbox.checked = user.isAdmin;
|
||||||
saveBtn.textContent = 'Speichern';
|
saveBtn.textContent = 'Speichern';
|
||||||
formMessage.hidden = true;
|
|
||||||
userForm.hidden = false;
|
userForm.hidden = false;
|
||||||
usernameInput.focus();
|
usernameInput.focus();
|
||||||
}
|
}
|
||||||
@@ -142,7 +121,7 @@ registerPage('admin', {
|
|||||||
await apiAdminDeleteUser(user.userId);
|
await apiAdminDeleteUser(user.userId);
|
||||||
await loadUsers();
|
await loadUsers();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showPageMessage(err.message, true);
|
showToast(err.message, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +131,7 @@ registerPage('admin', {
|
|||||||
var link = window.location.origin + '/api/auth/autologin?token=' + result.token;
|
var link = window.location.origin + '/api/auth/autologin?token=' + result.token;
|
||||||
showLoginLinkModal(user, link);
|
showLoginLinkModal(user, link);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showPageMessage(err.message, true);
|
showToast(err.message, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,14 +228,12 @@ registerPage('admin', {
|
|||||||
defaultPathInput.value = '';
|
defaultPathInput.value = '';
|
||||||
isAdminCheckbox.checked = false;
|
isAdminCheckbox.checked = false;
|
||||||
saveBtn.textContent = 'Erstellen';
|
saveBtn.textContent = 'Erstellen';
|
||||||
formMessage.hidden = true;
|
|
||||||
userForm.hidden = false;
|
userForm.hidden = false;
|
||||||
usernameInput.focus();
|
usernameInput.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Speichern
|
// Speichern
|
||||||
saveBtn.addEventListener('click', async function() {
|
saveBtn.addEventListener('click', async function() {
|
||||||
formMessage.hidden = true;
|
|
||||||
saveBtn.disabled = true;
|
saveBtn.disabled = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -276,14 +253,14 @@ registerPage('admin', {
|
|||||||
await apiAdminResetPassword(editingUserId, pw);
|
await apiAdminResetPassword(editingUserId, pw);
|
||||||
}
|
}
|
||||||
|
|
||||||
showFormMessage('Benutzer aktualisiert', false);
|
showToast('Benutzer aktualisiert');
|
||||||
} else {
|
} else {
|
||||||
// Create
|
// Create
|
||||||
var username = usernameInput.value.trim();
|
var username = usernameInput.value.trim();
|
||||||
var password = passwordInput.value;
|
var password = passwordInput.value;
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
showFormMessage('Benutzername und Passwort erforderlich', true);
|
showToast('Benutzername und Passwort erforderlich', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,13 +272,13 @@ registerPage('admin', {
|
|||||||
isAdmin: isAdminCheckbox.checked
|
isAdmin: isAdminCheckbox.checked
|
||||||
});
|
});
|
||||||
|
|
||||||
showFormMessage('Benutzer erstellt', false);
|
showToast('Benutzer erstellt');
|
||||||
}
|
}
|
||||||
|
|
||||||
userForm.hidden = true;
|
userForm.hidden = true;
|
||||||
await loadUsers();
|
await loadUsers();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showFormMessage(err.message, true);
|
showToast(err.message, 'error');
|
||||||
} finally {
|
} finally {
|
||||||
saveBtn.disabled = false;
|
saveBtn.disabled = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,18 +47,10 @@ registerPage('agent', {
|
|||||||
|
|
||||||
// EasyMDE für Task-Instruction
|
// EasyMDE für Task-Instruction
|
||||||
const taskEl = document.getElementById('agentTaskInstruction');
|
const taskEl = document.getElementById('agentTaskInstruction');
|
||||||
_agentTaskMde = new EasyMDE({
|
_agentTaskMde = createMDE(taskEl, {
|
||||||
element: taskEl,
|
|
||||||
toolbar: ['bold', 'italic', 'heading', '|', 'quote', 'unordered-list', 'ordered-list', '|', 'link', 'code', '|', 'preview', 'guide'],
|
toolbar: ['bold', 'italic', 'heading', '|', 'quote', 'unordered-list', 'ordered-list', '|', 'link', 'code', '|', 'preview', 'guide'],
|
||||||
status: false,
|
placeholder: 'Spezifische Anweisungen für diesen Agent (Markdown, optional)'
|
||||||
autofocus: false,
|
|
||||||
spellChecker: false,
|
|
||||||
autoDownloadFontAwesome: false,
|
|
||||||
placeholder: 'Spezifische Anweisungen für diesen Agent (Markdown, optional)',
|
|
||||||
minHeight: '120px'
|
|
||||||
});
|
});
|
||||||
_agentTaskMde.codemirror.setOption('theme', 'default');
|
|
||||||
_agentTaskMde.codemirror.getWrapperElement().closest('.EasyMDEContainer').classList.add('fe-mde-wrap');
|
|
||||||
|
|
||||||
if (agent) {
|
if (agent) {
|
||||||
title.textContent = agent.name;
|
title.textContent = agent.name;
|
||||||
|
|||||||
@@ -156,68 +156,15 @@ registerPage('cronjob', {
|
|||||||
function openSessionPickerModal(sessions, agentMap, currentId, onSelect, options) {
|
function openSessionPickerModal(sessions, agentMap, currentId, onSelect, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
const filterWithAgent = options.filterWithAgent || false;
|
const filterWithAgent = options.filterWithAgent || false;
|
||||||
const title = options.title || 'Session auswählen';
|
const filtered = filterWithAgent ? sessions.filter(s => s.agentId) : sessions;
|
||||||
const noneLabel = options.noneLabel || 'Keine Session';
|
const items = filtered.map(s => {
|
||||||
const noneIcon = options.noneIcon || 'fa-ban';
|
s._pickerId = s.sessionId;
|
||||||
|
return s;
|
||||||
// Overlay
|
|
||||||
const overlay = document.createElement('div');
|
|
||||||
overlay.className = 'agent-picker-overlay';
|
|
||||||
|
|
||||||
const modal = document.createElement('div');
|
|
||||||
modal.className = 'agent-picker-modal';
|
|
||||||
|
|
||||||
// Header
|
|
||||||
const header = document.createElement('div');
|
|
||||||
header.className = 'agent-picker-header';
|
|
||||||
header.innerHTML = `<span>${esc(title)}</span><button class="icon-btn agent-picker-close"><i class="fa fa-times"></i></button>`;
|
|
||||||
modal.appendChild(header);
|
|
||||||
|
|
||||||
// Body
|
|
||||||
const body = document.createElement('div');
|
|
||||||
body.className = 'agent-picker-body';
|
|
||||||
|
|
||||||
// "Keine Session" Option
|
|
||||||
const noneCard = document.createElement('div');
|
|
||||||
noneCard.className = 'card agent-picker-item' + (!currentId ? ' selected' : '');
|
|
||||||
noneCard.innerHTML = `
|
|
||||||
<div class="card-image" style="display:flex;align-items:center;justify-content:center"><i class="fa ${esc(noneIcon)}" style="font-size:2em;opacity:var(--semi-transparent)"></i></div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="card-title-row">
|
|
||||||
<span class="card-title">${esc(noneLabel)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
noneCard.addEventListener('click', () => {
|
|
||||||
onSelect(null);
|
|
||||||
overlay.remove();
|
|
||||||
});
|
});
|
||||||
body.appendChild(noneCard);
|
_openPickerModal(options.title || 'Session auswählen', items, currentId, onSelect, {
|
||||||
|
noneLabel: options.noneLabel || 'Keine Session',
|
||||||
// Session Cards via Helper
|
noneIcon: options.noneIcon || 'fa-ban',
|
||||||
for (const s of sessions) {
|
buildCard: (s) => buildSessionCard(s, s.agentId ? agentMap[s.agentId] : null)
|
||||||
// Filter: nur Sessions mit Agent, wenn gewünscht
|
|
||||||
if (filterWithAgent && !s.agentId) continue;
|
|
||||||
|
|
||||||
const agent = s.agentId ? agentMap[s.agentId] : null;
|
|
||||||
const item = buildSessionCard(s, agent);
|
|
||||||
item.classList.add('agent-picker-item');
|
|
||||||
if (s.sessionId === currentId) item.classList.add('selected');
|
|
||||||
item.addEventListener('click', () => {
|
|
||||||
onSelect(s.sessionId);
|
|
||||||
overlay.remove();
|
|
||||||
});
|
|
||||||
body.appendChild(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
modal.appendChild(body);
|
|
||||||
overlay.appendChild(modal);
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
// Schließen
|
|
||||||
header.querySelector('.agent-picker-close').addEventListener('click', () => overlay.remove());
|
|
||||||
overlay.addEventListener('click', (e) => {
|
|
||||||
if (e.target === overlay) overlay.remove();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,23 +107,7 @@ registerPage('dashboard', {
|
|||||||
const menuDropdown = card.querySelector('.card-menu-dropdown');
|
const menuDropdown = card.querySelector('.card-menu-dropdown');
|
||||||
menuTrigger.addEventListener('click', (e) => {
|
menuTrigger.addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
closeAllCardMenus();
|
_openCardMenu(menuTrigger, menuDropdown);
|
||||||
// Position fixed dropdown below trigger, aligned to the left
|
|
||||||
const rect = menuTrigger.getBoundingClientRect();
|
|
||||||
menuDropdown.hidden = false;
|
|
||||||
const dropRect = menuDropdown.getBoundingClientRect();
|
|
||||||
// Position: below trigger, right edge aligned with trigger's right edge
|
|
||||||
menuDropdown.style.top = `${rect.bottom + 4}px`;
|
|
||||||
menuDropdown.style.left = `${rect.right - dropRect.width}px`;
|
|
||||||
// Adjust if off-screen bottom
|
|
||||||
if (rect.bottom + 4 + dropRect.height > window.innerHeight) {
|
|
||||||
menuDropdown.style.top = `${rect.top - dropRect.height - 4}px`;
|
|
||||||
}
|
|
||||||
// Safety: if off-screen left
|
|
||||||
const finalRect = menuDropdown.getBoundingClientRect();
|
|
||||||
if (finalRect.left < 0) {
|
|
||||||
menuDropdown.style.left = '8px';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
chatBtn.addEventListener('click', (e) => {
|
chatBtn.addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -166,23 +150,7 @@ registerPage('dashboard', {
|
|||||||
const menuDropdown = card.querySelector('.card-menu-dropdown');
|
const menuDropdown = card.querySelector('.card-menu-dropdown');
|
||||||
menuTrigger.addEventListener('click', (e) => {
|
menuTrigger.addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
closeAllCardMenus();
|
_openCardMenu(menuTrigger, menuDropdown);
|
||||||
// Position fixed dropdown below trigger, aligned to the left
|
|
||||||
const rect = menuTrigger.getBoundingClientRect();
|
|
||||||
menuDropdown.hidden = false;
|
|
||||||
const dropRect = menuDropdown.getBoundingClientRect();
|
|
||||||
// Position: below trigger, right edge aligned with trigger's right edge
|
|
||||||
menuDropdown.style.top = `${rect.bottom + 4}px`;
|
|
||||||
menuDropdown.style.left = `${rect.right - dropRect.width}px`;
|
|
||||||
// Adjust if off-screen bottom
|
|
||||||
if (rect.bottom + 4 + dropRect.height > window.innerHeight) {
|
|
||||||
menuDropdown.style.top = `${rect.top - dropRect.height - 4}px`;
|
|
||||||
}
|
|
||||||
// Safety: if off-screen left
|
|
||||||
const finalRect = menuDropdown.getBoundingClientRect();
|
|
||||||
if (finalRect.left < 0) {
|
|
||||||
menuDropdown.style.left = '8px';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
menuDropdown.querySelector('[data-action="files"]').addEventListener('click', (e) => {
|
menuDropdown.querySelector('[data-action="files"]').addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -243,17 +211,7 @@ registerPage('dashboard', {
|
|||||||
const menuDropdown = card.querySelector('.card-menu-dropdown');
|
const menuDropdown = card.querySelector('.card-menu-dropdown');
|
||||||
menuTrigger.addEventListener('click', (e) => {
|
menuTrigger.addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
closeAllCardMenus();
|
_openCardMenu(menuTrigger, menuDropdown);
|
||||||
const rect = menuTrigger.getBoundingClientRect();
|
|
||||||
menuDropdown.hidden = false;
|
|
||||||
const dropRect = menuDropdown.getBoundingClientRect();
|
|
||||||
menuDropdown.style.top = `${rect.bottom + 4}px`;
|
|
||||||
menuDropdown.style.left = `${rect.right - dropRect.width}px`;
|
|
||||||
if (rect.bottom + 4 + dropRect.height > window.innerHeight) {
|
|
||||||
menuDropdown.style.top = `${rect.top - dropRect.height - 4}px`;
|
|
||||||
}
|
|
||||||
const finalRect = menuDropdown.getBoundingClientRect();
|
|
||||||
if (finalRect.left < 0) menuDropdown.style.left = '8px';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
menuDropdown.querySelector('[data-action="edit"]').addEventListener('click', (e) => {
|
menuDropdown.querySelector('[data-action="edit"]').addEventListener('click', (e) => {
|
||||||
@@ -327,17 +285,7 @@ registerPage('dashboard', {
|
|||||||
const menuDropdown = card.querySelector('.card-menu-dropdown');
|
const menuDropdown = card.querySelector('.card-menu-dropdown');
|
||||||
menuTrigger.addEventListener('click', (e) => {
|
menuTrigger.addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
closeAllCardMenus();
|
_openCardMenu(menuTrigger, menuDropdown);
|
||||||
const rect = menuTrigger.getBoundingClientRect();
|
|
||||||
menuDropdown.hidden = false;
|
|
||||||
const dropRect = menuDropdown.getBoundingClientRect();
|
|
||||||
menuDropdown.style.top = `${rect.bottom + 4}px`;
|
|
||||||
menuDropdown.style.left = `${rect.right - dropRect.width}px`;
|
|
||||||
if (rect.bottom + 4 + dropRect.height > window.innerHeight) {
|
|
||||||
menuDropdown.style.top = `${rect.top - dropRect.height - 4}px`;
|
|
||||||
}
|
|
||||||
const finalRect = menuDropdown.getBoundingClientRect();
|
|
||||||
if (finalRect.left < 0) menuDropdown.style.left = '8px';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
menuDropdown.querySelector('[data-action="copy"]').addEventListener('click', async (e) => {
|
menuDropdown.querySelector('[data-action="copy"]').addEventListener('click', async (e) => {
|
||||||
@@ -378,6 +326,20 @@ function closeAllCardMenus() {
|
|||||||
document.querySelectorAll('.card-menu-dropdown').forEach(d => d.hidden = true);
|
document.querySelectorAll('.card-menu-dropdown').forEach(d => d.hidden = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _openCardMenu(trigger, dropdown) {
|
||||||
|
closeAllCardMenus();
|
||||||
|
const rect = trigger.getBoundingClientRect();
|
||||||
|
dropdown.hidden = false;
|
||||||
|
const dropRect = dropdown.getBoundingClientRect();
|
||||||
|
dropdown.style.top = `${rect.bottom + 4}px`;
|
||||||
|
dropdown.style.left = `${rect.right - dropRect.width}px`;
|
||||||
|
if (rect.bottom + 4 + dropRect.height > window.innerHeight) {
|
||||||
|
dropdown.style.top = `${rect.top - dropRect.height - 4}px`;
|
||||||
|
}
|
||||||
|
const finalRect = dropdown.getBoundingClientRect();
|
||||||
|
if (finalRect.left < 0) dropdown.style.left = '8px';
|
||||||
|
}
|
||||||
|
|
||||||
function formatDate(iso) {
|
function formatDate(iso) {
|
||||||
if (!iso) return '';
|
if (!iso) return '';
|
||||||
return new Date(iso).toLocaleString('de-AT', {
|
return new Date(iso).toLocaleString('de-AT', {
|
||||||
|
|||||||
@@ -12,28 +12,18 @@ registerPage('profile', {
|
|||||||
var defaultPathInput = document.getElementById('profileDefaultPath');
|
var defaultPathInput = document.getElementById('profileDefaultPath');
|
||||||
var personaInput = document.getElementById('profilePersona');
|
var personaInput = document.getElementById('profilePersona');
|
||||||
var saveBtn = document.getElementById('profileSaveBtn');
|
var saveBtn = document.getElementById('profileSaveBtn');
|
||||||
var messageDiv = document.getElementById('profileMessage');
|
|
||||||
|
|
||||||
// EasyMDE für Persona
|
// EasyMDE für Persona
|
||||||
_profileMde = new EasyMDE({
|
_profileMde = createMDE(personaInput, {
|
||||||
element: personaInput,
|
|
||||||
initialValue: profile.persona || '',
|
initialValue: profile.persona || '',
|
||||||
toolbar: ['bold', 'italic', 'heading', '|', 'unordered-list', 'ordered-list', '|', 'link', 'code', '|', 'preview'],
|
|
||||||
status: false,
|
|
||||||
spellChecker: false,
|
|
||||||
autoDownloadFontAwesome: false,
|
|
||||||
minHeight: '150px',
|
minHeight: '150px',
|
||||||
placeholder: 'Beschreibe dich selbst – Name, Rolle, Präferenzen, Kommunikationsstil...',
|
placeholder: 'Beschreibe dich selbst – Name, Rolle, Präferenzen, Kommunikationsstil...'
|
||||||
previewClass: ['editor-preview', 'markdown']
|
|
||||||
});
|
});
|
||||||
_profileMde.codemirror.setOption('theme', 'default');
|
|
||||||
_profileMde.codemirror.getWrapperElement().closest('.EasyMDEContainer').classList.add('fe-mde-wrap');
|
|
||||||
|
|
||||||
var currentPwInput = document.getElementById('profileCurrentPassword');
|
var currentPwInput = document.getElementById('profileCurrentPassword');
|
||||||
var newPwInput = document.getElementById('profileNewPassword');
|
var newPwInput = document.getElementById('profileNewPassword');
|
||||||
var confirmPwInput = document.getElementById('profileConfirmPassword');
|
var confirmPwInput = document.getElementById('profileConfirmPassword');
|
||||||
var passwordBtn = document.getElementById('profilePasswordBtn');
|
var passwordBtn = document.getElementById('profilePasswordBtn');
|
||||||
var passwordMessageDiv = document.getElementById('profilePasswordMessage');
|
|
||||||
|
|
||||||
var logoutBtn = document.getElementById('profileLogoutBtn');
|
var logoutBtn = document.getElementById('profileLogoutBtn');
|
||||||
|
|
||||||
@@ -46,7 +36,6 @@ registerPage('profile', {
|
|||||||
saveBtn.addEventListener('click', async function() {
|
saveBtn.addEventListener('click', async function() {
|
||||||
saveBtn.disabled = true;
|
saveBtn.disabled = true;
|
||||||
saveBtn.textContent = 'Speichern...';
|
saveBtn.textContent = 'Speichern...';
|
||||||
messageDiv.hidden = true;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var updated = await apiUpdateProfile({
|
var updated = await apiUpdateProfile({
|
||||||
@@ -59,14 +48,9 @@ registerPage('profile', {
|
|||||||
currentUser.displayName = updated.displayName;
|
currentUser.displayName = updated.displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
messageDiv.textContent = 'Profil gespeichert';
|
showToast('Profil gespeichert');
|
||||||
messageDiv.className = 'profile-message profile-message-success';
|
|
||||||
messageDiv.hidden = false;
|
|
||||||
setTimeout(function() { messageDiv.hidden = true; }, 3000);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
messageDiv.textContent = err.message;
|
showToast(err.message, 'error');
|
||||||
messageDiv.className = 'profile-message profile-message-error';
|
|
||||||
messageDiv.hidden = false;
|
|
||||||
} finally {
|
} finally {
|
||||||
saveBtn.disabled = false;
|
saveBtn.disabled = false;
|
||||||
saveBtn.textContent = 'Speichern';
|
saveBtn.textContent = 'Speichern';
|
||||||
@@ -75,23 +59,17 @@ registerPage('profile', {
|
|||||||
|
|
||||||
// Passwort ändern
|
// Passwort ändern
|
||||||
passwordBtn.addEventListener('click', async function() {
|
passwordBtn.addEventListener('click', async function() {
|
||||||
passwordMessageDiv.hidden = true;
|
|
||||||
|
|
||||||
var currentPw = currentPwInput.value;
|
var currentPw = currentPwInput.value;
|
||||||
var newPw = newPwInput.value;
|
var newPw = newPwInput.value;
|
||||||
var confirmPw = confirmPwInput.value;
|
var confirmPw = confirmPwInput.value;
|
||||||
|
|
||||||
if (!currentPw || !newPw) {
|
if (!currentPw || !newPw) {
|
||||||
passwordMessageDiv.textContent = 'Alle Felder ausfüllen';
|
showToast('Alle Felder ausfüllen', 'error');
|
||||||
passwordMessageDiv.className = 'profile-message profile-message-error';
|
|
||||||
passwordMessageDiv.hidden = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newPw !== confirmPw) {
|
if (newPw !== confirmPw) {
|
||||||
passwordMessageDiv.textContent = 'Neue Passwörter stimmen nicht überein';
|
showToast('Neue Passwörter stimmen nicht überein', 'error');
|
||||||
passwordMessageDiv.className = 'profile-message profile-message-error';
|
|
||||||
passwordMessageDiv.hidden = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,14 +83,9 @@ registerPage('profile', {
|
|||||||
newPwInput.value = '';
|
newPwInput.value = '';
|
||||||
confirmPwInput.value = '';
|
confirmPwInput.value = '';
|
||||||
|
|
||||||
passwordMessageDiv.textContent = 'Passwort geändert';
|
showToast('Passwort geändert');
|
||||||
passwordMessageDiv.className = 'profile-message profile-message-success';
|
|
||||||
passwordMessageDiv.hidden = false;
|
|
||||||
setTimeout(function() { passwordMessageDiv.hidden = true; }, 3000);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
passwordMessageDiv.textContent = err.message;
|
showToast(err.message, 'error');
|
||||||
passwordMessageDiv.className = 'profile-message profile-message-error';
|
|
||||||
passwordMessageDiv.hidden = false;
|
|
||||||
} finally {
|
} finally {
|
||||||
passwordBtn.disabled = false;
|
passwordBtn.disabled = false;
|
||||||
passwordBtn.textContent = 'Passwort ändern';
|
passwordBtn.textContent = 'Passwort ändern';
|
||||||
|
|||||||
@@ -61,19 +61,10 @@ registerPage('session', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EasyMDE für Kontext
|
// EasyMDE für Kontext
|
||||||
_sessionContextMde = new EasyMDE({
|
_sessionContextMde = createMDE(contextEl, {
|
||||||
element: contextEl,
|
|
||||||
initialValue: (session && session.context) || '',
|
initialValue: (session && session.context) || '',
|
||||||
toolbar: ['bold', 'italic', 'heading', '|', 'unordered-list', 'ordered-list', '|', 'link', 'code', '|', 'preview'],
|
placeholder: 'Zusätzlicher Kontext für den System-Prompt (optional)'
|
||||||
status: false,
|
|
||||||
spellChecker: false,
|
|
||||||
autoDownloadFontAwesome: false,
|
|
||||||
minHeight: '120px',
|
|
||||||
placeholder: 'Zusätzlicher Kontext für den System-Prompt (optional)',
|
|
||||||
previewClass: ['editor-preview', 'markdown']
|
|
||||||
});
|
});
|
||||||
_sessionContextMde.codemirror.setOption('theme', 'default');
|
|
||||||
_sessionContextMde.codemirror.getWrapperElement().closest('.EasyMDEContainer').classList.add('fe-mde-wrap');
|
|
||||||
|
|
||||||
// Befehl hinzufügen
|
// Befehl hinzufügen
|
||||||
addCommandBtn.addEventListener('click', () => createCommandRow(commandsList));
|
addCommandBtn.addEventListener('click', () => createCommandRow(commandsList));
|
||||||
@@ -277,61 +268,13 @@ registerPage('session', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function openAgentPickerModal(agents, agentSkillsMap, currentId, onSelect) {
|
function openAgentPickerModal(agents, agentSkillsMap, currentId, onSelect) {
|
||||||
// Overlay
|
const items = agents.map(a => {
|
||||||
const overlay = document.createElement('div');
|
a._pickerId = a.agentId;
|
||||||
overlay.className = 'agent-picker-overlay';
|
return a;
|
||||||
|
|
||||||
const modal = document.createElement('div');
|
|
||||||
modal.className = 'agent-picker-modal';
|
|
||||||
|
|
||||||
// Header
|
|
||||||
const header = document.createElement('div');
|
|
||||||
header.className = 'agent-picker-header';
|
|
||||||
header.innerHTML = '<span>Agent auswählen</span><button class="icon-btn agent-picker-close"><i class="fa fa-times"></i></button>';
|
|
||||||
modal.appendChild(header);
|
|
||||||
|
|
||||||
// Body
|
|
||||||
const body = document.createElement('div');
|
|
||||||
body.className = 'agent-picker-body';
|
|
||||||
|
|
||||||
// "Kein Agent" Option
|
|
||||||
const noneCard = document.createElement('div');
|
|
||||||
noneCard.className = 'card agent-picker-item' + (!currentId ? ' selected' : '');
|
|
||||||
noneCard.innerHTML = `
|
|
||||||
<div class="card-image" style="display:flex;align-items:center;justify-content:center"><i class="fa fa-ban" style="font-size:2em;opacity:var(--semi-transparent)"></i></div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="card-title-row">
|
|
||||||
<span class="card-title">Kein Agent</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
noneCard.addEventListener('click', () => {
|
|
||||||
onSelect(null);
|
|
||||||
overlay.remove();
|
|
||||||
});
|
});
|
||||||
body.appendChild(noneCard);
|
_openPickerModal('Agent auswählen', items, currentId, onSelect, {
|
||||||
|
noneLabel: 'Kein Agent',
|
||||||
// Agent Cards via Helper
|
buildCard: (a) => buildAgentCard(a, agentSkillsMap[a.agentId] || [])
|
||||||
for (const a of agents) {
|
|
||||||
const skills = agentSkillsMap[a.agentId] || [];
|
|
||||||
const item = buildAgentCard(a, skills);
|
|
||||||
item.classList.add('agent-picker-item');
|
|
||||||
if (a.agentId === currentId) item.classList.add('selected');
|
|
||||||
item.addEventListener('click', () => {
|
|
||||||
onSelect(a.agentId);
|
|
||||||
overlay.remove();
|
|
||||||
});
|
|
||||||
body.appendChild(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
modal.appendChild(body);
|
|
||||||
overlay.appendChild(modal);
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
// Schließen
|
|
||||||
header.querySelector('.agent-picker-close').addEventListener('click', () => overlay.remove());
|
|
||||||
overlay.addEventListener('click', (e) => {
|
|
||||||
if (e.target === overlay) overlay.remove();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-18
@@ -2,23 +2,6 @@
|
|||||||
|
|
||||||
let pendingReply = null; // { index, role, text } - Zitierte Nachricht
|
let pendingReply = null; // { index, role, text } - Zitierte Nachricht
|
||||||
|
|
||||||
function _formatReplyTimestamp(ts) {
|
|
||||||
if (!ts) return 'Jetzt';
|
|
||||||
try {
|
|
||||||
const d = new Date(ts);
|
|
||||||
const now = new Date();
|
|
||||||
const hh = d.getHours().toString().padStart(2, '0');
|
|
||||||
const mm = d.getMinutes().toString().padStart(2, '0');
|
|
||||||
// Gleiches Datum → nur Uhrzeit, sonst Datum + Uhrzeit
|
|
||||||
if (d.toDateString() === now.toDateString()) {
|
|
||||||
return hh + ':' + mm;
|
|
||||||
}
|
|
||||||
const dd = d.getDate().toString().padStart(2, '0');
|
|
||||||
const mo = (d.getMonth() + 1).toString().padStart(2, '0');
|
|
||||||
return dd + '.' + mo + '. ' + hh + ':' + mm;
|
|
||||||
} catch { return 'Jetzt'; }
|
|
||||||
}
|
|
||||||
|
|
||||||
function setReply(reply) {
|
function setReply(reply) {
|
||||||
pendingReply = reply;
|
pendingReply = reply;
|
||||||
renderReplyPreview();
|
renderReplyPreview();
|
||||||
@@ -41,7 +24,7 @@ function renderReplyPreview() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const roleLabel = pendingReply.role === 'user' ? 'User' : 'Assistant';
|
const roleLabel = pendingReply.role === 'user' ? 'User' : 'Assistant';
|
||||||
const timeLabel = _formatReplyTimestamp(pendingReply.timestamp);
|
const timeLabel = _formatTimestamp(pendingReply.timestamp) || 'Jetzt';
|
||||||
const preview = truncate(pendingReply.text, 150);
|
const preview = truncate(pendingReply.text, 150);
|
||||||
|
|
||||||
container.innerHTML = `<div class="reply-preview-item">
|
container.innerHTML = `<div class="reply-preview-item">
|
||||||
|
|||||||
@@ -68,6 +68,27 @@ function removeToast(toast) {
|
|||||||
toast.addEventListener('transitionend', function() { toast.remove(); });
|
toast.addEventListener('transitionend', function() { toast.remove(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── EasyMDE Helper ──
|
||||||
|
|
||||||
|
function createMDE(element, options) {
|
||||||
|
options = options || {};
|
||||||
|
const mde = new EasyMDE({
|
||||||
|
element: element,
|
||||||
|
initialValue: options.initialValue || '',
|
||||||
|
toolbar: options.toolbar || ['bold', 'italic', 'heading', '|', 'unordered-list', 'ordered-list', '|', 'link', 'code', '|', 'preview'],
|
||||||
|
status: false,
|
||||||
|
spellChecker: false,
|
||||||
|
autoDownloadFontAwesome: false,
|
||||||
|
autofocus: options.autofocus || false,
|
||||||
|
minHeight: options.minHeight || '120px',
|
||||||
|
placeholder: options.placeholder || '',
|
||||||
|
previewClass: ['editor-preview', 'markdown']
|
||||||
|
});
|
||||||
|
mde.codemirror.setOption('theme', 'default');
|
||||||
|
mde.codemirror.getWrapperElement().closest('.EasyMDEContainer').classList.add('fe-mde-wrap');
|
||||||
|
return mde;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Meta Bar ──
|
// ── Meta Bar ──
|
||||||
|
|
||||||
function setMeta(text) {
|
function setMeta(text) {
|
||||||
|
|||||||
Reference in new issue
Block a user