prompt_template/web/js/templates.js

109 lines
3.6 KiB
JavaScript

/**
* Template-Rendering und Filterung.
*
* Rendert die Template-Karte und wendet Filter (Typ, Suche) an.
*/
let currentEditTemplate = null;
/**
* Render Template-Karten in den Container
* @param {Array} templates - Zu rendernde Templates
*/
function renderTemplates(templates) {
const container = document.getElementById('templates');
const count = document.getElementById('count');
count.textContent = `${templates.length} Template(s)`;
if (templates.length === 0) {
container.innerHTML = '<div class="empty-state"><h3>Keine Templates gefunden</h3><p>Füge Templates in den templates/ Ordnern hinzu.</p></div>';
return;
}
container.innerHTML = templates.map(t => `
<div class="template-item">
<h3>${esc(t.name)}</h3>
<div class="meta">
<span>🏷️ ${esc(t.type)}</span>
<span>📄 ${esc(t.format)}</span>
<span>📌 v${esc(t.version)}</span>
</div>
<p>${esc(t.description) || 'Keine Beschreibung'}</p>
<div class="tags">
${t.tags.map(tag => `<span class="tag">${esc(tag)}</span>`).join('')}
</div>
<div class="actions">
<button class="btn btn-icon btn-view" data-path="${t.path}" aria-label="Anzeigen: ${esc(t.name)}">Anzeigen</button>
<button class="btn btn-icon btn-edit" data-path="${t.path}" aria-label="Bearbeiten: ${esc(t.name)}">📝 Bearbeiten</button>
<button class="btn btn-icon btn-copy" data-path="${t.path}" aria-label="Inhalt kopieren: ${esc(t.name)}">Inhalt kopieren</button>
<button class="btn btn-icon btn-tune" data-path="${t.path}" aria-label="Tunen: ${esc(t.name)}">🎯 Tunen</button>
</div>
</div>
`).join('');
}
/**
* Filter-State: aktueller Typ-Filter
*/
let currentType = null;
let currentQuery = '';
/**
* Extrahiere Typ aus URL-Hash
* @returns {string|null} Typ-Filter oder null
*/
function parseTypeFromHash() {
const match = window.location.hash.match(/[?&]type=([^&]+)/);
return match ? decodeURIComponent(match[1]) : null;
}
/**
* Setze Nav-Active-State basierend auf Typ
* @param {string|null} type - Typ-Filter
*/
function setNavActive(type) {
document.querySelectorAll('.nav a').forEach(a => {
const m = (a.getAttribute('href') || '').match(/type=([^&]+)/);
const aType = m ? m[1] : null;
a.classList.toggle('active', aType === type);
});
}
/**
* Wende Filter an und render Templates
*/
function applyFilters() {
let list = window.allTemplates || [];
if (currentType) {
list = list.filter(t => t.type === currentType);
}
if (currentQuery) {
const q = currentQuery;
list = list.filter(t =>
(t.name || '').toLowerCase().includes(q) ||
(t.description || '').toLowerCase().includes(q) ||
(t.tags || []).some(tag => tag.toLowerCase().includes(q))
);
}
setNavActive(currentType);
renderTemplates(list);
}
// Event delegation: click on template cards routes to action functions
document.addEventListener('click', (e) => {
const btn = e.target.closest('.btn[data-path]');
if (!btn) return;
const card = btn.closest('.template-item');
if (!card) return;
const path = btn.dataset.path;
const action = btn.classList.contains('btn-view') ? 'viewTemplate'
: btn.classList.contains('btn-edit') ? 'editModalContent'
: btn.classList.contains('btn-copy') ? 'copyContent'
: btn.classList.contains('btn-tune') ? 'tuneModalContent' : null;
if (action) window[action](path);
});
// Export for main.js (global scope, loaded before main.js)
// renderTemplates, applyFilters, parseTypeFromHash, setNavActive
// currentType, currentQuery sind als globale Variablen verfügbar