Add stopwatch fill button and asset cache-busting

- Stopwatch ⏱ button appears in add/quick-book rows when sw >= 1s, fills input with floor(seconds)
- AppController passes md5 hashes of app.js/style.css to template for automatic cache-busting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Simon Kühn 2026-04-30 11:00:54 +02:00
parent f0cbe5b5d0
commit a8f6692de4
3 changed files with 23 additions and 6 deletions

View file

@ -715,18 +715,23 @@ document.addEventListener('visibilitychange',function(){
}
});
(function(){
var sw = (function(){
var swEl = document.getElementById('sw');
var state = 0; // 0=stopped, 1=running, 2=paused
var start = 0, elapsed = 0, raf = null;
function fmt(ms){
var s = ms / 1000;
return s.toFixed(2) + 's';
function getMs(){ return state === 1 ? elapsed + (Date.now() - start) : elapsed; }
function updateFillBtns(){
var show = getMs() >= 1000;
document.querySelectorAll('.btn-sw-fill').forEach(function(b){ b.style.display = show ? '' : 'none'; });
}
function fmt(ms){ return (ms / 1000).toFixed(2) + 's'; }
function tick(){
swEl.textContent = fmt(Date.now() - start + elapsed);
updateFillBtns();
raf = requestAnimationFrame(tick);
}
@ -740,12 +745,20 @@ document.addEventListener('visibilitychange',function(){
elapsed += Date.now() - start;
swEl.textContent = fmt(elapsed);
swEl.classList.remove('running');
state = 2;
state = 2; updateFillBtns();
} else {
cancelAnimationFrame(raf);
elapsed = 0; swEl.textContent = '0.00s';
swEl.classList.remove('running');
state = 0;
state = 0; updateFillBtns();
}
});
document.addEventListener('click', function(e){
if(!e.target.classList.contains('btn-sw-fill')) return;
var inp = e.target.closest('.add-row, .qb-row').querySelector('.num-in');
if(inp) inp.value = Math.floor(getMs() / 1000);
});
return { getMs: getMs };
})();

View file

@ -73,6 +73,8 @@ body{font-family:'DM Sans',sans-serif;background:var(--bg);color:var(--text);min
.ulbl{font-size:12px;color:var(--text3)}
.btn-as{flex:1;padding:8px;border-radius:var(--rs);background:var(--blue-bg);color:var(--blue);border:1px solid rgba(37,99,235,.2);cursor:pointer;font-family:'DM Sans',sans-serif;font-size:13px;font-weight:600}
.btn-as:active{opacity:.7}
.btn-sw-fill{flex:none;padding:7px 9px;border-radius:var(--rs);background:var(--bg3);color:var(--text2);border:1px solid var(--border2);cursor:pointer;font-size:13px;line-height:1}
.btn-sw-fill:active{opacity:.7}
.nosets{font-size:12px;color:var(--text3);padding:4px 0 8px}
.card-foot{padding:8px 16px 12px;display:flex;justify-content:flex-end}
.btn-del{background:none;border:none;color:var(--text3);font-size:12px;cursor:pointer;padding:4px 0;font-family:'DM Sans',sans-serif}

View file

@ -55,6 +55,7 @@
<div class="add-row">
<input class="num-in" type="number" min="1"/>
<span class="ulbl"></span>
<button class="btn-sw-fill" style="display:none">⏱</button>
<button class="btn-as">Eintragen</button>
</div>
</template>
@ -74,6 +75,7 @@
<div class="qb-name"></div>
<div class="qb-stat"></div>
<input class="num-in" type="number" min="1"/>
<button class="btn-sw-fill" style="display:none">⏱</button>
<button class="btn-as">Eintragen</button>
</div>
</template>