From fb6453e2e47444a002d42116f35054822e0deffb Mon Sep 17 00:00:00 2001 From: chk <79915315+ChKendel@users.noreply.github.com> Date: Sun, 14 Jun 2026 22:08:02 +0200 Subject: [PATCH] FileBrowser --- public/index.css | 26 ++++++-- public/index.html | 147 ++++++++++++++++++++++++++++------------------ 2 files changed, 110 insertions(+), 63 deletions(-) diff --git a/public/index.css b/public/index.css index cd12e1f..d9bbe48 100644 --- a/public/index.css +++ b/public/index.css @@ -12,15 +12,20 @@ * { box-sizing: border-box; margin: 0; padding: 0; } -html, body { +html { min-height: 100%; - font-family: Arial, sans-serif; - font-size: 14px; - background: linear-gradient(to bottom, #dddddd -20%, var(--bg) 130%); - color: var(--text); } -body { padding: 16px; } +body { + min-height: 100vh; + font-family: Arial, sans-serif; + font-size: 14px; + /* background-attachment: fixed hΓ€lt den Verlauf relativ zum Viewport β€” + verhindert das Wiederholen bei langen Seiten */ + background: linear-gradient(to bottom, #dddddd -20%, var(--bg) 130%) fixed; + color: var(--text); + padding: 16px; +} /* ===== HEADER ===== */ .app-header { @@ -28,6 +33,10 @@ body { padding: 16px; } align-items: baseline; gap: 12px; margin-bottom: 16px; + background: var(--panel); + border: 1px solid var(--border); + border-radius: 8px; + padding: 10px 16px; } .app-header h1 { font-size: 16px; @@ -100,6 +109,11 @@ body { padding: 16px; } background: var(--active); border-color: var(--accent); } +.prog-item.is-selected { + background: #1e293b; + border-color: #475569; +} +.prog-item.is-folder { font-style: italic; } .prog-item .prog-name { flex: 1; font-size: 13px; diff --git a/public/index.html b/public/index.html index 027cb79..46c2dc4 100644 --- a/public/index.html +++ b/public/index.html @@ -23,7 +23,9 @@
- + + +
@@ -84,25 +86,28 @@ // ─── Zustand ───────────────────────────────────────────────────────────────── let currentActiveId = null; + let selectedId = null; // in der Liste ausgewΓ€hltes Item (unabhΓ€ngig vom aktiven Programm) + let selectedType = null; // 'file' | 'folder' // ─── DOM-Referenzen ─────────────────────────────────────────────────────────── - const elProgCount = document.getElementById('prog-count'); - const elProgList = document.getElementById('program-list'); - const elListStatus = document.getElementById('list-status'); - const elActiveName = document.getElementById('active-name'); - const elActiveBar = document.getElementById('active-bar'); - const elAiId = document.getElementById('ai-id'); - const elAiCount = document.getElementById('ai-count'); - const elAiCursor = document.getElementById('ai-cursor'); - const elAiVersion = document.getElementById('ai-version'); - const elLinesBody = document.getElementById('lines-body'); - const elActStatus = document.getElementById('active-status'); - const elBtnFirst = document.getElementById('btn-first'); - const elBtnPrev = document.getElementById('btn-prev'); - const elBtnNext = document.getElementById('btn-next'); - const elBtnLast = document.getElementById('btn-last'); - const elBtnClear = document.getElementById('btn-clear'); - const elBtnDownload = document.getElementById('btn-download'); + const elProgCount = document.getElementById('prog-count'); + const elProgList = document.getElementById('program-list'); + const elListStatus = document.getElementById('list-status'); + const elActiveName = document.getElementById('active-name'); + const elActiveBar = document.getElementById('active-bar'); + const elAiId = document.getElementById('ai-id'); + const elAiCount = document.getElementById('ai-count'); + const elAiCursor = document.getElementById('ai-cursor'); + const elAiVersion = document.getElementById('ai-version'); + const elLinesBody = document.getElementById('lines-body'); + const elActStatus = document.getElementById('active-status'); + const elBtnFirst = document.getElementById('btn-first'); + const elBtnPrev = document.getElementById('btn-prev'); + const elBtnNext = document.getElementById('btn-next'); + const elBtnLast = document.getElementById('btn-last'); + const elBtnClear = document.getElementById('btn-clear'); + const elBtnDownload = document.getElementById('btn-download'); + const elBtnDeleteSelected = document.getElementById('btn-delete-selected'); // ─── API-Helpers ───────────────────────────────────────────────────────────── async function apiFetch(method, path, body) { @@ -137,37 +142,50 @@ return { code: line.slice(0, idx).trim(), ts: fmtTs(line.slice(idx + 1).trim()) }; } + // ─── Selection-State aktualisieren ────────────────────────────────────────── + function setSelected(id, type) { + selectedId = id; + selectedType = type; + // Alle Items neu markieren + elProgList.querySelectorAll('.prog-item').forEach(el => { + el.classList.toggle('is-selected', el.dataset.id === id); + }); + elBtnDeleteSelected.disabled = !id; + } + // ─── Programmliste rendern ─────────────────────────────────────────────────── - function renderProgList(programs, activeId) { - elProgCount.textContent = programs.length; - if (!programs.length) { + function renderProgList(programs, folders, activeId) { + const total = folders.length + programs.length; + elProgCount.textContent = total; + if (!total) { elProgList.innerHTML = 'Keine Programme gefunden'; + elBtnDeleteSelected.disabled = true; return; } - elProgList.innerHTML = programs - .map(p => { - const isActive = p.id === activeId; - return `
- ${esc(p.name)} - ${esc(p.id)} - ${p.lineCount} Z. - -
`; - }).join(''); - // Klick auf Zeile β†’ FLoad (Programm aktivieren) + // Ordner zuerst, dann Dateien + const folderHtml = folders.map(f => ` +
+ πŸ“ ${esc(f.name)} +
`).join(''); + + const fileHtml = programs.map(p => { + const isActive = p.id === activeId; + const isSel = p.id === selectedId; + return `
+ πŸ“„ ${esc(p.name)} + ${esc(p.id)} + ${p.lineCount} Z. +
`; + }).join(''); + + elProgList.innerHTML = folderHtml + fileHtml; + + // Einfachklick β†’ auswΓ€hlen; Doppelklick β†’ Programm laden elProgList.querySelectorAll('.prog-item').forEach(el => { - el.addEventListener('click', e => { - if (e.target.classList.contains('btn-del')) return; - loadProgram(el.dataset.id); - }); - }); - - // Klick auf LΓΆschen-Button - elProgList.querySelectorAll('.btn-del').forEach(btn => { - btn.addEventListener('click', e => { - e.stopPropagation(); - deleteProgram(btn.dataset.id); + el.addEventListener('click', () => setSelected(el.dataset.id, el.dataset.type)); + el.addEventListener('dblclick', () => { + if (el.dataset.type === 'file') loadProgram(el.dataset.id); }); }); } @@ -238,7 +256,7 @@ apiFetch('GET', '/api/programs'), apiFetch('GET', '/api/active'), ]); - renderProgList(programs.programs || [], active.programId); + renderProgList(programs.programs || [], [], active.programId); renderLines(active); setStatus(elListStatus, ''); setStatus(elActStatus, ''); @@ -260,19 +278,6 @@ } } - // ─── Programm lΓΆschen ──────────────────────────────────────────────────────── - async function deleteProgram(id) { - if (!confirm(`Programm '${id}' wirklich lΓΆschen?`)) return; - setStatus(elListStatus, `LΓΆsche '${id}'…`); - try { - await apiFetch('DELETE', `/api/programs/${encodeURIComponent(id)}`); - setStatus(elListStatus, `'${id}' gelΓΆscht`); - await refresh(); - } catch (err) { - setStatus(elListStatus, `Fehler: ${err.message}`, true); - } - } - // ─── Stepping ──────────────────────────────────────────────────────────────── async function step(endpoint) { setStatus(elActStatus, '…'); @@ -298,8 +303,36 @@ } } + // ─── Programm lΓΆschen (aus Toolbar) ───────────────────────────────────────── + async function deleteSelected() { + if (!selectedId) return; + if (selectedType === 'folder') { + alert(`Ordner-LΓΆschung ist noch nicht implementiert.\n('${selectedId}')`); + return; + } + if (!confirm(`Programm '${selectedId}' wirklich lΓΆschen?`)) return; + setStatus(elListStatus, `LΓΆsche '${selectedId}'…`); + try { + await apiFetch('DELETE', `/api/programs/${encodeURIComponent(selectedId)}`); + setStatus(elListStatus, `'${selectedId}' gelΓΆscht`); + selectedId = null; + selectedType = null; + elBtnDeleteSelected.disabled = true; + await refresh(); + } catch (err) { + setStatus(elListStatus, `Fehler: ${err.message}`, true); + } + } + + // ─── Neuen Ordner anlegen ──────────────────────────────────────────────────── + async function createFolder() { + alert('Ordner-Verwaltung ist noch nicht implementiert.\n(kommt in Phase 3 des Roadmaps)'); + } + // ─── Event-Listener ────────────────────────────────────────────────────────── document.getElementById('btn-refresh').addEventListener('click', refresh); + document.getElementById('btn-new-folder').addEventListener('click', createFolder); + elBtnDeleteSelected.addEventListener('click', deleteSelected); elBtnFirst.addEventListener('click', () => step('first')); elBtnPrev .addEventListener('click', () => step('prev')); elBtnNext .addEventListener('click', () => step('next'));