prime8dev

prime8dev
git clone https://git.prime8.dev/prime8dev.git
Log | Files | Refs | README | LICENSE

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:
Mwebsite/src/layouts/Layout.astro | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mwebsite/src/layouts/components/Header.astro | 2+-
Awebsite/src/ui/help.ts | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mwebsite/src/ui/index.ts | 2++
Mwebsite/src/ui/tabs.ts | 3+++
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");