Skip to content

⌨️ fix: Make Scope Selector Buttons Operable with Enter and Highlight the Open Profile - #192

Open
danny-avila wants to merge 1 commit into
mainfrom
pr/redo-scope-selector-enter
Open

danny-avila wants to merge 1 commit into
mainfrom
pr/redo-scope-selector-enter

Conversation

@danny-avila

Copy link
Copy Markdown
Contributor

Pull Request Template

⚠️ Before Submitting a PR, Please Review:

  • Please ensure that you have thoroughly read and understood the Contributing Docs before submitting your Pull Request.

⚠️ Documentation Updates Notice:

  • Kindly note that documentation updates are managed in this repository: librechat.ai

Summary

Makes every button in the configuration scope selector work with Enter. Reopening the selector now highlights the open profile instead of Base. This PR supersedes #121 by @dustinhealy, written fresh on today's main. It reuses that PR's analysis of how cmdk handles Enter, its approach of stopping Enter on the buttons left inside the cmdk root, and its keyboard tests driven by @testing-library/user-event.

Root cause (from #121). The selector renders inside cmdk. cmdk's root onKeyDown calls preventDefault() on Enter and runs the highlighted option instead, which cancels the native click of any focused button inside it. #162 has since made the per-profile delete and activate controls and the role, group and user candidates cmdk options, so Enter already works on those. On main the cause still affects these controls:

Focus Enter on main
Create (profile list) picks the highlighted Base option and closes the selector, so an open profile switches to Base
Back to profiles (create view) creates a profile for the highlighted candidate (ADMIN) and opens it
Cancel / Delete configuration (delete confirmation) nothing; the dialog stays open
The untouched profile search, with a profile open switches to Base, because reopening always highlights Base

Buttons inside the menu. Create and Back to profiles stop Enter before it reaches the cmdk root (keepEnter), so Enter activates them like a click. cmdk doesn't handle Space, so Space already worked on these buttons and isn't changed.

One dialog, one cmdk root per view. The selector now renders its own Radix dialog (ScopeDialog) instead of Command.Dialog, with the same overlay and content classes. The profile list and the create view each mount their own <Command>. With one shared root, cmdk re-highlights its first option (Base) as soon as the highlighted option unmounts, so going back from the create view would lose the open profile's highlight. The delete confirmation has no menu, so cmdk's key handling never reaches its Cancel and Delete configuration buttons. The confirmation opens with focus on Cancel: deleting removes every override in the profile and can't be undone, so Cancel is the safer default. Delete is one Tab away. Each search input takes focus when its view opens, so focus no longer drops to the dialog after Create or Back.

Highlighting the open profile. The profile list sets cmdk's highlighted option through its controlled value. Until the user moves the highlight, that is the open profile (or Base). This applies on every open, after going back from the create view and after a delete. Enter in the untouched search keeps the open profile and just closes the selector. Typing and the arrow keys move the highlight as before. Cancelling a delete returns to the delete option it came from. The create view's candidates are unchanged: Enter in its search still creates the highlighted candidate.

A visible highlight on the open profile. In src/styles.css, the open profile's rule (.scope-item[data-active='true'], hover background) came after the highlight's rule (.scope-item[aria-selected='true'], active background) with the same specificity. So a row that was both open and highlighted kept the hover background. On main that already hid the highlight on Base when no profile was open. With the open profile now highlighted on every open, it would have hidden the highlight on most opens. The highlight's rule now comes last, so the highlighted row always gets the active background. The preview picker uses the same classes, so its current option, highlighted when the picker opens, now shows the highlight too.

Not carried over from #121.

  • Moving the create view and the delete confirmation into click-ui Dialogs. The create view now depends on cmdk filtering and the user search, so it stays a cmdk menu. The confirmation leaves cmdk without a second dialog.
  • Stopping Enter in the empty search. Highlighting the open profile covers that case, and Enter still works after typing.
  • Stopping Enter on the per-profile delete buttons, the new com_ui_back label and the Vitest config change. ✨ feat: Overhaul the Configuration Page (Wave 1) #162 made delete a cmdk option and labelled the back button, and main already has that Vitest config.
  • Focusing Delete in the confirmation (see above).

The @testing-library/user-event dev dependency is carried over, now at 14.6.7.

Change Type

  • Bug fix (non-breaking change which fixes an issue)

Testing

Unit tests in src/components/configuration/__tests__/ScopeSelector.test.tsx use the real cmdk, Radix dialog and click-ui Button. Keys are pressed with user-event, and the server functions are mocked:

  • Enter in the untouched search with a profile open keeps it (no onSelect) and closes.
  • Reopening after moving the highlight to Base (Home, Escape) highlights the open profile again.
  • Enter on Create opens the create view with focus in its search, selects nothing and keeps the dialog open.
  • Enter on Back to profiles returns to the profile list with focus in its search and creates nothing.
  • After typing in the create search and going back, the open profile is highlighted again and Enter keeps it.
  • Enter on Delete configuration deletes exactly once and returns to the list with Base highlighted.
  • A delete reached with the arrow keys opens with Cancel focused. Enter on Cancel returns to the list with the delete option highlighted, without deleting.
  • Guards for existing behaviour: arrow keys plus Enter still select a profile, and Enter in the create search still creates the highlighted candidate.

On main, the 7 new tests fail and the 2 guards pass. Each part of the fix was also reverted on its own: removing keepEnter from Create or Back, rendering the confirmation inside a cmdk root, or dropping the controlled value each fails its tests.

e2e/profiles.spec.ts gains a live test (E2E_LIVE=1, E2E_PROFILE_USER) that creates, keeps and deletes a user profile with the keyboard alone. It passes on this branch and fails on main at the first Enter on Create. It also checks that the highlight can be seen: the open profile's background while it is highlighted must match Base's once Home highlights Base, and must differ from its own background once it isn't highlighted. With the old rule order the test fails at that check. The first test in that spec fails on main before reaching the selector code, because #section-registration [role=note] now matches two notes. That test isn't changed here, so the new test was run on its own (-g "keyboard alone").

Live check with a production build against a LibreChat backend. The configs collection was read before and after each key:

Step This PR
Open with Base, Enter in the search closes, Base kept, nothing created
Enter on Create, then Enter on Back to profiles create view with its search focused, then the profile list with Base highlighted and nothing created
USER profile open, reopen USER highlighted; Enter keeps USER
Background of the highlighted USER row, then after Home highlight background (rgb(227, 229, 232) light, rgb(50, 50, 50) dark), the same as highlighted Base; after Home, USER falls back to the open-profile background (rgb(245, 246, 247) light, rgb(40, 40, 40) dark)
Home (Base highlighted), Escape, reopen USER highlighted again
Enter on Create, type "adm", Enter on Back profile list, USER highlighted, nothing created
Arrow to "Delete profile USER", Enter confirmation with Cancel focused
Enter on Cancel profile list, "Delete profile USER" highlighted
Enter on Delete configuration USER deleted once, scope back to Base, Base highlighted
Space on Create, Space on Back same as Enter
12 profiles, the last one open, reopen the last profile is highlighted and scrolled into view

Apart from the highlighted open row, the dialog looks the same as on main in light and dark mode. The preview picker's highlighted current option also shows the highlight background. No console errors. Every profile created for the check was deleted afterwards.

Test Configuration:

  • LibreChat dev, MongoDB replica set.
  • The panel's production build (bun run build + bun server.ts).
  • npx tsc --noEmit, npx eslint src/ e2e/ --max-warnings 0, npx prettier --check on the changed files, bun run test:lint-rules, npx vitest run (3347 tests), bun run build, npx playwright test e2e/profiles.spec.ts -g "keyboard alone" against the live stack.

Follow-up

  • In the create view, user search results aren't highlighted automatically. The Users group is force-mounted so its hint stays visible, and cmdk doesn't register the options of a force-mounted group. So Enter right after typing an email does nothing until ArrowDown highlights the user. Role and group candidates aren't affected.
  • When the selector closes, focus still lands on the page body instead of the trigger. That's the cmdk part of the focus-return follow-up to 🪃 fix: Return Focus to Trigger When Dialogs Close #120.

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • I have commented in any complex areas of my code
  • My changes do not introduce new warnings
  • I have written tests demonstrating that my changes are effective or that my feature works
  • Local unit tests pass with my changes

… the Open Profile

cmdk's root keydown cancels Enter and runs the highlighted option instead, so
Enter on a plain button inside the scope selector never reached the button.
Enter on Create picked the highlighted Base option and closed the selector,
Enter on "Back to profiles" created a profile for the highlighted candidate,
and Enter on the delete confirmation's Cancel and "Delete configuration" did
nothing.

Create and "Back to profiles" now stop Enter before it reaches the cmdk root,
so Enter activates them like a click. The selector renders its own Radix
dialog, and each view with a menu mounts its own cmdk root, so a highlight
never carries from one view's options into the other's. The delete
confirmation has no menu, so cmdk's key handling never reaches its buttons. It
opens with focus on Cancel. The profile and create searches take focus when
their view opens.

The profile list highlights the open profile (or Base) through cmdk's
controlled value, on every open and after going back from the create view, so
Enter in the untouched search keeps the open profile instead of switching to
Base. Cancelling a delete returns to the delete option it came from.

The highlight's background now wins over the open profile's, so the open
profile reads as highlighted when the selector opens. Before, both rules had
the same specificity and the open profile's came last, which also hid the
highlight on Base with no profile open and in the preview picker.

Builds on the Enter analysis and keyboard tests proposed in #121.
@danny-avila

Copy link
Copy Markdown
Contributor Author

@codex review

Pass 1. Head: 2d6b863

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T07:17:11.773833Z 2d6b863 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: 2d6b8631dd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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.

1 participant