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'));