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:
2026-02-25 02:40:24 +03:00
parent a82eec7a06
commit f67936c977
16 changed files with 917 additions and 34 deletions

View File

@@ -46,10 +46,10 @@
<div class="tabs">
<div class="tab-bar" role="tablist">
<button class="tab-btn" data-tab="dashboard" onclick="switchTab('dashboard')" role="tab" aria-selected="true" aria-controls="tab-dashboard" id="tab-btn-dashboard"><span data-i18n="dashboard.title">📊 Dashboard</span></button>
<button class="tab-btn" data-tab="profiles" onclick="switchTab('profiles')" role="tab" aria-selected="false" aria-controls="tab-profiles" id="tab-btn-profiles"><span data-i18n="profiles.title">📋 Profiles</span></button>
<button class="tab-btn" data-tab="targets" onclick="switchTab('targets')" role="tab" aria-selected="false" aria-controls="tab-targets" id="tab-btn-targets"><span data-i18n="targets.title">⚡ Targets</span></button>
<button class="tab-btn" data-tab="streams" onclick="switchTab('streams')" role="tab" aria-selected="false" aria-controls="tab-streams" id="tab-btn-streams"><span data-i18n="streams.title">📺 Sources</span></button>
<button class="tab-btn" data-tab="dashboard" onclick="switchTab('dashboard')" role="tab" aria-selected="true" aria-controls="tab-dashboard" id="tab-btn-dashboard" title="Ctrl+1"><span data-i18n="dashboard.title">📊 Dashboard</span></button>
<button class="tab-btn" data-tab="profiles" onclick="switchTab('profiles')" role="tab" aria-selected="false" aria-controls="tab-profiles" id="tab-btn-profiles" title="Ctrl+2"><span data-i18n="profiles.title">📋 Profiles</span><span class="tab-badge" id="tab-badge-profiles" style="display:none"></span></button>
<button class="tab-btn" data-tab="targets" onclick="switchTab('targets')" role="tab" aria-selected="false" aria-controls="tab-targets" id="tab-btn-targets" title="Ctrl+3"><span data-i18n="targets.title">⚡ Targets</span><span class="tab-badge" id="tab-badge-targets" style="display:none"></span></button>
<button class="tab-btn" data-tab="streams" onclick="switchTab('streams')" role="tab" aria-selected="false" aria-controls="tab-streams" id="tab-btn-streams" title="Ctrl+4"><span data-i18n="streams.title">📺 Sources</span></button>
</div>
<div class="tab-panel" id="tab-dashboard" role="tabpanel" aria-labelledby="tab-btn-dashboard">
@@ -79,7 +79,8 @@
<script>
// Apply saved tab immediately during parse to prevent visible jump
(function() {
var saved = localStorage.getItem('activeTab');
var hash = location.hash.replace(/^#/, '');
var saved = hash ? hash.split('/')[0] : localStorage.getItem('activeTab');
if (saved === 'devices') saved = 'targets';
if (!saved || !document.getElementById('tab-' + saved)) saved = 'dashboard';
document.querySelectorAll('.tab-btn').forEach(function(btn) { btn.classList.toggle('active', btn.dataset.tab === saved); });
@@ -124,6 +125,15 @@
{% include 'partials/image-lightbox.html' %}
{% include 'partials/display-picker.html' %}
<div id="command-palette" style="display:none">
<div class="cp-backdrop"></div>
<div class="cp-dialog">
<input id="cp-input" class="cp-input" placeholder="Search..." autocomplete="off">
<div id="cp-results" class="cp-results"></div>
<div class="cp-footer">↑↓ navigate · Enter select · Esc close</div>
</div>
</div>
<script type="module" src="/static/js/app.js"></script>
<script>
// Initialize theme