Add WebUI navigation improvements: keyboard shortcuts, hash routing, command palette, cross-entity links
- Keyboard shortcuts: Ctrl+1-4 for tab switching - URL hash routing: #tab/subtab format with browser back/forward support - Tab count badges: running targets and active profiles counts - Cross-entity quick links: clickable references navigate to related cards - Command palette (Ctrl+K): global search across all entities with keyboard navigation - Expand/collapse all sections: buttons in sub-tab bars - Sticky section headers: headers pin while scrolling long card grids - Improved section filter: better styling with reset button Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,7 @@ import {
|
||||
addFilterFromSelect, toggleFilterExpand, removeFilter, moveFilter, updateFilterOption,
|
||||
renderModalFilterList, updateCaptureDuration,
|
||||
cloneStream, cloneCaptureTemplate, clonePPTemplate,
|
||||
expandAllStreamSections, collapseAllStreamSections,
|
||||
} from './features/streams.js';
|
||||
import {
|
||||
createKCTargetCard, testKCTarget, toggleKCTestAutoRefresh,
|
||||
@@ -88,6 +89,7 @@ import {
|
||||
startTargetProcessing, stopTargetProcessing,
|
||||
startTargetOverlay, stopTargetOverlay, deleteTarget,
|
||||
cloneTarget, toggleLedPreview,
|
||||
expandAllTargetSections, collapseAllTargetSections,
|
||||
} from './features/targets.js';
|
||||
|
||||
// Layer 5: color-strip sources
|
||||
@@ -123,8 +125,10 @@ import {
|
||||
showCSSCalibration, toggleCalibrationOverlay,
|
||||
} from './features/calibration.js';
|
||||
|
||||
// Layer 6: tabs
|
||||
import { switchTab, initTabs, startAutoRefresh } from './features/tabs.js';
|
||||
// Layer 6: tabs, navigation, command palette
|
||||
import { switchTab, initTabs, startAutoRefresh, handlePopState } from './features/tabs.js';
|
||||
import { navigateToCard } from './core/navigation.js';
|
||||
import { openCommandPalette, closeCommandPalette, initCommandPalette } from './core/command-palette.js';
|
||||
|
||||
// ─── Register all HTML onclick / onchange / onfocus globals ───
|
||||
|
||||
@@ -191,6 +195,7 @@ Object.assign(window, {
|
||||
// streams / capture templates / PP templates
|
||||
loadPictureSources,
|
||||
switchStreamTab,
|
||||
expandAllStreamSections, collapseAllStreamSections,
|
||||
showAddTemplateModal,
|
||||
editTemplate,
|
||||
closeTemplateModal,
|
||||
@@ -285,6 +290,7 @@ Object.assign(window, {
|
||||
loadTargetsTab,
|
||||
loadTargets,
|
||||
switchTargetSubTab,
|
||||
expandAllTargetSections, collapseAllTargetSections,
|
||||
showTargetEditor,
|
||||
closeTargetEditorModal,
|
||||
forceCloseTargetEditorModal,
|
||||
@@ -348,14 +354,38 @@ Object.assign(window, {
|
||||
showCSSCalibration,
|
||||
toggleCalibrationOverlay,
|
||||
|
||||
// tabs
|
||||
// tabs / navigation / command palette
|
||||
switchTab,
|
||||
startAutoRefresh,
|
||||
navigateToCard,
|
||||
openCommandPalette,
|
||||
closeCommandPalette,
|
||||
});
|
||||
|
||||
// ─── Global Escape key handler ───
|
||||
// ─── Global keyboard shortcuts ───
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
const tag = document.activeElement?.tagName;
|
||||
const inInput = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';
|
||||
|
||||
// Command palette: Ctrl+K / Cmd+K (works even in inputs)
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k' && !e.altKey && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
openCommandPalette();
|
||||
return;
|
||||
}
|
||||
|
||||
// Tab shortcuts: Ctrl+1..4 (skip when typing in inputs)
|
||||
if (!inInput && e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) {
|
||||
const tabMap = { '1': 'dashboard', '2': 'profiles', '3': 'targets', '4': 'streams' };
|
||||
const tab = tabMap[e.key];
|
||||
if (tab) {
|
||||
e.preventDefault();
|
||||
switchTab(tab);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
// Close in order: overlay lightboxes first, then modals via stack
|
||||
if (document.getElementById('display-picker-lightbox').classList.contains('active')) {
|
||||
@@ -368,6 +398,10 @@ document.addEventListener('keydown', (e) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Browser back/forward via hash routing ───
|
||||
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
|
||||
// ─── Cleanup on page unload ───
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
@@ -393,6 +427,9 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Show content now that translations are loaded and tabs are set
|
||||
document.body.style.visibility = 'visible';
|
||||
|
||||
// Initialize command palette
|
||||
initCommandPalette();
|
||||
|
||||
// Setup form handler
|
||||
document.getElementById('add-device-form').addEventListener('submit', handleAddDevice);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user