Skip to content

feat(shortcuts): add keyboard shortcuts and repo filter input#204

Open
Adit-Jain-srm wants to merge 4 commits into
PRODHOSH:mainfrom
Adit-Jain-srm:feat/168-keyboard-shortcuts
Open

feat(shortcuts): add keyboard shortcuts and repo filter input#204
Adit-Jain-srm wants to merge 4 commits into
PRODHOSH:mainfrom
Adit-Jain-srm:feat/168-keyboard-shortcuts

Conversation

@Adit-Jain-srm

@Adit-Jain-srm Adit-Jain-srm commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds keyboard shortcut support and a repo filter input. Press / anywhere on the profile page to focus the repo filter, then type to narrow down repositories by name or description.

Related Issue

Closes #168

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Refactor
  • Chore / dependency update

Changes Made

New file: src/hooks/useKeyboardShortcuts.ts

  • Reusable hook that registers a global keydown listener
  • Supports onSlash (fires when / pressed outside inputs) and onEscape callbacks
  • Checks activeElement.tagName and isContentEditable to avoid capturing keystrokes while typing

Modified: src/components/profile/ProfileView.tsx

  • Added repo filter input above the repository grid
  • Wired useKeyboardShortcuts to focus the filter on / press
  • Repos are filtered by name and description (case-insensitive)
  • Input styled per DESIGN.md text-input spec (6px border-radius, hairline border, 13px font)

AI Usage

  • I did not use AI for any part of the code in this PR
  • I used AI for coding (Cursor with Claude) and I fully understand every change I made, including which functions I changed, why I changed them, and what side effects they could create

The hook uses useCallback to memoize the handler and useEffect for cleanup. The filter input uses a ref so the hook can call .focus() imperatively. Escape is handled natively by Radix UI dialogs (no custom code needed).

Screenshots (if UI change)

A text input appears above the repo grid with placeholder "Filter repositories... (press / to focus)". Pressing / from anywhere on the page focuses it.

Checklist

  • I was assigned to the issue before opening this PR
  • My branch is up to date with main
  • Code works locally and I have tested it
  • No console.log left in src/
  • If schema changed - both schema.sql and a new migration file are included
  • If this is a UI change - I read DESIGN.md and followed the design system (colors, spacing, typography, components)
  • Docs updated if needed
  • PR title follows Conventional Commits format (feat:, fix:, docs:, etc.)
  • This PR description is written in my own words

Summary by CodeRabbit

  • New Features
    • Added keyboard shortcuts to speed up repository filtering: press / (or Ctrl+/) to focus the “Popular repositories” filter, and Esc to dismiss.
    • Added a “Filter repositories…” input to refine the “Popular repositories” list.
  • Bug Fixes
    • Repositories now filter live as you type, matching name or description case-insensitively (with missing descriptions handled gracefully).

…RODHOSH#168)

Adds:
- New useKeyboardShortcuts hook (reusable, supports / and Escape)
- Repo filter input with '/' keyboard shortcut to focus
- Filters repos by name and description (case-insensitive)
- Escape handling delegated to Radix (already built-in)
- Input styling matches DESIGN.md text-input spec (6px radius, hairline border)

Signed-off-by: Adit Jain <aditjain2005@gmail.com>
@github-actions github-actions Bot added frontend Related to UI / Next.js UI Visual / design changes labels Jun 25, 2026
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a69daf66-2714-4712-8202-720082557009

📥 Commits

Reviewing files that changed from the base of the PR and between 313cdf2 and 6843dbb.

📒 Files selected for processing (1)
  • src/hooks/useKeyboardShortcuts.ts

📝 Walkthrough

Walkthrough

ProfileView now adds keyboard-driven repository filtering, with / focusing the filter input and the list narrowing by repository name or description. A new client hook handles global slash and Escape key events while avoiding active text entry.

Changes

Profile repository search shortcuts

Layer / File(s) Summary
Keyboard shortcut hook
src/hooks/useKeyboardShortcuts.ts
A client hook listens for global keydown events, ignores typing in form and editable fields, and invokes slash or escape callbacks while registering and cleaning up the listener.
Profile filter wiring
src/components/profile/ProfileView.tsx
ProfileView adds the shortcut hook, tracks repository filter text, focuses the filter input on /, and renders only repositories whose name or description matches the filter.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant document
  participant useKeyboardShortcuts
  participant ProfileView
  participant repositoryFilterInput
  User->>document: presses "/"
  document->>useKeyboardShortcuts: keydown
  useKeyboardShortcuts->>ProfileView: onSlash()
  ProfileView->>repositoryFilterInput: focus()
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

A bunny tapped the slash so neat,
And repos hopped to search-by-feet.
With whiskers twitching, I can see,
The matching cards all dance for me.
Hop hop! 🐇

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR adds shortcut handling and repo filtering, but it misses arrow-key stat card navigation and only applies on the profile page. Add arrow-key navigation for stat cards and wire the shortcut handling globally so the profile shortcuts work across all pages.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is clear and matches the main change: keyboard shortcuts plus a repository filter input.
Out of Scope Changes check ✅ Passed The changes stay focused on the requested shortcut handling and repository filter input.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@Adit-Jain-srm

Copy link
Copy Markdown
Contributor Author

Could you add the elusoc label? Issue #168 has ELUSOC + ADVENTURER. Thanks!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/profile/ProfileView.tsx`:
- Around line 817-834: The repository filter input in ProfileView lacks an
accessible name because it only uses placeholder text. Update the input element
in ProfileView to include an aria-label that clearly describes its purpose,
keeping the existing searchRef, repoFilter, and onChange behavior unchanged.
- Around line 120-122: Inline onSlash in ProfileView is recreating a new
function on every render, which breaks useKeyboardShortcuts memoization and
causes the document listener to be re-subscribed repeatedly. Update ProfileView
to wrap the onSlash handler in useCallback, add useCallback to the React import
if needed, and keep the callback stable when passing it into
useKeyboardShortcuts so handleKeyDown/useEffect in that hook do not rerun
unnecessarily.

In `@src/hooks/useKeyboardShortcuts.ts`:
- Around line 19-22: The keyboard shortcut handler in useKeyboardShortcuts
currently only matches the bare "/" key, so update the shortcut logic to also
recognize Ctrl+/ (and the platform equivalent if applicable) when calling
onSlash. Keep the existing isTyping guard, and adjust the condition in the
keyboard event handler so both "/" and Ctrl+/ trigger the search focus behavior
without interfering with typing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bfa0d8d3-bbdc-4223-b495-4ae9ecbd0320

📥 Commits

Reviewing files that changed from the base of the PR and between 40f062c and ae4b4d3.

📒 Files selected for processing (2)
  • src/components/profile/ProfileView.tsx
  • src/hooks/useKeyboardShortcuts.ts

Comment thread src/components/profile/ProfileView.tsx Outdated
Comment thread src/components/profile/ProfileView.tsx
Comment thread src/hooks/useKeyboardShortcuts.ts Outdated
Addresses CodeRabbit review:
- Input: added aria-label for screen readers
- onSlash: wrapped in useCallback to prevent listener re-subscription on each render
- Hook: supports both bare / (when not typing) and Ctrl+/ (always)

Signed-off-by: Adit Jain <aditjain2005@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/profile/ProfileView.tsx (1)

837-844: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

No feedback when the filter matches nothing.

The repos.length === 0 branch only covers profiles with zero repositories. When repoFilter excludes every repo, an empty grid renders with no "no matches" message, leaving the user unsure whether the filter worked. Consider rendering an empty-state for the filtered result.

Sketch
const filteredRepos = repos.filter(
  (repo) =>
    !repoFilter ||
    repo.name.toLowerCase().includes(repoFilter.toLowerCase()) ||
    (repo.description || "").toLowerCase().includes(repoFilter.toLowerCase())
);
// ...then render a "No repositories match your filter." message when
// repos.length > 0 && filteredRepos.length === 0
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/profile/ProfileView.tsx` around lines 837 - 844, The
ProfileView repo list only handles the empty unfiltered case, so when repoFilter
removes all items the grid renders blank with no feedback. In ProfileView.tsx,
factor the filtering logic used in the repos.map render into a reusable
filteredRepos variable and add a separate empty-state branch for repos.length >
0 && filteredRepos.length === 0, showing a “No repositories match your filter.”
message before rendering the grid.
♻️ Duplicate comments (1)
src/hooks/useKeyboardShortcuts.ts (1)

19-25: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add metaKey so the shortcut also works on macOS.

isCtrlSlash only checks e.ctrlKey. macOS users typically use ⌘ (e.metaKey), so Cmd+/ won't focus the search there. Consider matching the platform modifier as well.

Proposed change
-      const isCtrlSlash = e.ctrlKey && e.key === "/";
+      const isCtrlSlash = (e.ctrlKey || e.metaKey) && e.key === "/";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useKeyboardShortcuts.ts` around lines 19 - 25, The shortcut check
in useKeyboardShortcuts only handles Ctrl+/ via isCtrlSlash, so it misses macOS
Command+/ users. Update the shortcut detection in the keyboard handler to treat
either e.ctrlKey or e.metaKey as the modifier for the slash shortcut, while
keeping the bare slash behavior unchanged; use the existing isCtrlSlash
condition and onSlash callback to locate the logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/components/profile/ProfileView.tsx`:
- Around line 837-844: The ProfileView repo list only handles the empty
unfiltered case, so when repoFilter removes all items the grid renders blank
with no feedback. In ProfileView.tsx, factor the filtering logic used in the
repos.map render into a reusable filteredRepos variable and add a separate
empty-state branch for repos.length > 0 && filteredRepos.length === 0, showing a
“No repositories match your filter.” message before rendering the grid.

---

Duplicate comments:
In `@src/hooks/useKeyboardShortcuts.ts`:
- Around line 19-25: The shortcut check in useKeyboardShortcuts only handles
Ctrl+/ via isCtrlSlash, so it misses macOS Command+/ users. Update the shortcut
detection in the keyboard handler to treat either e.ctrlKey or e.metaKey as the
modifier for the slash shortcut, while keeping the bare slash behavior
unchanged; use the existing isCtrlSlash condition and onSlash callback to locate
the logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fa933a38-1fac-4d01-ae5f-82a275055dc0

📥 Commits

Reviewing files that changed from the base of the PR and between ae4b4d3 and 2f8efea.

📒 Files selected for processing (2)
  • src/components/profile/ProfileView.tsx
  • src/hooks/useKeyboardShortcuts.ts

Signed-off-by: Adit Jain <aditjain2005@gmail.com>
Signed-off-by: Adit Jain <aditjain2005@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

frontend Related to UI / Next.js UI Visual / design changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] Add keyboard shortcuts for profile navigation (Ctrl+/ for search, Escape to close modals)

1 participant