diff --git a/assets/css/base.css b/assets/css/base.css
index ebf3b03..6e4c566 100644
--- a/assets/css/base.css
+++ b/assets/css/base.css
@@ -1449,11 +1449,7 @@ select option:checked {
border: 1px solid var(--border-color);
transition: var(--transition-default);
}
-.lt-sidebar.collapsed { width: 32px; overflow: hidden; }
-.lt-sidebar.collapsed .lt-sidebar-body { display: none; }
-.lt-sidebar.collapsed .lt-sidebar-header { justify-content: center; padding: 0.5rem 0; border-bottom: none; }
-.lt-sidebar.collapsed .lt-sidebar-header > span:first-child { display: none; }
-.lt-sidebar.collapsed .lt-sidebar-toggle { font-size: 0.8rem; padding: 0.4rem; color: var(--accent-cyan); }
+.lt-sidebar.collapsed { display: none; }
.lt-sidebar-header {
display: flex;
@@ -5589,6 +5585,33 @@ body.lt-is-offline .lt-main { margin-top: 2rem; transition: margin-top 0.25s eas
.lt-footer { flex-direction: column; align-items: flex-start; gap: 0.25rem; }
}
+.lt-footer-hints { display: flex; align-items: center; flex-wrap: wrap; gap: 0.25rem; }
+
+.lt-footer-hint {
+ /* reset button defaults */
+ all: unset;
+ cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25rem;
+ color: var(--text-muted);
+ font-size: 0.7rem;
+ font-family: var(--font-mono);
+ white-space: nowrap;
+}
+.lt-footer-hint:hover { color: var(--accent-cyan); }
+.lt-footer-hint:focus-visible { outline: 1px dashed var(--accent-cyan); outline-offset: 2px; }
+
+.lt-footer-key {
+ color: var(--accent-cyan);
+ opacity: 0.8;
+}
+
+.lt-footer-sep {
+ color: var(--border-dim);
+ user-select: none;
+}
+
/* ================================================================
BLINKING CURSOR
SYSTEM STATUS
diff --git a/assets/js/base.js b/assets/js/base.js
index 7bdbdf1..91a8b00 100644
--- a/assets/js/base.js
+++ b/assets/js/base.js
@@ -391,7 +391,10 @@
let combo = '';
if (e.ctrlKey || e.metaKey) combo += 'ctrl+';
if (e.altKey) combo += 'alt+';
- if (e.shiftKey) combo += 'shift+';
+ // Only add shift+ for letter keys — for symbol keys (?, !, @, etc.) the shift state
+ // is already encoded in e.key itself, so adding shift+ would break registrations like '?'.
+ const isLetter = e.key.length === 1 && /[a-zA-Z]/.test(e.key);
+ if (e.shiftKey && isLetter) combo += 'shift+';
combo += e.key.toLowerCase();
const alwaysFire = e.key === 'Escape' || e.ctrlKey || e.metaKey;
if (inInput && !alwaysFire) return;
diff --git a/assets/js/dashboard.js b/assets/js/dashboard.js
index e48ddb2..b81fcae 100644
--- a/assets/js/dashboard.js
+++ b/assets/js/dashboard.js
@@ -3,15 +3,11 @@
*/
function toggleSidebar() {
const sidebar = document.getElementById('lt-sidebar');
+ const btn = document.getElementById('lt-sidebar-toggle-btn');
if (!sidebar) return;
- const isCollapsed = sidebar.classList.toggle('collapsed');
- const btn = sidebar.querySelector('.lt-sidebar-toggle');
- if (btn) {
- btn.textContent = isCollapsed ? '\u25B6' : '\u25C0';
- btn.setAttribute('aria-label', isCollapsed ? 'Expand filter sidebar' : 'Collapse filter sidebar');
- btn.setAttribute('aria-expanded', isCollapsed ? 'false' : 'true');
- }
- localStorage.setItem('sidebarCollapsed', isCollapsed ? 'true' : 'false');
+ const isHidden = sidebar.classList.toggle('collapsed');
+ localStorage.setItem('sidebarCollapsed', isHidden ? 'true' : 'false');
+ if (btn) btn.textContent = isHidden ? '\u22EE\u22EE Show Filters' : '\u22EE\u22EE Filters';
}
/**
@@ -76,28 +72,20 @@ function initMobileSidebar() {
}
-// Restore sidebar state and bind toggle button directly (no event delegation)
+// Restore sidebar state and bind toggle button
document.addEventListener('DOMContentLoaded', function() {
const savedState = localStorage.getItem('sidebarCollapsed');
- const sidebar = document.getElementById('lt-sidebar');
+ const sidebar = document.getElementById('lt-sidebar');
+ const toggleBtn = document.getElementById('lt-sidebar-toggle-btn');
+
if (savedState === 'true' && sidebar) {
sidebar.classList.add('collapsed');
- const btn = sidebar.querySelector('.lt-sidebar-toggle');
- if (btn) {
- btn.textContent = '\u25B6';
- btn.setAttribute('aria-label', 'Expand filter sidebar');
- btn.setAttribute('aria-expanded', 'false');
- }
+ if (toggleBtn) toggleBtn.textContent = '\u22EE\u22EE Show Filters';
}
- // Bind directly — bypass event delegation entirely so it works
- // regardless of isDashboard guard or overflow/clipping issues
- document.querySelectorAll('.lt-sidebar-toggle').forEach(function(btn) {
- btn.addEventListener('click', function(e) {
- e.stopPropagation();
- toggleSidebar();
- });
- });
+ if (toggleBtn) {
+ toggleBtn.addEventListener('click', toggleSidebar);
+ }
});
// Main initialization
diff --git a/views/DashboardView.php b/views/DashboardView.php
index c0a542d..79a3251 100644
--- a/views/DashboardView.php
+++ b/views/DashboardView.php
@@ -409,12 +409,6 @@ include __DIR__ . '/layout_header.php';