fix(ui): move power switch from Dashboard card to header
Wrong placement: a standalone Dashboard card was awkward and easy to
miss depending on layout/scroll position, and didn't fit the
multi-printer case as naturally as the header does - the header
already tracks the active printer via renderPrinterDropdown() (single
name display + dropdown when >1 printer configured), so hooking the
power toggle in there means it always reflects whichever printer is
currently active, including after switching via the dropdown.
Replaces the removed card-power dashboard card with a compact
segmented An/Aus toggle in the header, next to the printer name and
before the status badge - same has_power_control gating and
/kx/printers/{id}/power + power-status endpoints as before, just
relocated and restyled for the header's smaller scale (.h-power-btn
instead of the dashboard-sized .spd-btn, same spd-active highlight
convention for the current state).
Also reverts the DASH_CARD_KEYS/DASH_DEFAULT_LAYOUT change and the
initDashGrid() backward-compat migration for old saved layouts, since
there's no longer a "power" grid card to add.
This commit is contained in:
@ -329,43 +329,46 @@ function initPrinters(){
|
||||
var idx=window._printerIndex||1;
|
||||
_activePrinter=_printers.find(function(p){return String(p.id)===String(idx)})||_printers[0]||null;
|
||||
renderPrinterDropdown();
|
||||
_initDashboardPowerCard();
|
||||
}).catch(function(){});
|
||||
}
|
||||
|
||||
// ── Dashboard Power Switch (Issue: hard to find + poorly visible in the
|
||||
// ── Header Power Switch (Issue: hard to find + poorly visible in the
|
||||
// Printers tab; mirrors togglePrinterPower()/_refreshPrinterPowerIcon() for
|
||||
// the active printer, on a second, more prominent toggle on the Dashboard) ──
|
||||
function _initDashboardPowerCard(){
|
||||
var card=document.getElementById('card-power');
|
||||
if(!card)return;
|
||||
if(!_activePrinter||!_activePrinter.has_power_control){card.style.display='none';return;}
|
||||
card.style.display='';
|
||||
_refreshDashboardPower();
|
||||
// the currently active printer, right next to its name/status in the header
|
||||
// so it works the same in single- and multi-printer setups - switching the
|
||||
// active printer via the dropdown re-evaluates has_power_control for the
|
||||
// newly active one instead of a fixed dashboard card tied to whichever
|
||||
// printer happened to be active on page load) ──
|
||||
function _initHeaderPower(){
|
||||
var wrap=document.getElementById('h-power-wrap');
|
||||
if(!wrap)return;
|
||||
if(!_activePrinter||!_activePrinter.has_power_control){wrap.style.display='none';return;}
|
||||
wrap.style.display='flex';
|
||||
_refreshHeaderPower();
|
||||
}
|
||||
function _refreshDashboardPower(){
|
||||
function _refreshHeaderPower(){
|
||||
if(!_activePrinter||!_activePrinter.has_power_control)return;
|
||||
var bridgeUrl=(_activePrinter.bridge_url||'').replace(/\/+$/,'');
|
||||
fetch(bridgeUrl+'/kx/printers/'+encodeURIComponent(_activePrinter.id)+'/power-status',{signal:AbortSignal.timeout(5000)})
|
||||
.then(function(r){return r.json()})
|
||||
.then(function(d){
|
||||
var onBtn=document.getElementById('d-power-on'), offBtn=document.getElementById('d-power-off');
|
||||
var onBtn=document.getElementById('h-power-on'), offBtn=document.getElementById('h-power-off');
|
||||
if(!onBtn||!offBtn)return;
|
||||
onBtn.classList.toggle('spd-active',d.state==='on');
|
||||
offBtn.classList.toggle('spd-active',d.state==='off');
|
||||
})
|
||||
.catch(function(){/* status endpoint optional - toggle just stays neutral */});
|
||||
}
|
||||
function setDashboardPower(action){
|
||||
function setHeaderPower(action){
|
||||
if(!_activePrinter)return;
|
||||
if(action==='off'&&!confirm(T.printers_power_off_confirm||'Turn printer power off? Make sure no print is running.'))return;
|
||||
var onBtn=document.getElementById('d-power-on'), offBtn=document.getElementById('d-power-off');
|
||||
var onBtn=document.getElementById('h-power-on'), offBtn=document.getElementById('h-power-off');
|
||||
if(onBtn)onBtn.style.opacity='0.5';
|
||||
if(offBtn)offBtn.style.opacity='0.5';
|
||||
post('/kx/printers/'+encodeURIComponent(_activePrinter.id)+'/power',{action:action}).then(function(){
|
||||
if(onBtn)onBtn.style.opacity='1';
|
||||
if(offBtn)offBtn.style.opacity='1';
|
||||
setTimeout(_refreshDashboardPower,1500);
|
||||
setTimeout(_refreshHeaderPower,1500);
|
||||
}).catch(function(e){
|
||||
if(onBtn)onBtn.style.opacity='1';
|
||||
if(offBtn)offBtn.style.opacity='1';
|
||||
@ -395,6 +398,7 @@ function renderPrinterDropdown(){
|
||||
if(wrap)wrap.style.display='none';
|
||||
if(single)single.style.display='';
|
||||
}
|
||||
_initHeaderPower();
|
||||
}
|
||||
function togglePrinterDropdown(){
|
||||
var menu=document.getElementById('printer-dropdown-menu');
|
||||
@ -460,9 +464,8 @@ function applyLang(){
|
||||
setText('d-card-temps',T.card_temps);
|
||||
setText('d-card-lightfan',T.card_light_fan);
|
||||
setText('d-card-speed',T.card_speed);
|
||||
setText('d-card-power',T.card_power);
|
||||
setText('d-power-lbl-on',T.printers_power_on_short||'An');
|
||||
setText('d-power-lbl-off',T.printers_power_off_short||'Aus');
|
||||
setText('h-power-lbl-on',T.printers_power_on_short||'An');
|
||||
setText('h-power-lbl-off',T.printers_power_off_short||'Aus');
|
||||
setText('d-card-cam',T.card_cam);
|
||||
setText('d-card-ams',T.panel_ams_title);
|
||||
setText('d-lbl-elapsed',T.lbl_elapsed);
|
||||
@ -2137,7 +2140,7 @@ var pollTimer;
|
||||
}).catch(function(){});
|
||||
poll();pollTimer=setInterval(poll,ms);
|
||||
setInterval(_loadSpoolmanStatus,30000);
|
||||
setInterval(_refreshDashboardPower,30000);
|
||||
setInterval(_refreshHeaderPower,30000);
|
||||
// initDashGrid() is called at the very end of the file, after all the
|
||||
// DASH_* var declarations below have executed (they are hoisted but not yet
|
||||
// assigned at this point in the IIFE).
|
||||
@ -2154,7 +2157,7 @@ var pollTimer;
|
||||
// - columnOpts.breakpoints -> 1-column below 700px GRID width (sidebar-aware)
|
||||
var DASH_STORAGE_KEY='dashLayout';
|
||||
var DASH_LAYOUT_VERSION=3; // v1/v2 = older editors; discard those states
|
||||
var DASH_CARD_KEYS=['camera','progress','temps','motion','speed','power','fan','ams'];
|
||||
var DASH_CARD_KEYS=['camera','progress','temps','motion','speed','fan','ams'];
|
||||
// Layout arrays in GridStack save()/load() format. cellHeight=60px.
|
||||
var DASH_DEFAULT_LAYOUT=[
|
||||
{id:'camera', x:0,y:0, w:12,h:7},
|
||||
@ -2162,9 +2165,8 @@ var DASH_DEFAULT_LAYOUT=[
|
||||
{id:'temps', x:0,y:13,w:6, h:7},
|
||||
{id:'motion', x:6,y:13,w:6, h:7},
|
||||
{id:'speed', x:0,y:20,w:6, h:3},
|
||||
{id:'power', x:6,y:20,w:6, h:3},
|
||||
{id:'fan', x:0,y:23,w:6, h:3},
|
||||
{id:'ams', x:0,y:26,w:12,h:4},
|
||||
{id:'fan', x:6,y:20,w:6, h:3},
|
||||
{id:'ams', x:0,y:23,w:12,h:4},
|
||||
];
|
||||
// "Wide desktop" preset (Blaim, Issue #89): big camera left, settings center,
|
||||
// motion right.
|
||||
@ -2242,20 +2244,6 @@ function initDashGrid(){
|
||||
var item=_dashItem(n.id);
|
||||
if(item){_dashGrid.removeWidget(item,false);item.style.display='none';}
|
||||
});
|
||||
// Cards added after a user's layout was already saved (e.g. the "power"
|
||||
// card) never appear otherwise: load(...,false) only updates ids already
|
||||
// present in the saved layout, it never adds new ones. Append any missing
|
||||
// known card next to its default-layout neighbour instead of silently
|
||||
// dropping it - the alternative (bumping DASH_LAYOUT_VERSION) would wipe
|
||||
// every user's custom layout just to surface one new card.
|
||||
var known={};
|
||||
state.layout.forEach(function(n){known[n.id]=true;});
|
||||
_dashHidden.forEach(function(n){known[n.id]=true;});
|
||||
DASH_DEFAULT_LAYOUT.forEach(function(def){
|
||||
if(known[def.id])return;
|
||||
var item=_dashItem(def.id);
|
||||
if(item)_dashGrid.addWidget(item,{x:def.x,y:def.y,w:def.w,h:def.h});
|
||||
});
|
||||
}else{
|
||||
_dashGrid.load(DASH_DEFAULT_LAYOUT,false);
|
||||
}
|
||||
|
||||
@ -35,6 +35,10 @@
|
||||
</div>
|
||||
<div id="h-pname-single" class="hname">Anycubic Kobra X</div>
|
||||
<span id="h-version" style="font-size:11px;opacity:.5;margin-left:6px"></span>
|
||||
<div id="h-power-wrap" style="display:none;margin-left:10px;gap:2px;background:var(--raised);border:1px solid var(--border);border-radius:6px;padding:2px" title="Steckdose">
|
||||
<button id="h-power-on" class="h-power-btn" onclick="setHeaderPower('on')">🟢 <span id="h-power-lbl-on">An</span></button>
|
||||
<button id="h-power-off" class="h-power-btn" onclick="setHeaderPower('off')">🔴 <span id="h-power-lbl-off">Aus</span></button>
|
||||
</div>
|
||||
<div class="hbadge" id="h-badge"><span class="dot"></span><span id="h-state">Standby</span></div>
|
||||
<button class="theme-btn" onclick="toggleTheme()">☀ / ☾</button>
|
||||
<button class="theme-btn" onclick="showPanel('settings')" id="settings-btn" title="Einstellungen">⚙</button>
|
||||
@ -335,21 +339,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Steckdose (Power Switch) -->
|
||||
<div class="card" id="card-power" data-card="power" gs-w="6" gs-h="3" style="display:none">
|
||||
<div class="card-title"><span>🔌</span> <span id="d-card-power">Steckdose</span></div>
|
||||
<div style="display:flex;gap:8px;margin-top:4px">
|
||||
<button class="spd-btn" id="d-power-on" onclick="setDashboardPower('on')">
|
||||
<span class="spd-icon">🟢</span>
|
||||
<span id="d-power-lbl-on">An</span>
|
||||
</button>
|
||||
<button class="spd-btn" id="d-power-off" onclick="setDashboardPower('off')">
|
||||
<span class="spd-icon">🔴</span>
|
||||
<span id="d-power-lbl-off">Aus</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lüfter -->
|
||||
<div class="card" id="card-fan" data-card="fan" gs-w="6" gs-h="3">
|
||||
<div class="card-title"><span>🌀</span> <span id="d-card-lightfan">Lüfter</span></div>
|
||||
|
||||
@ -202,6 +202,10 @@ main{flex:1;overflow-y:auto;padding:20px}
|
||||
.spd-btn .spd-icon{font-size:22px}
|
||||
.spd-bar{height:4px;border-radius:2px;background:var(--border);margin-top:10px;overflow:hidden}
|
||||
.spd-bar-fill{height:100%;border-radius:2px;background:linear-gradient(90deg,var(--accent2),var(--accent));transition:width .3s}
|
||||
.h-power-btn{border:1.5px solid transparent;background:none;color:var(--txt2);
|
||||
border-radius:5px;padding:3px 8px;font-size:12px;font-weight:600;cursor:pointer;transition:all .15s}
|
||||
.h-power-btn:hover{color:var(--txt)}
|
||||
.h-power-btn.spd-active{border-color:var(--accent);background:rgba(0,200,255,.12);color:var(--accent)}
|
||||
|
||||
/* ── TIME CARDS ── */
|
||||
.time-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;margin-top:8px}
|
||||
|
||||
Reference in New Issue
Block a user