better editor

This commit is contained in:
2026-04-29 15:03:40 +02:00
parent 3603f52979
commit f054014825
+221 -176
View File
@@ -1,211 +1,256 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>FSDB Editor</title> <title>FSDB Editor</title>
<style> <style>
body { body {
margin: 0; margin: 0;
font-family: sans-serif; font-family: sans-serif;
background: #111; background: #111;
color: #eee; color: #eee;
} }
.container { .controls {
display: flex; padding: 8px 10px;
height: 100vh; border-bottom: 1px solid #333;
} background: #161616;
display: flex;
align-items: center;
gap: 14px;
flex-wrap: wrap;
}
.panel { .picker-group {
flex: 1; display: flex;
padding: 10px; align-items: center;
box-sizing: border-box; gap: 6px;
overflow-y: auto; }
border-right: 1px solid #333;
}
.panel:last-child { .picker-label {
border-right: none; font-size: 12px;
} color: #888;
white-space: nowrap;
}
h2 { .seg-row {
margin-top: 0; display: flex;
font-size: 16px; gap: 3px;
color: #aaa; }
}
.item { .seg-btn {
padding: 6px; font-size: 12px;
margin: 2px 0; padding: 4px 9px;
cursor: pointer; border-radius: 5px;
border-radius: 4px; border: 1px solid #444;
} background: #1e1e1e;
color: #ccc;
cursor: pointer;
white-space: nowrap;
transition: background 0.1s;
}
.item:hover { .seg-btn:hover { background: #2a2a2a; }
background: #222;
}
.dir { .seg-btn.active {
color: #6cf; background: #1a3a5c;
} color: #7ab8f5;
border-color: #2a5a8c;
font-weight: 600;
}
.file { .playlist-display {
color: #ccc; font-family: monospace;
} font-size: 13px;
color: #777;
padding: 4px 8px;
border: 1px solid #333;
border-radius: 5px;
background: #1a1a1a;
}
.controls { .playlist-display span {
padding: 5px; color: #eee;
border-bottom: 1px solid #333; font-weight: 600;
} }
button { .refresh-btn {
margin-right: 5px; font-size: 12px;
background: #222; padding: 4px 10px;
color: #eee; border-radius: 5px;
border: 1px solid #444; border: 1px solid #444;
padding: 5px 8px; background: #222;
cursor: pointer; color: #eee;
} cursor: pointer;
}
button:hover { .refresh-btn:hover { background: #333; }
background: #333;
}
input { .container {
background: #222; display: flex;
color: #eee; height: calc(100vh - 52px);
border: 1px solid #444; }
padding: 4px;
}
</style> .panel {
</head> flex: 1;
<body> padding: 10px;
box-sizing: border-box;
overflow-y: auto;
border-right: 1px solid #333;
}
<div class="controls"> .panel:last-child { border-right: none; }
Playlist:
<input id="playlist" value="default">
<button onclick="refreshPlaylist()">Refresh</button>
</div>
<div class="container"> h2 {
<div class="panel"> margin-top: 0;
<h2>Ref Dir</h2> font-size: 13px;
<div id="ref"></div> color: #777;
</div> font-weight: 500;
}
<div class="panel"> .item {
<h2>Playlist</h2> padding: 5px 7px;
<div id="playlistView"></div> margin: 2px 0;
</div> cursor: pointer;
border-radius: 4px;
font-size: 13px;
}
</div> .item:hover { background: #1e1e1e; }
.dir { color: #6cf; }
.file { color: #ccc; }
</style>
</head>
<body>
<div class="controls">
<div class="picker-group">
<span class="picker-label">Day</span>
<div class="seg-row" id="dayPicker"></div>
</div>
<div class="picker-group">
<span class="picker-label">Part</span>
<div class="seg-row" id="partPicker"></div>
</div>
<div class="playlist-display">playlist: <span id="playlistVal">friday/day</span></div>
<button class="refresh-btn" onclick="refreshPlaylist()">Refresh</button>
</div>
<script> <div class="container">
const ws = new WebSocket("/ws"); <div class="panel">
<h2>Ref Dir</h2>
<div id="ref"></div>
</div>
<div class="panel">
<h2>Playlist</h2>
<div id="playlistView"></div>
</div>
</div>
let currentPlaylist = "default"; <script>
const DAYS = ["monday","tuesday","wednesday","thursday","friday","saturday","sunday"];
const PARTS = ["day","morning","night","late_night"];
ws.onopen = () => { let selectedDay = "friday";
console.log("WS connected"); let selectedPart = "day";
};
ws.onmessage = (ev) => { function buildPicker(containerId, items, getCurrent, setCurrent) {
const msg = JSON.parse(ev.data); const el = document.getElementById(containerId);
el.innerHTML = "";
items.forEach(val => {
const btn = document.createElement("button");
btn.className = "seg-btn" + (getCurrent() === val ? " active" : "");
btn.textContent = val.replace("_", " ");
btn.onclick = () => {
setCurrent(val);
buildPicker(containerId, items, getCurrent, setCurrent);
updateDisplay();
refreshPlaylist();
};
el.appendChild(btn);
});
}
if (msg.event === "state") { function updateDisplay() {
renderRef(msg.data.dirs); document.getElementById("playlistVal").textContent = selectedDay + "/" + selectedPart;
} }
if (msg.event === "fsdb_list") { function getPlaylistName() {
renderPlaylist(msg.data); return selectedDay + "/" + selectedPart;
} }
};
function getPlaylistName() { buildPicker("dayPicker", DAYS, () => selectedDay, v => { selectedDay = v; });
currentPlaylist = document.getElementById("playlist").value || ""; buildPicker("partPicker", PARTS, () => selectedPart, v => { selectedPart = v; });
return currentPlaylist;
}
function renderRef(data) { const ws = new WebSocket("/ws");
const el = document.getElementById("ref");
el.innerHTML = "";
data.dirs.forEach(d => { ws.onopen = () => {
const div = document.createElement("div"); console.log("WS connected");
div.className = "item dir"; refreshPlaylist();
div.textContent = d + "/"; };
div.onclick = () => addDir(d);
el.appendChild(div);
});
data.files.forEach(f => { ws.onmessage = (ev) => {
const div = document.createElement("div"); const msg = JSON.parse(ev.data);
div.className = "item file"; if (msg.event === "state") renderRef(msg.data.dirs);
div.textContent = f; if (msg.event === "fsdb_list") renderPlaylist(msg.data);
div.onclick = () => addFile(f); };
el.appendChild(div);
});
}
function renderPlaylist(data) { function renderRef(data) {
const el = document.getElementById("playlistView"); const el = document.getElementById("ref");
el.innerHTML = ""; el.innerHTML = "";
data.dirs.forEach(d => {
const div = document.createElement("div");
div.className = "item dir";
div.textContent = d + "/";
div.onclick = () => addDir(d);
el.appendChild(div);
});
data.files.forEach(f => {
const div = document.createElement("div");
div.className = "item file";
div.textContent = f;
div.onclick = () => addFile(f);
el.appendChild(div);
});
}
data.dirs.forEach(d => { function renderPlaylist(data) {
const div = document.createElement("div"); const el = document.getElementById("playlistView");
div.className = "item dir"; el.innerHTML = "";
div.textContent = d + "/"; data.dirs.forEach(d => {
div.onclick = () => removeEntry(d); const div = document.createElement("div");
el.appendChild(div); div.className = "item dir";
}); div.textContent = d + "/";
div.onclick = () => removeEntry(d);
el.appendChild(div);
});
data.files.forEach(f => {
const div = document.createElement("div");
div.className = "item file";
div.textContent = f;
div.onclick = () => removeEntry(f);
el.appendChild(div);
});
}
data.files.forEach(f => { function addFile(name) {
const div = document.createElement("div"); ws.send(JSON.stringify({ action: "fsdb_add", name, playlist: getPlaylistName() }));
div.className = "item file"; setTimeout(refreshPlaylist, 100);
div.textContent = f; }
div.onclick = () => removeEntry(f);
el.appendChild(div);
});
}
function addFile(name) { function addDir(name) {
ws.send(JSON.stringify({ ws.send(JSON.stringify({ action: "fsdb_add_dir", name, playlist: getPlaylistName() }));
action: "fsdb_add", setTimeout(refreshPlaylist, 100);
name: name, }
playlist: getPlaylistName()
}));
setTimeout(refreshPlaylist, 100);
}
function addDir(name) { function removeEntry(name) {
ws.send(JSON.stringify({ if (!confirm("Remove " + name + "?")) return;
action: "fsdb_add_dir", ws.send(JSON.stringify({ action: "fsdb_remove", name, playlist: getPlaylistName() }));
name: name, setTimeout(refreshPlaylist, 100);
playlist: getPlaylistName() }
}));
setTimeout(refreshPlaylist, 100);
}
function removeEntry(name) { function refreshPlaylist() {
if (!confirm("Remove " + name + "?")) return; ws.send(JSON.stringify({ action: "fsdb_list", playlist: getPlaylistName() }));
}
ws.send(JSON.stringify({ </script>
action: "fsdb_remove", </body>
name: name, </html>
playlist: getPlaylistName()
}));
setTimeout(refreshPlaylist, 100);
}
function refreshPlaylist() {
ws.send(JSON.stringify({
action: "fsdb_list",
playlist: getPlaylistName()
}));
}
</script>
</body>
</html>