Files
gott/wwwroot/js/modals.js
T
Meleki 1a2c407538 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
2026-03-07 21:50:23 +01:00

184 lines
6.3 KiB
JavaScript

// ── 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 ──
function _renderSystemPromptBlocks(container, blocks) {
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
if (!block.content) continue;
var wrapper = document.createElement('div');
wrapper.className = 'sp-block';
var label = document.createElement('div');
label.className = 'sp-block-label';
label.innerHTML = '<i class="fa ' + esc(block.icon || 'fa-cog') + '"></i> ' + esc(block.label || 'Block');
wrapper.appendChild(label);
var content = document.createElement('div');
content.className = 'sp-block-content markdown';
content.innerHTML = renderMarkdown(block.content);
parseFluentEmojis(content);
wrapper.appendChild(content);
container.appendChild(wrapper);
}
}
async function showSystemPromptModal(sessionId) {
const { body } = _createModal('System-Prompt', { body: 'markdown' });
body.innerHTML = '<div class="sp-loading">Laden...</div>';
try {
const blocks = await apiGetSystemPromptBlocks(sessionId, typeof _planModeActive !== 'undefined' && _planModeActive);
body.innerHTML = '';
_renderSystemPromptBlocks(body, blocks);
} catch (e) {
body.innerHTML = '<div class="sp-loading" style="color:var(--dangerous)">Fehler beim Laden des System-Prompts</div>';
}
}
// ── Command Output Modal ──
function showCommandOutputModal(name, command, result) {
const { body } = _createModal(name, { modal: 'cmd-output-modal' });
const exitCode = result.exitCode != null ? result.exitCode : '?';
const isError = exitCode !== 0;
const statusText = result.status === 'timeout' ? 'Timeout' : `Exit ${exitCode}`;
const meta = document.createElement('div');
meta.className = 'cmd-output-meta';
meta.innerHTML = `<code>${esc(command)}</code> <span class="cmd-exit-code${isError ? ' cmd-exit-error' : ''}">${esc(statusText)}</span>`;
body.appendChild(meta);
const output = document.createElement('pre');
output.className = 'cmd-output-content';
output.textContent = result.output || '(kein Output)';
body.appendChild(output);
}
// ── Krea Image Preview Modal ──
function showKreaImageModal(titleText, fullUrl) {
const { body } = _createModal(titleText, { overlay: 'krea-overlay', modal: 'krea-modal', body: 'krea-modal-body' });
var img = document.createElement('img');
img.src = fullUrl;
img.className = 'krea-modal-image';
img.alt = titleText;
img.addEventListener('error', function() {
body.innerHTML = '<div class="sp-loading" style="color:var(--dangerous)">Bild konnte nicht geladen werden</div>';
});
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);
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);
overlay.appendChild(modal);
document.body.appendChild(overlay);
header.querySelector('.agent-picker-close').addEventListener('click', () => overlay.remove());
overlay.addEventListener('click', (e) => {
if (e.target === overlay) overlay.remove();
});
}
// Event-Delegation für Krea-Thumbnails im Chat
document.addEventListener('click', function(e) {
var ref = e.target.closest('.krea-ref');
if (ref) {
var fullUrl = ref.dataset.fullurl;
var filename = ref.dataset.filename;
if (fullUrl) showKreaImageModal(filename || 'Bild', fullUrl);
}
});