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(){});
|
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-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-sub{font-size:12px;color:var(--text3);margin-top:1px;font-family:'DM Mono',monospace}
|
||||||
.hdr-btns{display:flex;gap:8px;align-items:center}
|
.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{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-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}
|
.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)]
|
#[Route('/{path}', name: 'app', requirements: ['path' => '.*'], priority: -10)]
|
||||||
public function index(): Response
|
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="mobile-web-app-capable" content="yes"/>
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
||||||
<title>Dudi</title>
|
<title>Dudi</title>
|
||||||
<link rel="stylesheet" href="style.css"/>
|
<link rel="stylesheet" href="style.css?v={{ cssv }}"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="main-wrap">
|
<div class="main-wrap">
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
<img src="logo.png" alt="Dudi" class="hdr-logo"/>
|
<img src="logo.png" alt="Dudi" class="hdr-logo"/>
|
||||||
<div class="hdr-sub" id="tlbl"></div>
|
<div class="hdr-sub" id="tlbl"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<span id="sw" class="sw">0.00s</span>
|
||||||
<div class="hdr-btns">
|
<div class="hdr-btns">
|
||||||
<button class="btn-menu" id="btnData">⋯</button>
|
<button class="btn-menu" id="btnData">⋯</button>
|
||||||
<button class="btn-add" id="btnNew">+</button>
|
<button class="btn-add" id="btnNew">+</button>
|
||||||
|
|
@ -324,6 +325,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="app.js"></script>
|
<script src="app.js?v={{ jsv }}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue