Skip to content

Commit 9b5d696

Browse files
committed
UI/Design Fix to led matrix painter
1 parent 0abb584 commit 9b5d696

File tree

2 files changed

+74
-60
lines changed

2 files changed

+74
-60
lines changed

examples/led-matrix-painter/assets/app.js

Lines changed: 68 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ function updateArrowButtonsState() {
140140

141141
function markLoaded(frame){
142142
const oldFrameId = loadedFrameId; // Store the old ID
143-
143+
144144
// Remove marker from the old frame
145145
if(oldFrameId !== null){
146146
const prev = document.querySelector(`#frames [data-id='${oldFrameId}']`);
@@ -149,11 +149,11 @@ function markLoaded(frame){
149149
prev.classList.remove('selected');
150150
}
151151
}
152-
152+
153153
// Update the global state
154154
loadedFrameId = frame ? frame.id : null;
155155
loadedFrame = frame;
156-
156+
157157
// Add marker to the new frame
158158
if(frame && frame.id){
159159
try{
@@ -207,20 +207,20 @@ async function persistFrame(){
207207
// Backend is responsible for naming - send empty if no value
208208
const frameName = (loadedFrame && loadedFrame.name) || '';
209209
const duration_ms = (loadedFrame && loadedFrame.duration_ms) || 1000;
210-
210+
211211
// Build payload with ID if we're updating an existing frame
212212
const payload = {
213213
rows: grid,
214214
name: frameName,
215215
duration_ms: duration_ms,
216216
brightness_levels: BRIGHTNESS_LEVELS
217217
};
218-
218+
219219
if (loadedFrame && loadedFrame.id) {
220220
payload.id = loadedFrame.id;
221221
payload.position = loadedFrame.position;
222222
}
223-
223+
224224
console.debug('[ui] persistFrame (save to DB + update board)', payload);
225225

226226
try {
@@ -278,24 +278,24 @@ async function initEditor(){
278278
headers: {'Content-Type':'application/json'},
279279
body: JSON.stringify({}) // no id = load last or create empty
280280
}, 'json', 'load initial frame');
281-
281+
282282
if (data && data.ok && data.frame) {
283283
const frame = data.frame;
284-
284+
285285
// Populate grid
286286
setGridFromRows(frame.rows || []);
287-
287+
288288
// Populate name input
289289
if (frameTitle) frameTitle.textContent = frame.name || `Frame ${frame.id}`;
290-
290+
291291
// Show C vector representation
292292
if (data.vector) {
293293
showVectorText(data.vector);
294294
}
295-
295+
296296
// Mark as loaded in sidebar
297297
markLoaded(frame);
298-
298+
299299
console.debug('[ui] initEditor loaded frame:', frame.id);
300300
}
301301
} catch (err) {
@@ -343,7 +343,7 @@ function displayFrame(frame) {
343343

344344
// Populate grid
345345
setGridFromRows(frame.rows || []);
346-
346+
347347
// Populate name input
348348
if (frameTitle) frameTitle.textContent = frame.name || `Frame ${frame.id}`;
349349

@@ -353,7 +353,7 @@ function displayFrame(frame) {
353353

354354
async function playAnimation() {
355355
if (!playAnimationBtn) return;
356-
356+
357357
// Stop any previous animation loop
358358
if (animationTimeout) {
359359
clearTimeout(animationTimeout);
@@ -368,20 +368,20 @@ async function playAnimation() {
368368
playAnimationBtn.disabled = false; // re-enable button
369369
return;
370370
}
371-
371+
372372
console.debug(`[ui] playAnimation, frameIds=`, frameIds);
373-
373+
374374
const payload = {
375375
frames: frameIds,
376376
loop: false
377377
};
378-
378+
379379
const data = await fetchWithHandling('/play_animation', {
380380
method: 'POST',
381381
headers: {'Content-Type': 'application/json'},
382382
body: JSON.stringify(payload)
383383
}, 'json', 'play animation');
384-
384+
385385
if (data.error) {
386386
showError('Error: ' + data.error);
387387
playAnimationBtn.disabled = false;
@@ -401,10 +401,10 @@ async function playAnimation() {
401401

402402
const frame = sessionFrames[currentFrameIndex];
403403
displayFrame(frame);
404-
404+
405405
const duration = frame.duration_ms || 1000;
406406
currentFrameIndex++;
407-
407+
408408
animationTimeout = setTimeout(animateNextFrame, duration);
409409
};
410410
animateNextFrame();
@@ -487,7 +487,7 @@ async function refreshFrames(){
487487
const data = await fetchWithHandling('/list_frames', {}, 'json', 'refresh frames');
488488
sessionFrames = data.frames || [];
489489
renderFrames();
490-
490+
491491
// Re-apply loaded state after rendering
492492
if(loadedFrameId !== null && loadedFrame !== null){
493493
const el = document.querySelector(`#frames [data-id='${loadedFrameId}']`);
@@ -507,7 +507,7 @@ function createEditableField(element, onSave) {
507507
const input = document.createElement('input');
508508
input.type = 'text';
509509
input.value = originalValue.replace(/ ms$/, ''); // Remove ' ms' for duration
510-
510+
511511
// Replace element with input
512512
element.style.display = 'none';
513513
element.parentNode.insertBefore(input, element);
@@ -559,7 +559,7 @@ function renderFrames(){
559559
}
560560
const name = document.createElement('div'); name.className = 'frame-name'; name.textContent = f.name || ('Frame ' + f.id);
561561
const duration = document.createElement('div'); duration.className = 'frame-duration'; duration.textContent = `${f.duration_ms || 1000} ms`;
562-
562+
563563
// Make name and duration editable
564564
createEditableField(name, (newName) => {
565565
const rows = (f.id === loadedFrameId) ? collectGridBrightness() : f.rows;
@@ -569,7 +569,7 @@ function renderFrames(){
569569
body: JSON.stringify({ id: f.id, name: newName, duration_ms: f.duration_ms, rows: rows })
570570
}).then(() => refreshFrames());
571571
});
572-
572+
573573
createEditableField(duration, (newDuration) => {
574574
const durationMs = parseInt(newDuration, 10);
575575
if (!isNaN(durationMs)) {
@@ -586,7 +586,7 @@ function renderFrames(){
586586
item.addEventListener('click', (e)=>{
587587
// Don't do anything if clicking inside an input field during editing
588588
if (e.target.tagName === 'INPUT') return;
589-
589+
590590
// If it's already selected, do nothing
591591
if (loadedFrameId === f.id) return;
592592

@@ -616,9 +616,9 @@ function renderFrames(){
616616
await refreshFrames();
617617
}
618618
});
619-
619+
620620
item.appendChild(thumb); item.appendChild(name); item.appendChild(duration);
621-
621+
622622
container.appendChild(item);
623623
});
624624

@@ -684,24 +684,24 @@ async function loadFrameIntoEditor(id){
684684
headers:{'Content-Type':'application/json'},
685685
body: JSON.stringify({id})
686686
}, 'json', `load frame ${id}`);
687-
687+
688688
if(data && data.ok && data.frame){
689689
const f = data.frame;
690-
690+
691691
// Populate grid
692692
setGridFromRows(f.rows || []);
693-
693+
694694
// Populate name input
695695
if(frameTitle) frameTitle.textContent = f.name || `Frame ${f.id}`;
696-
696+
697697
// Mark as loaded in sidebar
698698
markLoaded(f);
699-
699+
700700
// Show C vector representation (backend already sends it via load_frame)
701701
if (data.vector) {
702702
showVectorText(data.vector);
703703
}
704-
704+
705705
console.debug('[ui] loaded frame into editor:', id);
706706
}
707707
} catch(err) {
@@ -734,14 +734,14 @@ async function deleteFrame(id){
734734

735735
async function handleNewFrameClick() {
736736
console.debug('[ui] new frame button clicked');
737-
737+
738738
// Clear editor
739739
cells.forEach(c => { c.classList.remove('on'); delete c.dataset.b; });
740740
showVectorText('');
741-
741+
742742
// Clear loaded frame reference (we're creating new)
743743
clearLoaded();
744-
744+
745745
// Create empty frame in DB (no name = backend assigns progressive name)
746746
const grid = collectGridBrightness(); // all zeros
747747
try {
@@ -755,22 +755,22 @@ async function handleNewFrameClick() {
755755
brightness_levels: BRIGHTNESS_LEVELS
756756
})
757757
}, 'json', 'create new frame');
758-
758+
759759
if (data && data.ok && data.frame) {
760760
// Set name to the backend-assigned name (Frame {id})
761761
if(frameTitle) frameTitle.textContent = data.frame.name || `Frame ${data.frame.id}`;
762-
762+
763763
// Show C vector representation
764764
if (data.vector) {
765765
showVectorText(data.vector);
766766
}
767-
767+
768768
// Refresh frames list
769769
await refreshFrames();
770-
770+
771771
// Mark as loaded
772772
markLoaded(data.frame);
773-
773+
774774
console.debug('[ui] new frame created:', data.frame.id);
775775
}
776776
} catch(err) {
@@ -803,18 +803,20 @@ document.addEventListener('DOMContentLoaded', () => {
803803
if (customSelect) {
804804
const trigger = customSelect.querySelector('.custom-select__trigger');
805805
const options = customSelect.querySelectorAll('.custom-option');
806-
const triggerSvg = trigger.querySelector('svg.tool-icon');
807-
806+
const triggerImg = trigger.querySelector('img.tool-icon');
807+
808808
trigger.addEventListener('click', () => {
809809
customSelect.classList.toggle('open');
810810
});
811811

812812
options.forEach(option => {
813813
option.addEventListener('click', () => {
814814
const value = option.getAttribute('data-value');
815-
const svg = option.querySelector('svg.tool-icon');
816-
817-
triggerSvg.innerHTML = svg.innerHTML;
815+
const img = option.querySelector('img.tool-icon');
816+
817+
if (triggerImg && img) {
818+
triggerImg.src = img.src;
819+
}
818820
customSelect.classList.remove('open');
819821

820822
selectedTool = value;
@@ -835,9 +837,18 @@ document.addEventListener('DOMContentLoaded', () => {
835837
const brightnessAlphaValue = document.getElementById('brightness-alpha-value');
836838

837839
if (brightnessAlphaSlider && brightnessAlphaValue) {
838-
brightnessAlphaSlider.addEventListener('input', () => {
839-
brightnessAlphaValue.textContent = brightnessAlphaSlider.value;
840-
});
840+
// Function to update the slider's background gradient
841+
const updateSliderBackground = () => {
842+
const value = parseInt(brightnessAlphaSlider.value);
843+
const max = parseInt(brightnessAlphaSlider.max);
844+
const percent = (value / max) * 100;
845+
brightnessAlphaSlider.style.setProperty('--slider-value-percent', `${percent}%`);
846+
brightnessAlphaValue.textContent = value;
847+
};
848+
849+
brightnessAlphaSlider.addEventListener('input', updateSliderBackground);
850+
// Call once to set initial state
851+
updateSliderBackground();
841852
}
842853

843854
loadConfig(brightnessAlphaSlider, brightnessAlphaValue);
@@ -897,14 +908,15 @@ if (copyAnimBtn) {
897908
setTimeout(hideError, 3000);
898909
return;
899910
}
900-
911+
901912
try {
902913
const frameToCopy = loadedFrame;
903914
const newFramePayload = {
904915
name: `${frameToCopy.name} (copy)`,
905916
rows: frameToCopy.rows,
906917
duration_ms: frameToCopy.duration_ms,
907-
brightness_levels: frameToCopy.brightness_levels
918+
brightness_levels: frameToCopy.brightness_levels,
919+
position: frameToCopy.position
908920
};
909921
await fetchWithHandling('/persist_frame', {
910922
method: 'POST',
@@ -914,7 +926,7 @@ if (copyAnimBtn) {
914926
} catch (err) {
915927
console.error(`[ui] Failed to copy frame ${loadedFrameId}`, err);
916928
}
917-
929+
918930
await refreshFrames();
919931
});
920932
}
@@ -926,13 +938,13 @@ if (deleteAnimBtn) {
926938
setTimeout(hideError, 3000);
927939
return;
928940
}
929-
941+
930942
const idToDelete = loadedFrameId;
931943
await deleteFrame(idToDelete);
932-
944+
933945
clearLoaded();
934946
await refreshFrames();
935-
947+
936948
const frameToLoad = sessionFrames.find(f => f.id !== idToDelete) || (sessionFrames.length > 0 ? sessionFrames[0] : null);
937949

938950
if (frameToLoad) {
@@ -984,7 +996,7 @@ if (applyDurationBtn) {
984996
body: JSON.stringify(payload)
985997
}, 'json', `update duration for frame ${frame.id}`).catch(err => {
986998
console.error(`[ui] Failed to update duration for frame ${frame.id}`, err);
987-
return Promise.resolve();
999+
return Promise.resolve();
9881000
});
9891001
}
9901002
return Promise.resolve();

0 commit comments

Comments
 (0)