Extract app keyboard shortcuts, fix reverse paging, and deflake CI#144
Extract app keyboard shortcuts, fix reverse paging, and deflake CI#144benvinegar merged 6 commits intomainfrom
Conversation
Greptile SummaryThis PR extracts the large inline Key observations:
Confidence Score: 5/5Safe to merge — the refactoring is behaviourally equivalent to the original and all remaining findings are P2 style/cleanup items. No new logic errors are introduced. The only flagged issue (Shift+Space being shadowed by isPageDownKey) is a pre-existing condition faithfully preserved by the refactor. All other observations are test coverage gaps or naming clarity suggestions, none of which affect runtime behaviour. No files require special attention; the unreachable Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
K([KeyEvent]) --> MT{f10 pressed?}
MT -- yes, consumed --> DONE([return])
MT -- no --> PM{pagerMode?}
PM -- yes --> PS["handlePagerShortcut\nq/esc → quit\nspace/f/pgdn → scrollDown\nb/pgup → scrollUp\nd → halfDown\nu → halfUp\nj/down → stepDown\nk/up → stepUp\nhome/end → scrollContent\nw → lineWrap"]
PS --> DONE
PM -- no --> HS{"showHelp and esc?"}
HS -- yes --> CH[closeHelp] --> DONE
HS -- no --> MS{"activeMenuId set?"}
MS -- handled --> DONE
MS -- not handled --> FS{"focusArea = filter?"}
FS -- handled --> DONE
FS -- no --> AS["handleAppShortcut\nq/esc → quit\n? → toggleHelp\ntab → toggleFocus\n/ → focusFilter\nscroll keys\n0/1/2 → layout\ns/r/t/a/l/w/m → toggles\nhunk navigation"]
AS --> DONE
Reviews (1): Last reviewed commit: "refactor: extract app keyboard shortcuts..." | Re-trigger Greptile |
| if (isPageDownKey(key)) { | ||
| scrollDiff(1, "viewport"); | ||
| return; | ||
| } | ||
|
|
||
| if (isPageUpKey(key) || isShiftSpacePageUpKey(key)) { | ||
| scrollDiff(-1, "viewport"); | ||
| return; |
There was a problem hiding this comment.
isShiftSpacePageUpKey is unreachable
isPageDownKey includes the unguarded isSpaceKey helper, which matches any Space key event regardless of the shift modifier. Because isPageDownKey is checked first (line 111), a Shift+Space key press is always consumed as a page-down, and the isShiftSpacePageUpKey branch (line 116) is never reached. The same ordering issue applies to handleAppShortcut at lines 254–261.
This was present in the original code, but naming isShiftSpacePageUpKey as a documented, exported function with a JSDoc comment makes the dead-code more visible.
Two reasonable resolutions:
- If the
less-style Shift+Space reverse-page behaviour is intended to work,isPageDownKeyshould guard its space check with!key.shiftso that shifted Space falls through to the page-up branch. - If the feature is not intended, the
isShiftSpacePageUpKeyexport and its call sites can be removed to avoid the misleading impression that it is active.
There was a problem hiding this comment.
Good catch. I updated isPageDownKey to ignore shifted Space so Shift+Space now reaches the reverse-page path again, and added regression coverage for both the shared key matcher and the app interaction path.
Responded by pi-coding-agent using unknown-model.
| test("keyboard alias helpers normalize the shared scroll shortcut keys", () => { | ||
| expect(isPageDownKey(createKeyEvent({ name: "pagedown" }))).toBe(true); | ||
| expect(isPageDownKey(createKeyEvent({ name: "space" }))).toBe(true); | ||
| expect(isPageDownKey(createKeyEvent({ sequence: "f" }))).toBe(true); | ||
| expect(isPageUpKey(createKeyEvent({ name: "pageup" }))).toBe(true); | ||
| expect(isPageUpKey(createKeyEvent({ sequence: "b" }))).toBe(true); | ||
| expect(isShiftSpacePageUpKey(createKeyEvent({ name: "space", shift: true }))).toBe(true); | ||
| expect(isHalfPageDownKey(createKeyEvent({ name: "d" }))).toBe(true); | ||
| expect(isHalfPageUpKey(createKeyEvent({ sequence: "u" }))).toBe(true); | ||
| expect(isStepDownKey(createKeyEvent({ name: "down" }))).toBe(true); | ||
| expect(isStepDownKey(createKeyEvent({ sequence: "j" }))).toBe(true); | ||
| expect(isStepUpKey(createKeyEvent({ name: "up" }))).toBe(true); | ||
| expect(isStepUpKey(createKeyEvent({ sequence: "k" }))).toBe(true); | ||
| expect(isPageDownKey(createKeyEvent({ name: "q" }))).toBe(false); | ||
| expect(isShiftSpacePageUpKey(createKeyEvent({ name: "space", shift: false }))).toBe(false); | ||
| }); |
There was a problem hiding this comment.
Missing alias coverage in
isPageDownKey / isPageUpKey tests
The test exercises { sequence: "f" } for isPageDownKey but never { name: "f" }, and { sequence: "b" } for isPageUpKey but never { name: "b" }. Since the implementations treat key.name and key.sequence as separate branches, the name-based paths go untested. Consider adding:
expect(isPageDownKey(createKeyEvent({ name: "f" }))).toBe(true);
expect(isPageUpKey(createKeyEvent({ name: "b" }))).toBe(true);There was a problem hiding this comment.
Added the missing key.name === "f" and key.name === "b" assertions while expanding the keyboard alias coverage.
Responded by pi-coding-agent using unknown-model.
dcf66b1 to
0a2607f
Compare
0a2607f to
cefd4a7
Compare
Why
src/ui/App.tsxhad grown into a largeuseKeyboard(...)block that was hard to change safelySummary
src/ui/hooks/useAppKeyboardShortcuts.tssrc/ui/lib/keyboard.tsShift+Spacereverse paging by making it reachable againmainrebaseTesting