Files
gott/wwwroot/js/ui-helpers.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

148 lines
4.5 KiB
JavaScript

// ── DOM Helpers ──
const $ = (sel) => document.querySelector(sel);
const chatEl = () => $('#chat');
const metaEl = () => $('#meta');
function esc(s) {
const d = document.createElement('div');
d.textContent = s;
return d.innerHTML;
}
function truncate(s, max) {
return s && s.length > max ? s.substring(0, max) + '...' : (s || '');
}
function scrollDown() {
const chat = chatEl();
if (!chat) return;
chat.scrollTop = chat.scrollHeight;
// Nach Font-Swap kann sich die Höhe ändern → nochmal scrollen
document.fonts.ready.then(() => {
chat.scrollTop = chat.scrollHeight;
});
}
function prettyModelName(model) {
if (!model) return '?';
model = model.split('claude-').join('');
if (model == 'sonnet') return 'Sonnet';
if (model == 'sonnet-4-5') return 'Sonnet 4.5';
if (model == 'sonnet-4-6') return 'Sonnet 4.6';
if (model == 'opus') return 'Opus';
if (model == 'opus-4-5') return 'Opus 4.5';
if (model == 'opus-4-6') return 'Opus 4.6';
if (model == 'haiku') return 'Haiku';
if (model == 'haiku-4-5') return 'Haiku 4.5';
return 'Unbekannt (' + model + ')';
}
// ── Toast Notifications ──
function showToast(message, type) {
type = type || 'info';
var container = document.getElementById('toastContainer');
if (!container) {
container = document.createElement('div');
container.id = 'toastContainer';
document.body.appendChild(container);
}
var toast = document.createElement('div');
toast.className = 'toast toast-' + type;
var icon = type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-times-circle' : 'fa-info-circle';
toast.innerHTML = '<i class="fa ' + icon + '"></i> ' + (message || '');
container.appendChild(toast);
// Einblenden
requestAnimationFrame(function() { toast.classList.add('toast-visible'); });
// Automatisch ausblenden
var timeout = setTimeout(function() { removeToast(toast); }, 4000);
toast.addEventListener('click', function() {
clearTimeout(timeout);
removeToast(toast);
});
}
function removeToast(toast) {
toast.classList.remove('toast-visible');
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 ──
function setMeta(text) {
metaEl().textContent = text;
}
function setMetaHtml(html) {
metaEl().innerHTML = html;
}
// ── Older Messages Button ──
function showOlderMessagesButton(remainingCount) {
removeOlderMessagesButton();
const btn = document.createElement('button');
btn.className = 'archive-load-btn older-messages-btn';
btn.innerHTML = '<i class="fa fa-arrow-up"></i> ' + remainingCount + ' ältere Nachrichten laden';
btn.addEventListener('click', () => loadOlderMessages());
chatEl().prepend(btn);
}
function removeOlderMessagesButton() {
const btn = chatEl().querySelector('.older-messages-btn');
if (btn) btn.remove();
}
// ── Archive Button ──
function showArchiveLoadButton() {
const existing = chatEl().querySelector('.archive-load-btn:not(.older-messages-btn)');
if (existing) existing.remove();
const btn = document.createElement('button');
btn.className = 'archive-load-btn';
btn.innerHTML = '<i class="fa fa-archive"></i> Archivierte Nachrichten laden';
btn.addEventListener('click', loadLatestArchive);
// Immer ganz oben im Chat (aber nach dem older-messages-btn falls vorhanden)
const olderBtn = chatEl().querySelector('.older-messages-btn');
if (olderBtn) {
olderBtn.after(btn);
} else {
chatEl().prepend(btn);
}
}
function removeArchiveLoadButton() {
const btns = chatEl().querySelectorAll('.archive-load-btn:not(.older-messages-btn)');
btns.forEach(b => b.remove());
}
// ── Streamed Elements ──
function removeStreamedElements() {
chatEl().querySelectorAll('.streamed').forEach(el => el.remove());
}