Files
pti-ledy/index.h
T
2026-02-06 11:10:18 +01:00

362 lines
14 KiB
C

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<title>LED Panel Control</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial; text-align: center; margin:0px auto; padding-top: 30px;}
.button {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
textarea {
width: 80%;
height: 200px;
margin-top: 20px;
}
.text-controls {
margin-top: 20px;
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
}
.text-input-group {
margin-bottom: 10px;
}
.param-explanation {
font-size: 0.8em;
color: #666;
}
pre {
text-align: left;
background-color: #f0f0f0;
padding: 10px;
white-space: pre-wrap;
}
#nodes-container {
margin-top: 20px;
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
}
.node-item {
border-bottom: 1px solid #eee;
padding: 5px;
margin-bottom: 5px;
}
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
.modal-content {
background-color: #fefefe;
margin: 15% auto; /* 15% from the top and centered */
padding: 20px;
border: 1px solid #888;
width: 80%; /* Could be more or less, depending on screen size */
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
</style>
</head>
<body>
<h1>LED Panel Control</h1>
<button class="button" onclick="location.href='/upload-page'">Upload Image</button>
<br>
<div>
<label for="brightness">Brightness: </label>
<input type="range" id="brightness" min="0" max="100" value="100" onchange="updateBrightness()">
<span id="brightnessValue">100</span>
</div>
<br>
<div id="nodes-container">
<h2>Current Text Nodes</h2>
<div id="nodes-list"></div>
</div>
<div id="modify-modal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeModal()">&times;</span>
<h3>Modify Text Node</h3>
<div class="text-input-group">
<input type="hidden" id="modifyIdInput">
<b>Node ID:</b> <span id="modifyIdDisplay"></span>
</div>
<div class="text-input-group">
<input type="text" id="modifyTextInput" placeholder="New text...">
<div class="param-explanation">New text for the node.</div>
</div>
<div class="text-input-group">
<input type="color" id="modifyColorPicker">
<div class="param-explanation">New color for the text.</div>
</div>
<div class="text-input-group">
<input type="number" id="modifySlownessInput" placeholder="Slowness" min="0" max="255">
<div class="param-explanation">Animation slowness (0-255).</div>
</div>
<div class="text-buttons">
<button class="button" onclick="modifyText()">Save Changes</button>
</div>
</div>
</div>
<div class="text-controls">
<h3>Add Text Node (Full Control)</h3>
<pre><code>void addNewTextNode(char text[TEXT_MAX_LENGTH + 1], uint32_t color, bool handle_pos_via_cursor = true, short pos_x = 0, short pos_y = 0, unsigned char scroll_slowness = 1, bool is_scrolling = true, bool is_small = true, short disappear_time = -1)</code></pre>
<div class="text-input-group">
<input type="text" id="textInput" placeholder="Enter text...">
<div class="param-explanation">The text to display.</div>
</div>
<div class="text-input-group">
<input type="color" id="textColorPicker" value="#ffffff">
<div class="param-explanation">Color of the text.</div>
</div>
<div class="text-input-group">
<input type="number" id="xInput" placeholder="X position">
<div class="param-explanation">X coordinate of the text.</div>
</div>
<div class="text-input-group">
<input type="number" id="yInput" placeholder="Y position">
<div class="param-explanation">Y coordinate of the text.</div>
</div>
<div class="text-input-group">
<select id="fontSizeInput">
<option value="small">Small</option>
<option value="medium">Medium</option>
</select>
<div class="param-explanation">Font size of the text.</div>
</div>
<div class="text-input-group">
<input type="number" id="slownessInput" placeholder="Slowness" value="2" min="0" max="255">
<div class="param-explanation">Animation slowness (0-255).</div>
</div>
<div class="text-input-group">
<input type="checkbox" id="isRepeatingInput">
<label for="isRepeatingInput">Repeating</label>
<div class="param-explanation">If checked, the text will wrap around the screen.</div>
</div>
<div class="text-buttons">
<button class="button" onclick="sendText()">Add Text</button>
<button class="button" onclick="sendText('top')">Send to Top</button>
<button class="button" onclick="sendText('bottom')">Send to Bottom</button>
</div>
</div>
<div class="text-controls">
<h3>Add Multicolor Text Node</h3>
<pre><code>void addNewMultiColor(char text[TEXT_MAX_LENGTH + 1], RGBWithIndex colors[4], unsigned char color_count, bool handle_pos_via_cursor = true, short pos_x = 0, short pos_y = 0, unsigned char scroll_slowness = 1, bool is_scrolling = true, bool is_small = true, short disappear_time = -1)</code></pre>
<div class="text-input-group">
<input type="text" id="multicolorTextInput" placeholder="Enter text...">
<div class="param-explanation">The text to display.</div>
</div>
<div class="text-input-group">
<input type="text" id="multicolorColorInput" placeholder="e.g., #ff0000,#00ff00,#0000ff">
<div class="param-explanation">Comma-separated list of hex colors (max 4).</div>
</div>
<div class="text-input-group">
<input type="number" id="multicolorXInput" placeholder="X position">
<div class="param-explanation">X coordinate of the text.</div>
</div>
<div class="text-input-group">
<input type="number" id="multicolorYInput" placeholder="Y position">
<div class="param-explanation">Y coordinate of the text.</div>
</div>
<div class="text-input-group">
<select id="multicolorFontSizeInput">
<option value="small">Small</option>
<option value="medium">Medium</option>
</select>
<div class="param-explanation">Font size of the text.</div>
</div>
<div class="text-input-group">
<input type="number" id="multicolorSlownessInput" placeholder="Slowness" value="2" min="0" max="255">
<div class="param-explanation">Animation slowness (0-255).</div>
</div>
<div class="text-input-group">
<input type="checkbox" id="multicolorIsRepeatingInput">
<label for="multicolorIsRepeatingInput">Repeating</label>
<div class="param-explanation">If checked, the text will wrap around the screen.</div>
</div>
<div class="text-buttons">
<button class="button" onclick="sendMulticolorText()">Add Multicolor Text</button>
</div>
</div>
<script>
function updateBrightness() {
var brightness = document.getElementById("brightness").value;
document.getElementById("brightnessValue").innerText = brightness;
var xhr = new XMLHttpRequest();
xhr.open("POST", "/brightness", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("value=" + brightness);
}
function sendText(position) {
const text = document.getElementById('textInput').value;
const color = document.getElementById('textColorPicker').value;
const slowness = document.getElementById('slownessInput').value;
const x = document.getElementById('xInput').value;
const y = document.getElementById('yInput').value;
const fontSize = document.getElementById('fontSizeInput').value;
const isRepeating = document.getElementById('isRepeatingInput').checked;
if (!text) {
alert('Please enter some text.');
return;
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "/text", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
let data = "text=" + encodeURIComponent(text) + "&color=" + encodeURIComponent(color) + "&slowness=" + slowness + "&is_repeating=" + isRepeating;
if(position) {
data += "&position=" + position;
} else {
if(x) data += "&x=" + x;
if(y) data += "&y=" + y;
}
if(fontSize) data += "&fontSize=" + fontSize;
xhr.onload = function() {
if (xhr.status === 200) {
fetchNodes();
}
};
xhr.send(data);
}
function sendMulticolorText() {
const text = document.getElementById('multicolorTextInput').value;
const colors = document.getElementById('multicolorColorInput').value;
const slowness = document.getElementById('multicolorSlownessInput').value;
const x = document.getElementById('multicolorXInput').value;
const y = document.getElementById('multicolorYInput').value;
const fontSize = document.getElementById('multicolorFontSizeInput').value;
const isRepeating = document.getElementById('multicolorIsRepeatingInput').checked;
if (!text) {
alert('Please enter some text.');
return;
}
if (!colors) {
alert('Please enter some colors.');
return;
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "/multicolor-text", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
let data = "text=" + encodeURIComponent(text) + "&colors=" + encodeURIComponent(colors) + "&slowness=" + slowness + "&is_repeating=" + isRepeating;
if(x) data += "&x=" + x;
if(y) data += "&y=" + y;
if(fontSize) data += "&fontSize=" + fontSize;
xhr.onload = function() {
if (xhr.status === 200) {
fetchNodes();
}
};
xhr.send(data);
}
function modifyText() {
const id = document.getElementById('modifyIdInput').value;
const color = document.getElementById('modifyColorPicker').value;
const text = document.getElementById('modifyTextInput').value;
const slowness = document.getElementById('modifySlownessInput').value;
if (!id) {
alert('Please enter a node ID.');
return;
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "/modify-text", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
let data = "id=" + id + "&color=" + encodeURIComponent(color) + "&text=" + encodeURIComponent(text) + "&slowness=" + slowness;
xhr.onload = function() {
if (xhr.status === 200) {
fetchNodes();
closeModal();
}
};
xhr.send(data);
}
function fetchNodes() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/nodes", true);
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var nodes = JSON.parse(this.responseText);
var nodesList = document.getElementById('nodes-list');
nodesList.innerHTML = '';
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
var color = '#' + ('000000' + node.color.toString(16)).slice(-6);
var nodeDiv = document.createElement('div');
nodeDiv.className = 'node-item';
nodeDiv.innerHTML = '<b>ID:</b> ' + node.id + ', <b>Text:</b> ' + node.text + ', <b>Color:</b> <span style="color:' + color + '">' + color + '</span>' +
'<button style="margin-left: 10px;" onclick="populateModifyForm(' + node.id + ', \'' + color + '\', \'' + node.text + '\', ' + node.slowness + ')">Modify</button>';
nodesList.appendChild(nodeDiv);
}
}
};
xhr.send();
}
function populateModifyForm(id, color, text, slowness) {
document.getElementById('modifyIdInput').value = id;
document.getElementById('modifyIdDisplay').innerText = id;
document.getElementById('modifyColorPicker').value = color;
document.getElementById('modifyTextInput').value = text;
document.getElementById('modifySlownessInput').value = slowness;
document.getElementById('modify-modal').style.display = "block";
}
function closeModal() {
document.getElementById('modify-modal').style.display = "none";
}
window.onclick = function(event) {
if (event.target == document.getElementById('modify-modal')) {
closeModal();
}
}
setInterval(fetchNodes, 5000);
window.onload = fetchNodes;
</script>
</body>
</html>
)rawliteral";