E11 S5 — the HU/EN sweep and the keyboard-only pass over every module.
- The keyboard cheat sheet printed "Space" and "Menu" on a Hungarian sheet: a keycap that carries a
WORD (not a legend every layout prints alike, like Esc, Ctrl or Enter) is now named by the caller in
the reader's language (`formatChord(chord, words)`, `keys.cap.*`: Szóköz / Menü).
- The Plex player's ⌫/Esc line said "Stop & back to feed", but the player goes back to wherever it
was opened from (Home, a Plex page): "Stop & go back" / "Leállítás és vissza".
- Messages: the conversation list is a keyboard list (NavGrid + navItemAttrs, the name button is the
open element; the pop-out button keeps its own Enter). D1 covers every list whose rows open one
thing; this one was missed in S4. Notification cards have no single open action and stay out, like
the download queue.
Sweep, measured: every static `t("…")` key used in the source exists in both languages (the only
misses are template prefixes); the HU values equal to EN are proper nouns and loanwords (Admin, Plex,
Trakt, Shorts). Palette in Hungarian: "téma" makes "Téma: Midnight" the active row ahead of the channel
"Topik - mindig témában", live region "7 találat".
Keyboard-only pass in Chrome, Hungarian UI — 1024×768: every `g` chord lands on its module, `j` rings
the first item (solid 2px accent) on Home, Feed, Channels, Playlists, Plex and Downloads, `s` focuses
the search on Feed, Channels, Playlists and Plex, no page scrolls sideways. 3440×1440: `j` → → ↓
walks Home, Feed (8 columns: 0 → 1 → 9), Channels, Playlists, Plex and Downloads, no sideways scroll.
The help sheet lists every line translated; inside the Plex player "Szóköz / K" and "⌫ / Esc
Leállítás és vissza". Messages: a real `j` rings the row, Enter opens the thread.
73 lines
3.3 KiB
TypeScript
73 lines
3.3 KiB
TypeScript
import { Fragment } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import Modal from "./Modal";
|
|
import {
|
|
closeKeyboardHelp,
|
|
formatChord,
|
|
helpLines,
|
|
isPageLevelScope,
|
|
useActiveScopes,
|
|
useHelpConditionValues,
|
|
useKeyboardHelpOpen,
|
|
useKeyboardHelpOverLayer,
|
|
useNavigablePages,
|
|
} from "../../lib/keymap";
|
|
|
|
// The app's one keyboard cheat sheet. Mounted once by App; the global `h` that opens it lives in
|
|
// `useGlobalKeymap`, beside the palette's Mod+K, so one listener owns the whole `global` scope.
|
|
//
|
|
// It renders the shared SHORTCUTS table filtered to the scopes that are live right now — the Plex
|
|
// player's keys while you are in the player, the app-wide ones underneath — so there is a single
|
|
// answer to "what can I press here" and nothing to keep in sync. Before this, the Plex player had a
|
|
// cheat sheet nobody else did, listing an array hand-copied from its own handler.
|
|
//
|
|
// `h` rather than the conventional `?`: on a Hungarian layout `?` is a Shift chord, and `h` is the
|
|
// key this app's reader already has in their fingers from the player.
|
|
//
|
|
// Deliberately NOT a new overlay primitive: `Modal` already portals into
|
|
// `document.fullscreenElement ?? document.body` (so this works over a fullscreen player), registers
|
|
// a layer so Escape closes it before the player underneath, traps focus and handles browser Back.
|
|
export default function KeyboardHelp() {
|
|
const { t } = useTranslation();
|
|
const open = useKeyboardHelpOpen();
|
|
const scopes = useActiveScopes();
|
|
const conditions = useHelpConditionValues();
|
|
const overLayer = useKeyboardHelpOverLayer();
|
|
const pages = useNavigablePages();
|
|
|
|
if (!open) return null;
|
|
// A `when` row is listed only where it applies — "Previous / next episode" inside a movie would be
|
|
// a promise the player does not keep.
|
|
// Opened over a player or a dialog, the page-level keys (`s`, `g …`, `j`/`k`) are quiet — listing
|
|
// them there would promise keys that do nothing until the overlay closes. And a `g` chord is listed
|
|
// only for a module this account can actually reach.
|
|
// The context-menu key goes too: every `useContextMenu` is a page list, and with an overlay up the
|
|
// focus Shift+F10 needs is inside the overlay (pre-existing since E10, found in S4 UAT).
|
|
const live = overLayer
|
|
? scopes.filter((s) => !isPageLevelScope(s) && s !== "contextMenu")
|
|
: scopes;
|
|
const lines = helpLines(live, pages).filter((l) => !l.when || !!conditions[l.when]);
|
|
const words = { Space: t("keys.cap.space"), ContextMenu: t("keys.cap.menu") };
|
|
return (
|
|
<Modal title={t("keys.title")} onClose={closeKeyboardHelp} maxWidth="max-w-md">
|
|
<dl className="grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5 text-sm">
|
|
{lines.map((line) => (
|
|
<Fragment key={line.labelKey}>
|
|
<dt className="flex flex-wrap gap-1">
|
|
{line.chords.map((chord) => (
|
|
<kbd
|
|
key={chord}
|
|
className="rounded-sm border border-border bg-surface px-1.5 py-0.5 text-xs font-mono whitespace-nowrap"
|
|
>
|
|
{formatChord(chord, words)}
|
|
</kbd>
|
|
))}
|
|
</dt>
|
|
<dd className="text-muted">{t(line.labelKey)}</dd>
|
|
</Fragment>
|
|
))}
|
|
</dl>
|
|
</Modal>
|
|
);
|
|
}
|