Files
breadcrumb-the-shire/web/js/components/app-error-debug.js

101 lines
3.9 KiB
JavaScript

/* Error Debug Page — interactive behavior (inlined by ErrorRenderer) */
document.addEventListener('DOMContentLoaded', () => {
// ── Tab switching ──────────────────────────────────
const tabs = document.querySelectorAll('.app-error-debug-tab');
const panels = document.querySelectorAll('.app-error-debug-panel');
const activateTab = (tab) => {
const target = tab.dataset.panel;
tabs.forEach(t => t.classList.remove('active'));
tabs.forEach(t => t.setAttribute('aria-selected', 'false'));
tabs.forEach(t => t.setAttribute('tabindex', '-1'));
panels.forEach(p => p.classList.remove('active'));
tab.classList.add('active');
tab.setAttribute('aria-selected', 'true');
tab.setAttribute('tabindex', '0');
const panel = document.getElementById(target);
if (panel) panel.classList.add('active');
};
tabs.forEach(tab => {
tab.addEventListener('click', () => {
activateTab(tab);
});
tab.addEventListener('keydown', (event) => {
if (tabs.length === 0) return;
const currentIndex = Array.prototype.indexOf.call(tabs, tab);
if (currentIndex < 0) return;
let nextIndex = currentIndex;
if (event.key === 'ArrowRight') {
nextIndex = (currentIndex + 1) % tabs.length;
} else if (event.key === 'ArrowLeft') {
nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
} else if (event.key === 'Home') {
nextIndex = 0;
} else if (event.key === 'End') {
nextIndex = tabs.length - 1;
} else {
return;
}
event.preventDefault();
const nextTab = tabs[nextIndex];
if (!nextTab) return;
activateTab(nextTab);
nextTab.focus();
});
});
// ── Frame expand/collapse ──────────────────────────
document.querySelectorAll('.app-error-debug-frame__toggle').forEach(toggle => {
toggle.addEventListener('click', () => {
const frame = toggle.closest('.app-error-debug-frame');
if (!frame) return;
const isOpen = frame.classList.toggle('open');
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
});
});
// Ensure ARIA reflects initial state.
document.querySelectorAll('.app-error-debug-frame').forEach(frame => {
const toggle = frame.querySelector('.app-error-debug-frame__toggle');
if (!toggle) return;
toggle.setAttribute('aria-expanded', frame.classList.contains('open') ? 'true' : 'false');
});
// ── Query expand/collapse ──────────────────────────
document.querySelectorAll('.app-error-debug-query__toggle').forEach(toggle => {
toggle.addEventListener('click', () => {
const query = toggle.closest('.app-error-debug-query');
if (!query) return;
const isOpen = query.classList.toggle('open');
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
});
});
// ── Copy to clipboard ─────────────────────────────
document.querySelectorAll('.app-error-debug-copy-button').forEach(button => {
button.addEventListener('click', async (e) => {
e.stopPropagation();
const text = button.dataset.copy;
if (!text) return;
try {
await navigator.clipboard.writeText(text);
button.classList.add('copied');
const original = button.innerHTML;
button.innerHTML = '<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/></svg>';
setTimeout(() => {
button.classList.remove('copied');
button.innerHTML = original;
}, 1500);
} catch {
// Clipboard API may not be available in all contexts.
}
});
});
});