Skip to content

Commit 7df7582

Browse files
committed
fix keyboard help overlay overlap
1 parent b49ee66 commit 7df7582

File tree

2 files changed

+45
-24
lines changed

2 files changed

+45
-24
lines changed

src/ui/components/chrome/HelpDialog.tsx

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,32 +53,53 @@ export function HelpDialog({
5353
const bodyWidth = Math.max(1, width - 4);
5454
const keyWidth = Math.min(16, Math.max(12, Math.floor(bodyWidth * 0.28)));
5555
const descriptionWidth = Math.max(1, bodyWidth - keyWidth);
56-
const height = Math.min(
57-
sections.reduce((total, section) => total + 1 + section.items.length, 0) + 3,
58-
Math.max(8, terminalHeight - 2),
56+
const contentRowCount = sections.reduce(
57+
(rowCount, section) => rowCount + 1 + section.items.length,
58+
0,
59+
);
60+
// ModalFrame contributes the border rows, title row, padding, and one blank spacer row.
61+
const modalFrameChromeRowCount = 6;
62+
const requiredModalHeight = contentRowCount + modalFrameChromeRowCount;
63+
const modalHeight = Math.min(requiredModalHeight, Math.max(8, terminalHeight - 2));
64+
const shouldScroll = modalHeight < requiredModalHeight;
65+
const content = (
66+
<box style={{ width: "100%", flexDirection: "column" }}>
67+
{sections.map((section) => (
68+
<box key={section.title} style={{ width: "100%", flexDirection: "column" }}>
69+
<box style={{ width: "100%", height: 1 }}>
70+
<text fg={theme.badgeNeutral}>{section.title}</text>
71+
</box>
72+
{section.items.map(([keys, description]) => (
73+
<box
74+
key={`${section.title}:${keys}`}
75+
style={{ width: "100%", height: 1, flexDirection: "row" }}
76+
>
77+
<text fg={theme.accent}>{padText(fitText(keys, keyWidth), keyWidth)}</text>
78+
<text fg={theme.muted}>{fitText(description, descriptionWidth)}</text>
79+
</box>
80+
))}
81+
</box>
82+
))}
83+
</box>
5984
);
6085

6186
return (
6287
<ModalFrame
63-
height={height}
88+
height={modalHeight}
6489
terminalHeight={terminalHeight}
6590
terminalWidth={terminalWidth}
6691
theme={theme}
6792
title="Keyboard help"
6893
width={width}
6994
onClose={onClose}
7095
>
71-
{sections.map((section) => (
72-
<box key={section.title} style={{ flexDirection: "column" }}>
73-
<text fg={theme.badgeNeutral}>{section.title}</text>
74-
{section.items.map(([keys, description]) => (
75-
<box key={`${section.title}:${keys}`} style={{ flexDirection: "row" }}>
76-
<text fg={theme.accent}>{padText(fitText(keys, keyWidth), keyWidth)}</text>
77-
<text fg={theme.muted}>{fitText(description, descriptionWidth)}</text>
78-
</box>
79-
))}
80-
</box>
81-
))}
96+
{shouldScroll ? (
97+
<scrollbox focused={false} height="100%" scrollY={true} width="100%">
98+
{content}
99+
</scrollbox>
100+
) : (
101+
content
102+
)}
82103
</ModalFrame>
83104
);
84105
}

test/ui-components.test.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -836,27 +836,27 @@ describe("UI components", () => {
836836
expect(frame).toContain("beta");
837837
});
838838

839-
test("HelpDialog renders the grouped keyboard help modal", async () => {
839+
test("HelpDialog keeps previously clipped shortcut rows readable", async () => {
840840
const theme = resolveTheme("midnight", null);
841841
const frame = await captureFrame(
842842
<HelpDialog
843843
canRefresh={true}
844-
terminalHeight={20}
844+
terminalHeight={26}
845845
terminalWidth={76}
846846
theme={theme}
847847
onClose={() => {}}
848848
/>,
849849
76,
850-
20,
850+
26,
851851
);
852852

853853
expect(frame).toContain("Keyboard help");
854-
expect(frame).toContain("[Esc]");
855-
expect(frame).toContain("Navigation");
856-
expect(frame).toContain("View");
857-
expect(frame).toContain("Review");
858-
expect(frame).toContain("F10");
859-
expect(frame).toContain("reload / quit");
854+
expect(frame).toContain("b page up");
855+
expect(frame).toContain("a toggle AI notes");
856+
expect(frame).toContain("F10 open menus");
857+
expect(frame).toContain("r / q reload / quit");
858+
expect(frame).not.toContain("linese/Awrapt/smetadata");
859+
expect(frame).not.toContain("reloade/uquit");
860860
});
861861

862862
test("DiffSectionPlaceholder preserves offscreen section chrome without mounting rows", async () => {

0 commit comments

Comments
 (0)