Web UI polish: thumbnail-hover preview, white thumbnail selection, Themes bullet list; global Scryfall image retry (thumbs+previews) with fallbacks and cache-bust; standardized data-card-name. Deck Summary alignment overhaul (count//name/owned grid, tabular numerals, inset highlight, tooltips, starts under header). Added diagnostics (health + logs pages, error pages, request-id propagation), global HTMX error toasts, and docs updates. Update DOCKER guide and add run-web scripts. Update CHANGELOG and release notes template.
2025-08-27 11:21:46 -07:00
|
|
|
|
{% extends "base.html" %}
|
|
|
|
|
{% block content %}
|
|
|
|
|
<section>
|
|
|
|
|
<h2>Diagnostics</h2>
|
|
|
|
|
<p class="muted">Use these tools to verify error handling surfaces.</p>
|
2025-08-28 16:44:58 -07:00
|
|
|
|
<div class="card" style="background: var(--panel); border:1px solid var(--border); border-radius:10px; padding:.75rem; margin-bottom:.75rem">
|
Web UI polish: thumbnail-hover preview, white thumbnail selection, Themes bullet list; global Scryfall image retry (thumbs+previews) with fallbacks and cache-bust; standardized data-card-name. Deck Summary alignment overhaul (count//name/owned grid, tabular numerals, inset highlight, tooltips, starts under header). Added diagnostics (health + logs pages, error pages, request-id propagation), global HTMX error toasts, and docs updates. Update DOCKER guide and add run-web scripts. Update CHANGELOG and release notes template.
2025-08-27 11:21:46 -07:00
|
|
|
|
<h3 style="margin-top:0">System summary</h3>
|
|
|
|
|
<div id="sysSummary" class="muted">Loading…</div>
|
2025-08-28 16:44:58 -07:00
|
|
|
|
<div id="themeSummary" style="margin-top:.5rem"></div>
|
|
|
|
|
<div style="margin-top:.35rem">
|
|
|
|
|
<button class="btn" id="diag-theme-reset">Reset theme preference</button>
|
|
|
|
|
</div>
|
Web UI polish: thumbnail-hover preview, white thumbnail selection, Themes bullet list; global Scryfall image retry (thumbs+previews) with fallbacks and cache-bust; standardized data-card-name. Deck Summary alignment overhaul (count//name/owned grid, tabular numerals, inset highlight, tooltips, starts under header). Added diagnostics (health + logs pages, error pages, request-id propagation), global HTMX error toasts, and docs updates. Update DOCKER guide and add run-web scripts. Update CHANGELOG and release notes template.
2025-08-27 11:21:46 -07:00
|
|
|
|
</div>
|
2025-08-28 16:44:58 -07:00
|
|
|
|
<div class="card" style="background: var(--panel); border:1px solid var(--border); border-radius:10px; padding:.75rem; margin-bottom:.75rem">
|
2025-08-28 14:57:22 -07:00
|
|
|
|
<h3 style="margin-top:0">Performance (local)</h3>
|
|
|
|
|
<div class="muted" style="margin-bottom:.35rem">Scroll the Step 5 list; this panel shows a rough FPS estimate and virtualization renders.</div>
|
|
|
|
|
<div style="display:flex; gap:1rem; flex-wrap:wrap">
|
|
|
|
|
<div><strong>Scroll FPS:</strong> <span id="perf-fps">–</span></div>
|
|
|
|
|
<div><strong>Visible tiles:</strong> <span id="perf-visible">–</span></div>
|
|
|
|
|
<div><strong>Render count:</strong> <span id="perf-renders">0</span></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-09-01 16:55:24 -07:00
|
|
|
|
<div class="card" style="background: var(--panel); border:1px solid var(--border); border-radius:10px; padding:.75rem; margin-bottom:.75rem">
|
|
|
|
|
<h3 style="margin-top:0">Combos & Synergies (ad-hoc)</h3>
|
|
|
|
|
<div class="muted" style="margin-bottom:.35rem">Paste card names (one per line) and detect two-card combos and synergies using current lists.</div>
|
|
|
|
|
<textarea id="diag-combos-input" rows="6" style="width:100%; resize:vertical; font-family: var(--mono);"></textarea>
|
|
|
|
|
<div style="margin-top:.5rem; display:flex; gap:.5rem; align-items:center">
|
|
|
|
|
<button class="btn" id="diag-combos-run">Detect</button>
|
|
|
|
|
<small class="muted">Runs in diagnostics mode only.</small>
|
|
|
|
|
</div>
|
|
|
|
|
<pre id="diag-combos-out" style="margin-top:.5rem; white-space:pre-wrap"></pre>
|
|
|
|
|
</div>
|
2025-08-28 16:44:58 -07:00
|
|
|
|
{% if enable_pwa %}
|
|
|
|
|
<div class="card" style="background:#0f1115; border:1px solid var(--border); border-radius:10px; padding:.75rem; margin-bottom:.75rem">
|
|
|
|
|
<h3 style="margin-top:0">PWA status</h3>
|
|
|
|
|
<div id="pwaStatus" class="muted">Checking…</div>
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
<div class="card" style="background: var(--panel); border:1px solid var(--border); border-radius:10px; padding:.75rem;">
|
Web UI polish: thumbnail-hover preview, white thumbnail selection, Themes bullet list; global Scryfall image retry (thumbs+previews) with fallbacks and cache-bust; standardized data-card-name. Deck Summary alignment overhaul (count//name/owned grid, tabular numerals, inset highlight, tooltips, starts under header). Added diagnostics (health + logs pages, error pages, request-id propagation), global HTMX error toasts, and docs updates. Update DOCKER guide and add run-web scripts. Update CHANGELOG and release notes template.
2025-08-27 11:21:46 -07:00
|
|
|
|
<h3 style="margin-top:0">Error triggers</h3>
|
|
|
|
|
<div class="row" style="display:flex; gap:.5rem; align-items:center">
|
|
|
|
|
<button class="btn" hx-get="/diagnostics/trigger-error" hx-trigger="click" hx-target="this" hx-swap="none">Trigger HTTP error (418)</button>
|
|
|
|
|
<button class="btn" hx-get="/diagnostics/trigger-error?kind=unhandled" hx-trigger="click" hx-target="this" hx-swap="none">Trigger unhandled error (500)</button>
|
|
|
|
|
<small class="muted">You should see a toast and an inline banner with Request-ID.</small>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{% if show_logs %}
|
|
|
|
|
<p style="margin-top:.75rem"><a class="btn" href="/logs">Open Logs</a></p>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</section>
|
|
|
|
|
<script>
|
|
|
|
|
(function(){
|
|
|
|
|
var el = document.getElementById('sysSummary');
|
|
|
|
|
function render(data){
|
|
|
|
|
if (!el) return;
|
|
|
|
|
try {
|
|
|
|
|
var v = (data && data.version) || 'dev';
|
|
|
|
|
var up = (data && data.uptime_seconds) || 0;
|
|
|
|
|
var st = (data && data.server_time_utc) || '';
|
|
|
|
|
var flags = (data && data.flags) || {};
|
|
|
|
|
el.innerHTML = '<div><strong>Version:</strong> '+String(v)+'</div>'+
|
|
|
|
|
(st ? '<div><strong>Server time (UTC):</strong> '+String(st)+'</div>' : '')+
|
|
|
|
|
'<div><strong>Uptime:</strong> '+String(up)+'s</div>'+
|
|
|
|
|
'<div><strong>Flags:</strong> SHOW_LOGS='+ (flags.SHOW_LOGS? '1':'0') +', SHOW_DIAGNOSTICS='+ (flags.SHOW_DIAGNOSTICS? '1':'0') +', SHOW_SETUP='+ (flags.SHOW_SETUP? '1':'0') +'</div>';
|
|
|
|
|
} catch(_){ el.textContent = 'Unavailable'; }
|
|
|
|
|
}
|
|
|
|
|
function load(){
|
|
|
|
|
try { fetch('/status/sys', { cache: 'no-store' }).then(function(r){ return r.json(); }).then(render).catch(function(){ el.textContent='Unavailable'; }); } catch(_){ el.textContent='Unavailable'; }
|
|
|
|
|
}
|
|
|
|
|
load();
|
2025-08-28 16:44:58 -07:00
|
|
|
|
// Theme status and reset
|
|
|
|
|
try{
|
|
|
|
|
var tEl = document.getElementById('themeSummary');
|
|
|
|
|
var resetBtn = document.getElementById('diag-theme-reset');
|
|
|
|
|
function renderTheme(){
|
|
|
|
|
if (!tEl) return;
|
|
|
|
|
var key = 'mtg:theme';
|
|
|
|
|
var stored = localStorage.getItem(key);
|
|
|
|
|
var html = '';
|
|
|
|
|
var resolved = document.documentElement.getAttribute('data-theme') || '';
|
|
|
|
|
html += '<div><strong>Resolved theme:</strong> ' + resolved + '</div>';
|
|
|
|
|
html += '<div><strong>Preference:</strong> ' + (stored ? stored : '(none)') + '</div>';
|
|
|
|
|
tEl.innerHTML = html;
|
|
|
|
|
}
|
|
|
|
|
renderTheme();
|
|
|
|
|
if (resetBtn){
|
|
|
|
|
resetBtn.addEventListener('click', function(){
|
|
|
|
|
try{ localStorage.removeItem('mtg:theme'); }catch(_){ }
|
|
|
|
|
// Re-apply from server default via base script by simulating system apply
|
|
|
|
|
try{
|
|
|
|
|
var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
|
|
|
var v = prefersDark ? 'dark' : 'light-blend';
|
|
|
|
|
document.documentElement.setAttribute('data-theme', v);
|
|
|
|
|
}catch(_){ }
|
|
|
|
|
renderTheme();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}catch(_){ }
|
2025-09-01 16:55:24 -07:00
|
|
|
|
// Combos & synergies ad-hoc tester
|
|
|
|
|
try{
|
|
|
|
|
var runBtn = document.getElementById('diag-combos-run');
|
|
|
|
|
var ta = document.getElementById('diag-combos-input');
|
|
|
|
|
var out = document.getElementById('diag-combos-out');
|
|
|
|
|
function parseLines(){
|
|
|
|
|
var v = (ta && ta.value) || '';
|
|
|
|
|
return v.split(/\r?\n/).map(function(s){ return s.trim(); }).filter(Boolean);
|
|
|
|
|
}
|
|
|
|
|
async function run(){
|
|
|
|
|
if (!ta || !out) return;
|
|
|
|
|
out.textContent = 'Running…';
|
|
|
|
|
try{
|
|
|
|
|
var resp = await fetch('/diagnostics/combos', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ names: parseLines() })});
|
|
|
|
|
if (!resp.ok){ out.textContent = 'Error '+resp.status; return; }
|
|
|
|
|
var data = await resp.json();
|
|
|
|
|
var lines = [];
|
|
|
|
|
// Versions
|
|
|
|
|
try{
|
|
|
|
|
if (data.versions){
|
|
|
|
|
var vLine = 'List versions: ';
|
|
|
|
|
if (data.versions.combos) vLine += 'combos v'+ String(data.versions.combos);
|
|
|
|
|
if (data.versions.synergies) vLine += (data.versions.combos? ', ' : '') + 'synergies v'+ String(data.versions.synergies);
|
|
|
|
|
lines.push(vLine);
|
|
|
|
|
}
|
|
|
|
|
}catch(_){ }
|
|
|
|
|
lines.push('Combos: '+ data.counts.combos);
|
|
|
|
|
(data.combos||[]).forEach(function(c){
|
|
|
|
|
var badges = [];
|
|
|
|
|
if (c.cheap_early) badges.push('cheap/early');
|
|
|
|
|
if (c.setup_dependent) badges.push('setup-dependent');
|
|
|
|
|
var tagStr = (c.tags && c.tags.length? ' ['+c.tags.join(', ')+']' : '');
|
|
|
|
|
var badgeStr = badges.length ? ' {'+badges.join(', ')+'}' : '';
|
|
|
|
|
lines.push(' - '+c.a+' + '+c.b+ tagStr + badgeStr);
|
|
|
|
|
});
|
|
|
|
|
lines.push('Synergies: '+ data.counts.synergies);
|
|
|
|
|
(data.synergies||[]).forEach(function(s){ lines.push(' - '+s.a+' + '+s.b+(s.tags && s.tags.length? ' ['+s.tags.join(', ')+']':'')); });
|
|
|
|
|
out.textContent = lines.join('\n');
|
|
|
|
|
}catch(e){ out.textContent = 'Failed: '+ (e && e.message? e.message : 'Unknown error'); }
|
|
|
|
|
}
|
|
|
|
|
if (runBtn){ runBtn.addEventListener('click', run); }
|
|
|
|
|
}catch(_){ }
|
2025-08-28 16:44:58 -07:00
|
|
|
|
try{
|
|
|
|
|
var p = document.getElementById('pwaStatus');
|
|
|
|
|
if (p){
|
|
|
|
|
function renderPwa(){
|
|
|
|
|
try{
|
|
|
|
|
var st = window.__pwaStatus || {};
|
|
|
|
|
p.innerHTML = '<div><strong>Registered:</strong> '+ (st.registered? 'Yes':'No') +'</div>' + (st.scope? '<div><strong>Scope:</strong> '+ st.scope +'</div>' : '');
|
|
|
|
|
}catch(_){ p.textContent = 'Unavailable'; }
|
|
|
|
|
}
|
|
|
|
|
setTimeout(renderPwa, 500);
|
|
|
|
|
}
|
|
|
|
|
}catch(_){ }
|
2025-08-28 14:57:22 -07:00
|
|
|
|
// Perf probe: listen to scroll on a card grid if present
|
|
|
|
|
try{
|
|
|
|
|
var fpsEl = document.getElementById('perf-fps');
|
|
|
|
|
var visEl = document.getElementById('perf-visible');
|
|
|
|
|
var rcEl = document.getElementById('perf-renders');
|
|
|
|
|
var grid = document.querySelector('.card-grid');
|
|
|
|
|
var last = performance.now();
|
|
|
|
|
var frames = 0; var renders = 0;
|
|
|
|
|
function tick(){
|
|
|
|
|
frames++;
|
|
|
|
|
var now = performance.now();
|
|
|
|
|
if (now - last >= 500){
|
|
|
|
|
var fps = Math.round((frames * 1000) / (now - last));
|
|
|
|
|
if (fpsEl) fpsEl.textContent = String(fps);
|
|
|
|
|
frames = 0; last = now;
|
|
|
|
|
}
|
|
|
|
|
requestAnimationFrame(tick);
|
|
|
|
|
}
|
|
|
|
|
requestAnimationFrame(tick);
|
|
|
|
|
function updateVisible(){
|
|
|
|
|
try{
|
|
|
|
|
if (!grid) return;
|
|
|
|
|
var tiles = grid.querySelectorAll('.card-tile');
|
|
|
|
|
var c = 0; tiles.forEach(function(t){ if (t.style.display !== 'none') c++; });
|
|
|
|
|
if (visEl) visEl.textContent = String(c);
|
|
|
|
|
}catch(_){ }
|
|
|
|
|
}
|
|
|
|
|
if (grid){
|
|
|
|
|
grid.addEventListener('scroll', updateVisible);
|
|
|
|
|
var mo = new MutationObserver(function(){ renders++; if (rcEl) rcEl.textContent = String(renders); updateVisible(); });
|
|
|
|
|
mo.observe(grid, { childList: true, subtree: true, attributes: false });
|
|
|
|
|
updateVisible();
|
|
|
|
|
}
|
|
|
|
|
}catch(_){ }
|
Web UI polish: thumbnail-hover preview, white thumbnail selection, Themes bullet list; global Scryfall image retry (thumbs+previews) with fallbacks and cache-bust; standardized data-card-name. Deck Summary alignment overhaul (count//name/owned grid, tabular numerals, inset highlight, tooltips, starts under header). Added diagnostics (health + logs pages, error pages, request-id propagation), global HTMX error toasts, and docs updates. Update DOCKER guide and add run-web scripts. Update CHANGELOG and release notes template.
2025-08-27 11:21:46 -07:00
|
|
|
|
})();
|
|
|
|
|
</script>
|
|
|
|
|
{% endblock %}
|