import { useKeyboard, useTerminalDimensions } from '@opentui/react'; import { computeOverlayRect } from '../layout'; import { useAppDispatch } from '../state/context'; import { truncateTerminalText } from '../text'; import { colors } from '../theme'; const shortcutSections = [ { section: 'Global', shortcuts: [ { key: 'q', action: 'Quit' }, { key: 'Tab / Shift-Tab', action: 'Next / previous panel' }, { key: 'h / l or ← / →', action: 'Focus left / right panel' }, { key: '?', action: 'Toggle help' }, { key: '/', action: 'Search articles' }, { key: '[ / ]', action: 'Shrink / grow sidebar' }, { key: '1 / 2 / 3', action: 'Wide / medium / narrow layout' }, ], }, { section: 'Feed List', shortcuts: [ { key: 'j / k or ↑ / ↓', action: 'Navigate up / down' }, { key: 'g / G', action: 'Jump to top / bottom' }, { key: 'Enter', action: 'Select feed' }, { key: 'a', action: 'Add feed' }, { key: 'e', action: 'Edit feed' }, { key: 'd', action: 'Delete feed' }, { key: 'f', action: 'Fetch selected feed' }, { key: 'F', action: 'Fetch all feeds' }, { key: 'R', action: 'Mark all read in feed' }, ], }, { section: 'Article List', shortcuts: [ { key: 'j / k or ↑ / ↓', action: 'Navigate up / down' }, { key: 'Enter', action: 'Open article' }, { key: 's', action: 'Toggle star' }, { key: 'r', action: 'Toggle read' }, { key: 'R', action: 'Mark all read in feed' }, { key: 'f', action: 'Fetch selected feed' }, { key: 'F', action: 'Fetch all feeds' }, { key: 'u', action: 'Toggle unread filter' }, { key: 't', action: 'Cycle sort order' }, ], }, { section: 'Reader', shortcuts: [ { key: '↑ / ↓', action: 'Scroll' }, { key: 'Space or PgDn', action: 'Page down' }, { key: 'b or PgUp', action: 'Page up' }, { key: 'n / p', action: 'Next / previous article' }, { key: 's', action: 'Toggle star' }, { key: 'r', action: 'Toggle read' }, { key: 'o', action: 'Open article in browser' }, { key: 'u', action: 'Show link hints (then type label to open)' }, { key: 'h / ← / Esc', action: 'Back to list' }, ], }, ] as const; const shortcutRows = shortcutSections.flatMap(({ section, shortcuts }) => [ { type: 'section' as const, id: `section-${section}`, section }, ...shortcuts.map((shortcut) => ({ type: 'shortcut' as const, id: `shortcut-${section}-${shortcut.key}-${shortcut.action}`, ...shortcut, })), ]); export function HelpOverlay() { const dispatch = useAppDispatch(); const { width: terminalWidth, height: terminalHeight } = useTerminalDimensions(); const layout = computeOverlayRect(terminalWidth, terminalHeight, { widthRatio: 0.8, minWidth: 40, height: { ratio: 0.8, minHeight: 12 }, leftRatio: 0.1, topRatio: 0.1, padding: 2, }); const keyWidth = Math.min(25, layout.contentWidth); const actionWidth = Math.max(0, layout.contentWidth - keyWidth); useKeyboard((key) => { if (key.name === 'escape' || key.name === 'q' || key.name === '?' || (key.shift && key.name === '/')) { dispatch({ type: 'SET_OVERLAY', overlay: 'none' }); } }); return ( {shortcutRows.map((item) => { if (item.type === 'section') { return ( {truncateTerminalText(item.section, layout.contentWidth)} ); } return ( {truncateTerminalText(` ${item.key}`, keyWidth)} {truncateTerminalText(item.action, actionWidth)} ); })} {truncateTerminalText('Press Esc or ? to close', layout.contentWidth)} ); }