commit a083517591f826f9b15afc435e84236bf3949a71
parent 365ad2fddf40c8d7be197fa6abb02656b2219db4
Author: Damian Myrda <damian@prime8.dev>
Date: Sat, 31 Jan 2026 18:35:16 -0600
feat(ui): add keyboard shortcuts help overlay with ? key binding
Diffstat:
5 files changed, 186 insertions(+), 1 deletion(-)
diff --git a/website/src/layouts/Layout.astro b/website/src/layouts/Layout.astro
@@ -435,4 +435,113 @@ const ogImage = image
transform: translateY(0);
}
}
+
+ /* Help Overlay */
+ #help-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background: rgba(0, 0, 0, 0.8);
+ z-index: 9998;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ visibility: hidden;
+ transition:
+ opacity 0.2s,
+ visibility 0.2s;
+ }
+
+ #help-overlay.visible {
+ opacity: 1;
+ visibility: visible;
+ }
+
+ .help-modal {
+ background: var(--bg);
+ border: 1px solid var(--border);
+ padding: 20px 24px;
+ min-width: 280px;
+ max-width: 90vw;
+ }
+
+ .help-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid var(--border);
+ }
+
+ .help-title {
+ color: var(--accent);
+ font-weight: 600;
+ }
+
+ .help-close {
+ color: var(--fg-dim);
+ font-size: 11px;
+ }
+
+ .help-content {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+
+ .help-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 24px;
+ }
+
+ .help-key {
+ color: var(--cyan);
+ font-family: "JetBrains Mono", monospace;
+ font-size: 12px;
+ min-width: 60px;
+ }
+
+ .help-desc {
+ color: var(--fg);
+ font-size: 13px;
+ }
+
+ @media (max-width: 768px) {
+ .help-modal {
+ padding: 16px 20px;
+ min-width: 260px;
+ }
+
+ .help-header {
+ margin-bottom: 12px;
+ padding-bottom: 10px;
+ }
+
+ .help-title {
+ font-size: 14px;
+ }
+
+ .help-close {
+ font-size: 10px;
+ }
+
+ .help-row {
+ gap: 16px;
+ }
+
+ .help-key {
+ font-size: 11px;
+ min-width: 50px;
+ }
+
+ .help-desc {
+ font-size: 12px;
+ }
+ }
</style>
diff --git a/website/src/layouts/components/Header.astro b/website/src/layouts/components/Header.astro
@@ -27,7 +27,7 @@ import { TABS } from "../../config/tabs";
></span
>
</span>
- <span class="navigate"> navigate · Tab cycle</span>
+ <span class="navigate"> navigate · Tab cycle · ? help</span>
</div>
</header>
diff --git a/website/src/ui/help.ts b/website/src/ui/help.ts
@@ -0,0 +1,71 @@
+import { onKey } from "./keyboard";
+
+interface Shortcut {
+ key: string;
+ description: string;
+}
+
+const shortcuts: Shortcut[] = [
+ { key: "← →", description: "navigate tabs" },
+ { key: "↑ ↓", description: "scroll content" },
+ { key: "Tab", description: "cycle tabs" },
+ { key: "1-4", description: "switch tabs" },
+ { key: "Enter", description: "open item" },
+ { key: "?", description: "toggle help" },
+];
+
+export function initHelp(): void {
+ createHelpOverlay();
+}
+
+export function setupHelpKeybinds(): void {
+ onKey("?", () => {
+ toggleHelp();
+ });
+}
+
+function createHelpOverlay(): void {
+ const overlay = document.createElement("div");
+ overlay.id = "help-overlay";
+ overlay.innerHTML = `
+ <div class="help-modal">
+ <div class="help-header">
+ <span class="help-title">Help</span>
+ <span class="help-close">Press ? to close</span>
+ </div>
+ <div class="help-content">
+ ${shortcuts
+ .map(
+ (s) => `
+ <div class="help-row">
+ <span class="help-key">${s.key}</span>
+ <span class="help-desc">${s.description}</span>
+ </div>
+ `,
+ )
+ .join("")}
+ </div>
+ </div>
+ `;
+ document.body.appendChild(overlay);
+
+ overlay.addEventListener("click", (e) => {
+ if (e.target === overlay) {
+ hideHelp();
+ }
+ });
+}
+
+function toggleHelp(): void {
+ const overlay = document.getElementById("help-overlay");
+ if (!overlay) return;
+
+ overlay.classList.toggle("visible");
+}
+
+function hideHelp(): void {
+ const overlay = document.getElementById("help-overlay");
+ if (!overlay) return;
+
+ overlay.classList.remove("visible");
+}
diff --git a/website/src/ui/index.ts b/website/src/ui/index.ts
@@ -3,6 +3,7 @@ import { initRouter } from "./router";
import { routeHandler, initTabs } from "./tabs";
import { initKeyboard } from "./keyboard";
import { initStatus } from "./status";
+import { initHelp } from "./help";
// Register tabs (order matters)
import "../tabs/about";
@@ -20,6 +21,7 @@ export async function initUI(): Promise<void> {
// Initialize UI modules
initStatus();
+ initHelp();
await promise;
}
diff --git a/website/src/ui/tabs.ts b/website/src/ui/tabs.ts
@@ -1,6 +1,7 @@
import { $$ } from "./dom";
import { clearAllHandlers, onKey } from "./keyboard";
import { setRoute } from "./router";
+import { setupHelpKeybinds } from "./help";
const order: string[] = [];
let currentIndex = 0;
@@ -139,6 +140,8 @@ function updateRoute(): void {
}
function setupDefaultKeybinds(): void {
+ setupHelpKeybinds();
+
onKey("Tab", (e) => {
e.preventDefault();
cycleTab(e.shiftKey ? "prev" : "next");