Skip to content

feat(ui): add gg / G navigation in hunk diff#289

Open
SalzDevs wants to merge 1 commit into
modem-dev:mainfrom
SalzDevs:feat/gg-G-navigation
Open

feat(ui): add gg / G navigation in hunk diff#289
SalzDevs wants to merge 1 commit into
modem-dev:mainfrom
SalzDevs:feat/gg-G-navigation

Conversation

@SalzDevs
Copy link
Copy Markdown

Summary

  • add Vim-style navigation aliases to Hunk review controls:
    • G jumps to bottom (alias for End)
    • gg jumps to top (alias for Home)
  • support the aliases in both full app mode and pager mode
  • reset pending g state when entering menus/help/filter so gg stays predictable
  • update controls help copy to include the new aliases
  • add interaction coverage for G -> bottom and gg -> top behavior

Testing

  • bun run format:check
  • bun run typecheck
  • bun run lint
  • bun test src/ui/AppHost.interactions.test.tsx src/ui/components/ui-components.test.tsx

Closes #286

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 11, 2026

Greptile Summary

This PR adds Vim-style gg (jump to top) and G (jump to bottom) navigation aliases to the diff viewer, implemented as a single-key state machine (pendingTopJumpRef) that is shared across both full-app and pager keyboard handlers. The pending state is correctly reset whenever a menu, help dialog, or filter intercepts a key.

  • resolveJumpShortcut is introduced as a shared helper called at the top of both handleAppShortcut and handlePagerShortcut; it mutates a ref to track the first g keystroke.
  • HelpDialog and the snapshot test are updated to surface the new aliases in the controls reference.
  • A new interaction test verifies G→bottom and gg→top sequencing in full-app mode, but the pager-mode code path added in the same PR has no dedicated test coverage.

Confidence Score: 4/5

Safe to merge; the shortcut logic is sound and the state machine correctly resets across all handled modes and overlay states.

The implementation is correct end-to-end for the full-app path and the modifier-guard inconsistency in isUppercaseGKey is unlikely to cause a real issue on standard terminals. The pager-mode path is untested but mirrors the app-mode path exactly, so a silent regression there is low-probability rather than guaranteed.

src/ui/hooks/useAppKeyboardShortcuts.ts — the isUppercaseGKey modifier guard inconsistency is worth a second look before shipping.

Important Files Changed

Filename Overview
src/ui/hooks/useAppKeyboardShortcuts.ts Core implementation of gg/G shortcuts via a ref-based state machine; logic is correct but isUppercaseGKey's sequence branch skips modifier guards that the rest of the file applies consistently.
src/ui/AppHost.interactions.test.tsx Adds integration test covering G→bottom and gg→top in full-app mode; pager-mode path (also changed) has no corresponding test.
src/ui/components/chrome/HelpDialog.tsx Adds gg/G row to the keyboard shortcuts table in the help dialog; straightforward documentation-only change.
src/ui/components/ui-components.test.tsx Snapshot updated to include the new gg/G help line; no logic concerns.
CHANGELOG.md Changelog entry added for gg/G aliases under the Unreleased section; no issues.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    K[KeyEvent received] --> MT{handleMenuToggleShortcut?}
    MT -->|yes| RESET_MT[reset pendingTopJumpRef\nreturn]
    MT -->|no| PM{pagerMode?}
    PM -->|yes| PH[handlePagerShortcut]
    PM -->|no| HS{handleHelpShortcut?}
    HS -->|yes| RESET_H[reset pendingTopJumpRef\nreturn]
    HS -->|no| MS{handleMenuShortcut?}
    MS -->|yes| RESET_M[reset pendingTopJumpRef\nreturn]
    MS -->|no| FS{handleFilterShortcut?}
    FS -->|yes| RESET_F[reset pendingTopJumpRef\nreturn]
    FS -->|no| AH[handleAppShortcut]
    PH --> RJ1[resolveJumpShortcut]
    AH --> RJ2[resolveJumpShortcut]
    RJ1 --> GU1{isUppercaseGKey?}
    RJ2 --> GU1
    GU1 -->|yes| BOT[return bottom\nscrollDiff +1 content]
    GU1 -->|no| GL1{isLowercaseGKey?}
    GL1 -->|no| CLR[clear pending\nreturn null → other shortcut]
    GL1 -->|yes| PEND{pending already?}
    PEND -->|yes| TOP[return top\nscrollDiff -1 content]
    PEND -->|no| SET[set pending = true\nreturn pending → no-op]
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
src/ui/hooks/useAppKeyboardShortcuts.ts:34-38
**Modifier guards missing on `sequence === "G"` branch**

`isLowercaseGKey` explicitly checks `!key.option && !key.ctrl && !key.meta`, and the second branch of `isUppercaseGKey` does the same. The first branch (`key.sequence === "G"`) skips all four modifier guards entirely — so a combo that happens to produce the raw sequence `"G"` (e.g. on an unusual terminal mapping) would still fire jump-to-bottom. The inconsistency also makes the intent harder to follow at a glance.

```suggestion
function isUppercaseGKey(key: KeyEvent) {
  return (
    (key.sequence === "G" && !key.option && !key.ctrl && !key.meta) ||
    (key.name === "g" && key.shift && !key.option && !key.ctrl && !key.meta)
  );
}
```

### Issue 2 of 2
src/ui/AppHost.interactions.test.tsx:1693-1763
**Pager-mode path not exercised**

The PR description explicitly states "support the aliases in both full app mode and pager mode", but the new test only exercises `handleAppShortcut` (full app mode). `handlePagerShortcut` has its own independent call to `resolveJumpShortcut`, and there is no test that launches with `pagerMode: true` and verifies `G` → bottom / `gg` → top. A regression in the pager branch would not be caught by the current test suite.

Reviews (1): Last reviewed commit: "feat(ui): add gg and G navigation shortc..." | Re-trigger Greptile

Comment thread src/ui/hooks/useAppKeyboardShortcuts.ts
Comment thread src/ui/AppHost.interactions.test.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add gg / G navigation in hunk diff

1 participant