- base.html: lang динамический, meta theme-color, skip-link, убран дубль Dashboard - Фильтры: labels на search/select, type=search, placeholder с …, autocomplete=off - llm-retry: <span> → <button> во всех шаблонах (_llm_report, scan, package) - sortable th: tabindex=0, role=button через JS + keyboard handler - sort-icon: aria-hidden=true - style.css: color-scheme:dark, prefers-reduced-motion, tabular-nums, touch-action - app.js: clipboard fallback для не-HTTPS
46 lines
1.3 KiB
JavaScript
46 lines
1.3 KiB
JavaScript
// 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;
|
|
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);
|
|
}
|