Skip to content

feat(issues/pulls): add j/k vim keybindings for navigation#34

Merged
benjamincanac merged 2 commits intovolta-net:mainfrom
posva:feat/vim-keybindings
Mar 19, 2026
Merged

feat(issues/pulls): add j/k vim keybindings for navigation#34
benjamincanac merged 2 commits intovolta-net:mainfrom
posva:feat/vim-keybindings

Conversation

@posva
Copy link
Copy Markdown
Contributor

@posva posva commented Mar 18, 2026

Summary

  • Adds j and k as vim-style aliases for ArrowDown/ArrowUp in the issues list
  • Uses defineShortcuts which already ignores shortcuts when focus is inside an input/textarea, so typing in the search box is unaffected

Test plan

  • Press j outside any input — selected issue moves down
  • Press k outside any input — selected issue moves up
  • Click into search input and type j/k — no navigation triggered

Summary by CodeRabbit

  • New Features
    • Added keyboard shortcuts j and k to move to the next and previous issue.
    • Arrow key navigation unified with the same next/previous behavior.
    • Navigation is boundary-safe: selection stops at list ends (no wrap-around).

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 18, 2026

📝 Walkthrough

Walkthrough

Add a generic selectNextIssue(delta) and keyboard shortcuts (j/k) to navigate issues; arrow up/down now delegate to that function. Navigation picks the first issue if none selected and prevents moving past list boundaries (no wrapping).

Changes

Cohort / File(s) Summary
Issues component
app/components/issues/Issues.vue
Added selectNextIssue(delta) to compute/apply next selection; replaced explicit ArrowDown/ArrowUp logic with calls to selectNextIssue(1) and selectNextIssue(-1); added j and k shortcuts; selection defaults to first issue when none selected; navigation is boundary-safe (no wrapping).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 I nibble keys with gentle kicks,
j hops forward, k hops back—so quick,
If none are chosen, I pick the first,
I stop at edges, never burst,
A hop, a click, the issues tick.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly and accurately describes the main change: adding vim-style j/k keybindings for issue navigation, which matches the primary objective and implementation in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@posva posva marked this pull request as draft March 18, 2026 15:46
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/components/issues/Issues.vue (1)

58-75: Deduplicate alias handlers to avoid future drift.

j/arrowdown and k/arrowup currently duplicate logic. Consider extracting shared next/previous handlers and binding aliases to those functions.

Refactor sketch
+const getSelectedIndex = () =>
+  props.issues.findIndex(issue => issue.id === selectedIssue.value?.id)
+
+const selectNextIssue = () => {
+  const index = getSelectedIndex()
+  if (index === -1) selectedIssue.value = props.issues[0] ?? null
+  else if (index < props.issues.length - 1) selectedIssue.value = props.issues[index + 1]
+}
+
+const selectPreviousIssue = () => {
+  const index = getSelectedIndex()
+  if (index === -1) selectedIssue.value = props.issues[props.issues.length - 1] ?? null
+  else if (index > 0) selectedIssue.value = props.issues[index - 1]
+}
+
 defineShortcuts({
-  arrowdown: () => { ... },
-  arrowup: () => { ... },
-  j: () => { ... },
-  k: () => { ... }
+  arrowdown: selectNextIssue,
+  j: selectNextIssue,
+  arrowup: selectPreviousIssue,
+  k: selectPreviousIssue
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/issues/Issues.vue` around lines 58 - 75, Extract the
duplicated navigation logic in the j and k handlers into shared functions (e.g.,
nextIssue() and prevIssue()) that operate on selectedIssue and props.issues:
compute index via props.issues.findIndex(issue => issue.id ===
selectedIssue.value?.id) and then set selectedIssue.value to the next/previous
item (or wrap to first/last when index === -1); replace the inline bodies of j
and k with calls to those shared functions and bind the arrowdown/arrowup
aliases to the same functions to avoid drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/issues/Issues.vue`:
- Around line 52-53: The selection branches assign possibly undefined when
props.issues is empty; update all assignments to selectedIssue.value (the ones
around the index === -1 branch and the similar branches at lines 61-63 and
70-71) to default to null by using the nullish coalescing operator, e.g. set
selectedIssue.value = props.issues[props.issues.length - 1] ?? null (and the
analogous expressions) so the component consistently represents “no selection”
as null instead of undefined.

---

Nitpick comments:
In `@app/components/issues/Issues.vue`:
- Around line 58-75: Extract the duplicated navigation logic in the j and k
handlers into shared functions (e.g., nextIssue() and prevIssue()) that operate
on selectedIssue and props.issues: compute index via
props.issues.findIndex(issue => issue.id === selectedIssue.value?.id) and then
set selectedIssue.value to the next/previous item (or wrap to first/last when
index === -1); replace the inline bodies of j and k with calls to those shared
functions and bind the arrowdown/arrowup aliases to the same functions to avoid
drift.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 41bdc507-cf21-457b-9cde-326b2eb80e69

📥 Commits

Reviewing files that changed from the base of the PR and between 1013bce and 7a031a5.

📒 Files selected for processing (1)
  • app/components/issues/Issues.vue

Comment on lines +52 to +53
if (index === -1) {
selectedIssue.value = props.issues[props.issues.length - 1]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Handle empty-list selection explicitly with null.

These branches can assign undefined when issues is empty. Use ?? null so the model consistently represents “no selection” as null.

Suggested fix
-      selectedIssue.value = props.issues[props.issues.length - 1]
+      selectedIssue.value = props.issues[props.issues.length - 1] ?? null
@@
-      selectedIssue.value = props.issues[0]
+      selectedIssue.value = props.issues[0] ?? null
@@
-      selectedIssue.value = props.issues[props.issues.length - 1]
+      selectedIssue.value = props.issues[props.issues.length - 1] ?? null

Also applies to: 61-63, 70-71

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/issues/Issues.vue` around lines 52 - 53, The selection
branches assign possibly undefined when props.issues is empty; update all
assignments to selectedIssue.value (the ones around the index === -1 branch and
the similar branches at lines 61-63 and 70-71) to default to null by using the
nullish coalescing operator, e.g. set selectedIssue.value =
props.issues[props.issues.length - 1] ?? null (and the analogous expressions) so
the component consistently represents “no selection” as null instead of
undefined.

@posva posva marked this pull request as ready for review March 18, 2026 15:50
Copy link
Copy Markdown
Contributor

@benjamincanac benjamincanac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @posva! 😊

@benjamincanac benjamincanac changed the title feat(issues): add j/k vim keybindings for navigation feat(issues/pulls): add j/k vim keybindings for navigation Mar 19, 2026
@benjamincanac benjamincanac merged commit 9bf557d into volta-net:main Mar 19, 2026
3 checks passed
@posva posva deleted the feat/vim-keybindings branch March 19, 2026 10:41
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.

2 participants