Files
pixelserver2/html/index.html
2025-07-23 09:33:38 +02:00

446 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Pixelserver Interface</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover"/>
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<style>
/* --- Base Styles & CSS Variables --- */
:root {
--bg-color: #f7fafc;
--card-bg-color: #ffffff;
--text-color: #2d3748;
--header-color: #1a202c;
--muted-text-color: #718096;
--border-color: #e2e8f0;
--input-bg-color: #f7fafc;
--button-primary-bg: #3b82f6;
--button-primary-hover-bg: #2563eb;
--button-secondary-bg: #6b7280;
--button-secondary-hover-bg: #4b5563;
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #111827;
--card-bg-color: #1f2937;
--text-color: #d1d5db;
--header-color: #ffffff;
--muted-text-color: #9ca3af;
--border-color: #374151;
--input-bg-color: #374151;
--button-secondary-bg: #4b5563;
--button-secondary-hover-bg: #374151;
}
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
padding: 1rem;
box-sizing: border-box;
}
/* --- Layout --- */
.main-container {
max-width: 1400px;
margin: 0 auto;
}
.main {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
justify-content: center;
}
.col {
background-color: var(--card-bg-color);
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: var(--shadow-md);
border: 1px solid var(--border-color);
flex: 1 1 320px; /* Flex-grow, flex-shrink, basis */
min-width: 300px;
display: flex;
flex-direction: column;
}
/* --- Typography --- */
h1 {
text-align: center;
color: var(--header-color);
margin-bottom: 2rem;
font-size: 2.25rem;
}
h1 i {
font-style: normal;
color: var(--button-primary-bg);
}
h2 {
font-size: 1.25rem;
color: var(--header-color);
border-bottom: 1px solid var(--border-color);
padding-bottom: 0.5rem;
margin-top: 0;
margin-bottom: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
h3 {
color: var(--header-color);
margin-top: 0.5rem;
margin-bottom: 0;
font-size: 1rem;
}
label {
display: block;
/*margin-top: 0.5rem;*/
font-weight: 500;
}
/* --- Forms & Inputs --- */
form {
display: flex;
flex-direction: column;
gap: 1rem;
flex-grow: 1; /* Allow form to grow */
}
input[type="text"],
input[type="number"],
select {
width: 100%;
padding: 0.75rem;
border-radius: 0.375rem;
border: 1px solid var(--border-color);
background-color: var(--input-bg-color);
color: var(--text-color);
box-sizing: border-box;
font-size: 1rem;
}
input[type="checkbox"] {
/* Hide original checkbox */
display: none;
}
button {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 0.375rem;
background-color: var(--button-primary-bg);
color: white;
font-weight: 600;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.2s ease;
}
button:hover {
background-color: var(--button-primary-hover-bg);
}
button.startstop {
padding: 0.25rem 0.75rem;
font-size: 0.875rem;
font-weight: 500;
background-color: var(--button-secondary-bg);
}
button.startstop:hover {
background-color: var(--button-secondary-hover-bg);
}
textarea {
width: 100%;
box-sizing: border-box;
height: 300px;
background-color: var(--input-bg-color);
color: var(--text-color);
border: 1px solid var(--border-color);
border-radius: 0.375rem;
padding: 0.5rem;
font-family: monospace;
flex-grow: 1;
}
/* --- Helper classes --- */
.just-col {
display: flex;
flex-direction: column;
gap: 1rem;
align-items: stretch;
}
.just-col label {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
#gammaform span {
flex-basis: 4rem;
flex-shrink: 0;
}
/* --- Button Checkbox Styles --- */
.checkbox-button {
display: block;
padding: 0.75rem 1rem;
border: 5px solid var(--border-color);
border-radius: 0.375rem;
text-align: center;
cursor: pointer;
background-color: var(--card-bg-color);
color: var(--text-color);
transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
font-weight: 500;
}
#flipform input[type="checkbox"]:checked + .checkbox-button {
/*background-color: var(--button-primary-bg);*/
/*color: white;*/
border-color: var(--button-primary-bg);
border-width: 5px;
}
</style>
</head>
<body>
<div class="main-container">
<h1>Andreas <i>production-ready</i> Interface</h1>
<div class="main">
<div class="col">
<h2>Kommando</h2>
<form id="in" onSubmit="return request()">
<select id="list"></select>
<input id="args" type="text" placeholder="Argumente..."/>
<button>Ausführen</button>
</form>
</div>
<div class="col">
<h2>Intensität</h2>
<form onSubmit="return setbrightness()">
<input id="brightness" value="1.0" type="number" step="0.1"><br/>
<button>Setzen</button>
</form>
</div>
<div class="col">
<h2>Gamma</h2>
<form id="gammaform" class="just-col" onSubmit="return setgamma()">
<label><span>Rot: </span><input id="gammar" value="2.8" type="number" step="0.05"/></label>
<label><span>Grün: </span><input id="gammag" value="2.65" type="number" step="0.05"/></label>
<label><span>Blau: </span><input id="gammab" value="2.65" type="number" step="0.05"/></label>
<label><span>Weiß: </span><input id="gammaw" value="2.65" type="number" step="0.05"/></label>
<button>Setzen</button>
</form>
</div>
<div class="col">
<h2>Flip</h2>
<form id="flipform" onSubmit="return setFlip()">
<label>
<input id="flipx" type="checkbox"/>
<span class="checkbox-button">Flip X</span>
</label>
<label>
<input id="flipy" type="checkbox"/>
<span class="checkbox-button">Flip Y</span>
</label>
<button>Setzen</button>
</form>
</div>
<div class="col">
<h2>Filter</h2>
<form onSubmit="return setFilter()">
<h3><label for="filtername">Filterimage:</label></h3>
<input id="filtername" value="test" type="text">
<button>Setzen</button>
</form>
<form onSubmit="return setFilterExpr()">
<h3><label for="filterexpr">Filter expression:</label></h3>
<input id="filterexpr" value="0.5+0.25*sin(x/3+t)" type="text">
<button>Setzen</button>
</form>
</div>
<div class="col">
<h2>Crash Log
<button id="crashlogbtn" class="startstop" type="button" onclick="return enableCrashLog()">start</button>
</h2>
<textarea readonly id="crashlogs" style="display: none;"></textarea>
</div>
<div class="col">
<h2>Log
<button id="logbtn" class="startstop" type="button" onclick="return enableLog()">start</button>
</h2>
<textarea readonly id="logs" style="display: none;"></textarea>
</div>
</div>
</div>
<script>
function getRaw(url, callback) {
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200)
if (callback) callback(xhttp.responseText);
};
xhttp.open("GET", url, true);
xhttp.send();
}
function getJSON(url, callback) {
getRaw(url, function (text) {
callback(JSON.parse(text));
});
}
function post(url, data) {
const formdata = new FormData();
for (const key in data)
formdata.append(key, data[key]);
const xhttp = new XMLHttpRequest();
// xhttp.onreadystatechange = function () {
// if (this.readyState === 4 && this.status === 200) {}
// };
xhttp.open("POST", url, true);
xhttp.send(formdata);
}
function populateForm(parameters) {
const list = document.getElementById("list");
for (const i in parameters) {
const app = parameters[i];
const name = app["name"];
const guiname = app["guiname"];
const persistent = app["persistent"];
const element = document.createElement("option");
element.value = name;
element.innerHTML = guiname;
element.dataset.persistent = persistent;
list.appendChild(element);
}
list.onchange = function () {
const app = document.getElementById('list');
const persistent = app.options[app.selectedIndex].dataset.persistent;
if (persistent == "true") {
document.getElementById("args").style.display = "none";
} else {
document.getElementById("args").style.display = "inline";
}
};
list.onchange();
}
function request() {
let app = document.getElementById('list');
let val = app.options[app.selectedIndex].value;
let parameter = document.getElementById('args').value;
post("/apps/start/" + val, {"param": parameter});
return false;
}
function setgamma() {
const r = document.getElementById('gammar').value;
const g = document.getElementById('gammag').value;
const b = document.getElementById('gammab').value;
const w = document.getElementById('gammaw').value;
getRaw(`/setgamma/${r}/${g}/${b}/${w}`);
return false;
}
function setbrightness() {
const i = document.getElementById('brightness').value;
getRaw("/setbrightness/" + i);
return false;
}
function setFlip() {
const x = document.getElementById('flipx').checked;
const y = document.getElementById('flipy').checked;
getRaw("/filter/flipx/" + x);
getRaw("/filter/flipy/" + y);
return false;
}
function setFilter() {
const i = document.getElementById('filtername').value;
getRaw("/filter/img/" + i);
return false;
}
function setFilterExpr() {
const expr = document.getElementById('filterexpr').value;
post("/filter/expr/", {"expr": expr});
return false;
}
function updateLog() {
getRaw("/apps/log", function (text) {
document.getElementById('logs').value = text;
});
}
function updateCrashLog() {
getRaw("/apps/crashlog", function (text) {
document.getElementById('crashlogs').value = text;
});
}
function enableCrashLog() {
const logArea = document.getElementById('crashlogs');
const btn = document.getElementById('crashlogbtn');
const isHidden = logArea.style.display === 'none';
logArea.style.display = isHidden ? 'block' : 'none';
btn.textContent = isHidden ? 'stop' : 'start';
clearInterval(window.crash_interval);
if (isHidden) {
updateCrashLog();
window.crash_interval = setInterval(updateCrashLog, 1000);
}
return false;
}
function enableLog() {
const logArea = document.getElementById('logs');
const btn = document.getElementById('logbtn');
const isHidden = logArea.style.display === 'none';
logArea.style.display = isHidden ? 'block' : 'none';
btn.textContent = isHidden ? 'stop' : 'start';
clearInterval(window.log_interval);
if (isHidden) {
updateLog();
window.log_interval = setInterval(updateLog, 1000);
}
return false;
}
getJSON("/apps/list", populateForm);
</script>
</body>
</html>