Add header stopwatch and asset cache-busting
- Stopwatch between logo and menu: tap to start, tap to stop, tap to reset - AppController passes md5-based version hash for app.js and style.css to prevent browser caching issues Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
80e418f8b7
commit
f0cbe5b5d0
4 changed files with 45 additions and 3 deletions
|
|
@ -714,3 +714,38 @@ document.addEventListener('visibilitychange',function(){
|
|||
loadGoals().then(function(g){goals=g;render();}).catch(function(){});
|
||||
}
|
||||
});
|
||||
|
||||
(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 tick(){
|
||||
swEl.textContent = fmt(Date.now() - start + elapsed);
|
||||
raf = requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
swEl.addEventListener('click', function(){
|
||||
if(state === 0){
|
||||
start = Date.now(); elapsed = 0;
|
||||
swEl.classList.add('running');
|
||||
state = 1; tick();
|
||||
} else if(state === 1){
|
||||
cancelAnimationFrame(raf);
|
||||
elapsed += Date.now() - start;
|
||||
swEl.textContent = fmt(elapsed);
|
||||
swEl.classList.remove('running');
|
||||
state = 2;
|
||||
} else {
|
||||
cancelAnimationFrame(raf);
|
||||
elapsed = 0; swEl.textContent = '0.00s';
|
||||
swEl.classList.remove('running');
|
||||
state = 0;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ body{font-family:'DM Sans',sans-serif;background:var(--bg);color:var(--text);min
|
|||
.hdr-logo{height:100px;width:auto;mix-blend-mode:multiply;display:block}
|
||||
.hdr-sub{font-size:12px;color:var(--text3);margin-top:1px;font-family:'DM Mono',monospace}
|
||||
.hdr-btns{display:flex;gap:8px;align-items:center}
|
||||
.sw{font-family:'DM Mono',monospace;font-size:15px;color:var(--text3);cursor:pointer;user-select:none;letter-spacing:-.5px;min-width:52px;text-align:center}
|
||||
.sw.running{color:var(--text1)}
|
||||
.btn-add{width:38px;height:38px;border-radius:50%;background:var(--text);color:var(--bg);border:none;cursor:pointer;font-size:22px;display:flex;align-items:center;justify-content:center;font-weight:300;transition:transform .15s}
|
||||
.btn-add:active{transform:scale(.92)}
|
||||
.btn-menu{width:38px;height:38px;border-radius:50%;background:var(--bg3);color:var(--text2);border:1px solid var(--border);cursor:pointer;font-size:20px;display:flex;align-items:center;justify-content:center;transition:transform .15s}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ class AppController extends AbstractController
|
|||
#[Route('/{path}', name: 'app', requirements: ['path' => '.*'], priority: -10)]
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render('app.html.twig');
|
||||
$public = $this->getParameter('kernel.project_dir') . '/public/';
|
||||
return $this->render('app.html.twig', [
|
||||
'jsv' => substr(md5_file($public . 'app.js'), 0, 8),
|
||||
'cssv' => substr(md5_file($public . 'style.css'), 0, 8),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<meta name="mobile-web-app-capable" content="yes"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
||||
<title>Dudi</title>
|
||||
<link rel="stylesheet" href="style.css"/>
|
||||
<link rel="stylesheet" href="style.css?v={{ cssv }}"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-wrap">
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
<img src="logo.png" alt="Dudi" class="hdr-logo"/>
|
||||
<div class="hdr-sub" id="tlbl"></div>
|
||||
</div>
|
||||
<span id="sw" class="sw">0.00s</span>
|
||||
<div class="hdr-btns">
|
||||
<button class="btn-menu" id="btnData">⋯</button>
|
||||
<button class="btn-add" id="btnNew">+</button>
|
||||
|
|
@ -324,6 +325,6 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script src="app.js"></script>
|
||||
<script src="app.js?v={{ jsv }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in a new issue