Custom card colors (text/active/inactive), smart automation toggle logic

This commit is contained in:
Richard Sauer 2026-04-09 13:32:17 +10:00
parent b76cb7be9e
commit 61605fb7f2

View File

@ -466,6 +466,14 @@ function createCardEl(c) {
const s = simState[c.id] || {on: false, value: 0}; const s = simState[c.id] || {on: false, value: 0};
if (c.type !== 'temp' && s.on) el.classList.add('on'); if (c.type !== 'temp' && s.on) el.classList.add('on');
// Apply custom colors
if (c.type !== 'temp') {
const onColor = c.activeColor || (c.type === 'burner' ? 'var(--amber)' : 'var(--green)');
const offColor = c.inactiveColor || 'var(--red)';
el.style.background = s.on ? onColor : offColor;
}
if (c.textColor) el.style.color = c.textColor;
// Drag handle (edit mode) // Drag handle (edit mode)
el.innerHTML = `<span class="drag-handle edit-overlay">&#x2630;</span>`; el.innerHTML = `<span class="drag-handle edit-overlay">&#x2630;</span>`;
@ -779,14 +787,28 @@ function editCardProps(id) {
</select> </select>
</div> </div>
<div class="prop-row"> <div class="prop-row">
<label>Color</label> <label>Text Color</label>
<input type="color" id="prop-textcolor" value="${card.textColor||'#e8ecf4'}">
<span class="prop-preview" style="background:${card.textColor||'#e8ecf4'}"></span>
</div>
<div class="prop-row">
<label>ON Color</label>
<input type="color" id="prop-activecolor" value="${card.activeColor||(card.type==='burner'?'#ffab00':'#00c853')}">
<span class="prop-preview" style="background:${card.activeColor||(card.type==='burner'?'#ffab00':'#00c853')}"></span>
</div>
<div class="prop-row">
<label>OFF Color</label>
<input type="color" id="prop-inactivecolor" value="${card.inactiveColor||'#ff1744'}">
<span class="prop-preview" style="background:${card.inactiveColor||'#ff1744'}"></span>
</div>
<div class="prop-row">
<label>Accent Color</label>
<input type="color" id="prop-color" value="${card.color||'#ff8844'}"> <input type="color" id="prop-color" value="${card.color||'#ff8844'}">
<span class="prop-preview" id="prop-color-preview" style="background:${card.color||'#ff8844'}"></span> <span class="prop-preview" id="prop-color-preview" style="background:${card.color||'#ff8844'}"></span>
<button onclick="document.getElementById('prop-color').value='';document.getElementById('prop-color-preview').style.background='var(--card)'" style="padding:6px 10px;border:1px solid var(--border);border-radius:4px;background:var(--card);color:var(--text2);font-size:11px;cursor:pointer">Reset</button>
</div> </div>
<div class="prop-row"> <div class="prop-row">
<label>Font Size</label> <label>Font Size</label>
<input type="range" id="prop-fontsize" min="12" max="36" value="${card.fontSize||18}"> <input type="range" id="prop-fontsize" min="12" max="48" value="${card.fontSize||18}">
<span id="prop-fontsize-val" style="color:var(--text2);min-width:30px">${card.fontSize||18}px</span> <span id="prop-fontsize-val" style="color:var(--text2);min-width:30px">${card.fontSize||18}px</span>
</div> </div>
${extraFields} ${extraFields}
@ -816,6 +838,9 @@ function saveCardProps(id) {
card.width = document.getElementById('prop-width').value; card.width = document.getElementById('prop-width').value;
const color = document.getElementById('prop-color').value; const color = document.getElementById('prop-color').value;
card.color = color || null; card.color = color || null;
card.textColor = document.getElementById('prop-textcolor').value || null;
card.activeColor = document.getElementById('prop-activecolor').value || null;
card.inactiveColor = document.getElementById('prop-inactivecolor').value || null;
card.fontSize = parseInt(document.getElementById('prop-fontsize').value) || 18; card.fontSize = parseInt(document.getElementById('prop-fontsize').value) || 18;
const spEl = document.getElementById('prop-sp'); const spEl = document.getElementById('prop-sp');
@ -850,21 +875,54 @@ function toggleSim(id) {
} }
} }
// If this is an automation card, activate/deactivate all its rules // Automation logic: activating one deactivates others, but shared outputs stay on
if (card && card.type === 'automation' && card.rules) { if (card && card.type === 'automation') {
card.rules.forEach(r => { if (simState[id].on && card.rules) {
if (!r.target) return; // Collect all targets this automation activates
if (!simState[r.target]) simState[r.target] = {on: false, value: 0}; const myTargets = new Set();
if (simState[id].on) { card.rules.forEach(r => { if (r.target) myTargets.add(r.target); });
// Activating: apply all rules
// Deactivate other automation cards, but track what they had on
const allAutoCards = [];
layout.pages.forEach(p => p.cards.forEach(c => {
if (c.type === 'automation' && c.id !== id) allAutoCards.push(c);
}));
allAutoCards.forEach(ac => {
if (simState[ac.id] && simState[ac.id].on) {
// Turn off this automation
simState[ac.id].on = false;
// Turn off its outputs ONLY if not shared with the new automation
(ac.rules || []).forEach(r => {
if (r.target && !myTargets.has(r.target)) {
if (simState[r.target] && r.action !== 'off') simState[r.target].on = false;
}
});
}
});
// Now apply this automation's rules
card.rules.forEach(r => {
if (!r.target) return;
if (!simState[r.target]) simState[r.target] = {on: false, value: 0};
if (r.action === 'on') simState[r.target].on = true; if (r.action === 'on') simState[r.target].on = true;
else if (r.action === 'off') simState[r.target].on = false; else if (r.action === 'off') simState[r.target].on = false;
else if (r.action === 'set') { simState[r.target].on = true; simState[r.target].value = r.value || 0; } else if (r.action === 'set') { simState[r.target].on = true; simState[r.target].value = r.value || 0; }
} else { });
// Deactivating: turn everything off } else if (!simState[id].on && card.rules) {
if (r.action !== 'off') simState[r.target].on = false; // Deactivating: turn off outputs that no other active automation needs
} const otherActiveTargets = new Set();
}); layout.pages.forEach(p => p.cards.forEach(c => {
if (c.type === 'automation' && c.id !== id && simState[c.id] && simState[c.id].on) {
(c.rules || []).forEach(r => { if (r.target) otherActiveTargets.add(r.target); });
}
}));
card.rules.forEach(r => {
if (r.target && !otherActiveTargets.has(r.target) && r.action !== 'off') {
if (simState[r.target]) simState[r.target].on = false;
}
});
}
} }
renderCards(); renderCards();