diff --git a/guarddog_nexus/web/static/app.js b/guarddog_nexus/web/static/app.js index 00dd8db..ad0e65c 100644 --- a/guarddog_nexus/web/static/app.js +++ b/guarddog_nexus/web/static/app.js @@ -1,14 +1,45 @@ // GuardDog Nexus — shared UI utilities +document.addEventListener('DOMContentLoaded', function () { + document.querySelectorAll('th.sortable').forEach(function (th) { + th.setAttribute('tabindex', '0'); + th.setAttribute('role', 'button'); + }); +}); + +document.addEventListener('keydown', function (e) { + if ((e.key === 'Enter' || e.key === ' ') && e.target.matches('th.sortable')) { + e.preventDefault(); + e.target.click(); + } +}); + function copyCode(btn, codeId) { var el = document.getElementById(codeId); if (!el) return; - navigator.clipboard.writeText(el.textContent).then(function () { - btn.textContent = 'Copied!'; - btn.classList.add('copied'); - setTimeout(function () { - btn.textContent = 'Copy'; - btn.classList.remove('copied'); - }, 2000); - }); + if (navigator.clipboard) { + navigator.clipboard.writeText(el.textContent).then(function () { + showCopied(btn); + }); + } else { + // Fallback for non-HTTPS + var ta = document.createElement('textarea'); + ta.value = el.textContent; + ta.style.position = 'fixed'; + ta.style.left = '-9999px'; + document.body.appendChild(ta); + ta.select(); + document.execCommand('copy'); + document.body.removeChild(ta); + showCopied(btn); + } +} + +function showCopied(btn) { + btn.textContent = 'Copied!'; + btn.classList.add('copied'); + setTimeout(function () { + btn.textContent = 'Copy'; + btn.classList.remove('copied'); + }, 2000); } diff --git a/guarddog_nexus/web/static/style.css b/guarddog_nexus/web/static/style.css index 73c74ff..c2e1d2e 100644 --- a/guarddog_nexus/web/static/style.css +++ b/guarddog_nexus/web/static/style.css @@ -1,5 +1,7 @@ /* GuardDog Nexus — Web UI styles */ +html { color-scheme: dark; } + /* ------------------------------------------------------------------ */ /* Status / severity colours */ /* ------------------------------------------------------------------ */ @@ -35,10 +37,16 @@ /* ------------------------------------------------------------------ */ /* Tables */ /* ------------------------------------------------------------------ */ -table { font-size: 0.9rem; } +table { font-size: 0.9rem; touch-action: manipulation; } table.compact { font-size: 0.82rem; } table.compact th, table.compact td { padding: 0.35rem 0.5rem; } +table td:first-child { font-variant-numeric: tabular-nums; } + +@media (prefers-reduced-motion: reduce) { + .spinner { animation: none; } + .status-scanning { animation: none; } +} @media (max-width: 768px) { table { display: block; overflow-x: auto; white-space: nowrap; } @@ -182,9 +190,14 @@ table.compact td { padding: 0.35rem 0.5rem; } font-size: 0.7rem; opacity: 0.5; cursor: pointer; + border: none; border-bottom: 1px dashed; + background: none; + padding: 0; + color: inherit; } .llm-retry:hover { opacity: 0.8; } +.llm-retry:focus-visible { outline: 2px solid #2196f3; outline-offset: 2px; } .llm-disclaimer { margin-top: 0.6rem; diff --git a/guarddog_nexus/web/templates/_llm_report_fragment.html b/guarddog_nexus/web/templates/_llm_report_fragment.html index 06847b5..53875d6 100644 --- a/guarddog_nexus/web/templates/_llm_report_fragment.html +++ b/guarddog_nexus/web/templates/_llm_report_fragment.html @@ -5,11 +5,11 @@ {{ report.severity_rating }} {% endif %} {% if config.llm_enabled and not config.llm_auto_analyze %} - {{ t('llm_retry', request.state.lang) }} + hx-indicator="closest .llm-report">{{ t('llm_retry', request.state.lang) }} {% endif %}
{{ report.summary }}
diff --git a/guarddog_nexus/web/templates/_packages_table.html b/guarddog_nexus/web/templates/_packages_table.html index 2f40565..10c596b 100644 --- a/guarddog_nexus/web/templates/_packages_table.html +++ b/guarddog_nexus/web/templates/_packages_table.html @@ -2,19 +2,19 @@