|
| 1 | +import { fitText, padText } from "../../lib/text"; |
1 | 2 | import type { AppTheme } from "../../themes"; |
| 3 | +import { ModalFrame } from "./ModalFrame"; |
2 | 4 |
|
3 | | -/** Render the compact keyboard help overlay. */ |
| 5 | +/** Render the keyboard help modal. */ |
4 | 6 | export function HelpDialog({ |
5 | 7 | canRefresh = false, |
6 | | - left, |
| 8 | + terminalHeight, |
| 9 | + terminalWidth, |
7 | 10 | theme, |
8 | | - width, |
9 | 11 | onClose, |
10 | 12 | }: { |
11 | 13 | canRefresh?: boolean; |
12 | | - left: number; |
| 14 | + terminalHeight: number; |
| 15 | + terminalWidth: number; |
13 | 16 | theme: AppTheme; |
14 | | - width: number; |
15 | 17 | onClose: () => void; |
16 | 18 | }) { |
| 19 | + const sections = [ |
| 20 | + { |
| 21 | + title: "Navigation", |
| 22 | + items: [ |
| 23 | + ["↑ / ↓", "move line-by-line"], |
| 24 | + ["Space / b", "page down / page up"], |
| 25 | + ["[ / ]", "previous / next hunk"], |
| 26 | + ["Home / End", "jump to top / bottom"], |
| 27 | + ], |
| 28 | + }, |
| 29 | + { |
| 30 | + title: "View", |
| 31 | + items: [ |
| 32 | + ["1 / 2 / 0", "split / stack / auto"], |
| 33 | + ["s / t", "sidebar / theme"], |
| 34 | + ["a", "toggle AI notes"], |
| 35 | + ["l / w / m", "lines / wrap / metadata"], |
| 36 | + ], |
| 37 | + }, |
| 38 | + { |
| 39 | + title: "Review", |
| 40 | + items: [ |
| 41 | + ["/", "focus file filter"], |
| 42 | + ["Tab", "swap files / filter focus"], |
| 43 | + ["F10", "open menus"], |
| 44 | + [canRefresh ? "r / q" : "q", canRefresh ? "reload / quit" : "quit"], |
| 45 | + ], |
| 46 | + }, |
| 47 | + ] as const; |
| 48 | + |
| 49 | + const width = Math.min(74, Math.max(56, terminalWidth - 8)); |
| 50 | + const bodyWidth = Math.max(1, width - 4); |
| 51 | + const keyWidth = Math.min(16, Math.max(12, Math.floor(bodyWidth * 0.28))); |
| 52 | + const descriptionWidth = Math.max(1, bodyWidth - keyWidth); |
| 53 | + const height = Math.min( |
| 54 | + sections.reduce((total, section) => total + 1 + section.items.length, 0) + 3, |
| 55 | + Math.max(8, terminalHeight - 2), |
| 56 | + ); |
| 57 | + |
17 | 58 | return ( |
18 | | - <box |
19 | | - style={{ |
20 | | - position: "absolute", |
21 | | - top: 3, |
22 | | - left, |
23 | | - width, |
24 | | - height: canRefresh ? 10 : 9, |
25 | | - zIndex: 60, |
26 | | - border: true, |
27 | | - borderColor: theme.accent, |
28 | | - backgroundColor: theme.panel, |
29 | | - padding: 1, |
30 | | - flexDirection: "column", |
31 | | - gap: 1, |
32 | | - }} |
33 | | - onMouseUp={onClose} |
| 59 | + <ModalFrame |
| 60 | + height={height} |
| 61 | + terminalHeight={terminalHeight} |
| 62 | + terminalWidth={terminalWidth} |
| 63 | + theme={theme} |
| 64 | + title="Keyboard help" |
| 65 | + width={width} |
| 66 | + onClose={onClose} |
34 | 67 | > |
35 | | - <text fg={theme.text}>Keyboard</text> |
36 | | - <text fg={theme.muted}>F10 menus arrows navigate menus Enter select Esc close menu</text> |
37 | | - <text fg={theme.muted}>1 split 2 stack 0 auto t theme a notes l lines w wrap m meta</text> |
38 | | - {canRefresh ? <text fg={theme.muted}>r reload the current diff</text> : null} |
39 | | - <text fg={theme.muted}> |
40 | | - ↑/↓ line scroll space next page b previous page Home/End jump [ previous hunk ] next hunk |
41 | | - </text> |
42 | | - <text fg={theme.muted}>drag the Files/Diff divider with the mouse to resize the columns</text> |
43 | | - <text fg={theme.muted}>/ focus filter Tab swap files/filter q quit</text> |
44 | | - <text fg={theme.badgeNeutral}>click anywhere on this panel to close</text> |
45 | | - </box> |
| 68 | + {sections.map((section) => ( |
| 69 | + <box key={section.title} style={{ flexDirection: "column" }}> |
| 70 | + <text fg={theme.badgeNeutral}>{section.title}</text> |
| 71 | + {section.items.map(([keys, description]) => ( |
| 72 | + <box key={`${section.title}:${keys}`} style={{ flexDirection: "row" }}> |
| 73 | + <text fg={theme.accent}>{padText(fitText(keys, keyWidth), keyWidth)}</text> |
| 74 | + <text fg={theme.muted}>{fitText(description, descriptionWidth)}</text> |
| 75 | + </box> |
| 76 | + ))} |
| 77 | + </box> |
| 78 | + ))} |
| 79 | + </ModalFrame> |
46 | 80 | ); |
47 | 81 | } |
0 commit comments