LibreChat/client
Marco Beretta ebb4f15dbe
Some checks failed
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Waiting to run
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions
Sync Helm Chart Tags / Ignore non-main push (push) Waiting to run
Sync Helm Chart Tags / Sync chart tags (push) Waiting to run
Publish `@librechat/client` to NPM / pack (push) Has been cancelled
Publish `librechat-data-provider` to NPM / pack (push) Has been cancelled
Publish `@librechat/data-schemas` to NPM / pack (push) Has been cancelled
Publish `@librechat/client` to NPM / publish-npm (push) Has been cancelled
Publish `librechat-data-provider` to NPM / publish-npm (push) Has been cancelled
Publish `@librechat/data-schemas` to NPM / publish-npm (push) Has been cancelled
⌨️ feat: Keyboard Shortcuts (#12425)
* feat: add useKeyboardShortcuts hook and showShortcutsDialog atom

Implements the core keyboard shortcuts hook with 11 shortcuts:
- General: new chat, focus input, copy last response
- Navigation: toggle sidebar, model selector, search, settings
- Chat: stop generating, scroll to bottom, temporary chat, copy code

Also adds the showShortcutsDialog atom to control dialog visibility.

Closes #3664

* feat: add KeyboardShortcutsDialog component

Renders a modal dialog listing all available keyboard shortcuts
grouped by category (General, Navigation, Chat). Features:
- Platform-aware key labels (⌘ on Mac, Ctrl on others)
- Clean kbd-style key badges with subtle shadows
- Grouped sections with separators
- Sticky footer with shortcut to open the dialog itself
- Single close button, Escape to dismiss

* feat: integrate keyboard shortcuts into Root layout and account menu

- Mount useKeyboardShortcuts and KeyboardShortcutsDialog in Root.tsx
  via a KeyboardShortcutsProvider wrapper (only renders post-auth)
- Add 'Keyboard Shortcuts' menu item with Keyboard icon to the
  account settings popover for discoverability

* chore: add data-testid to model selector button

Adds data-testid="model-selector-button" to the model selector
trigger for reliable DOM targeting by keyboard shortcuts and tests.

* i18n: add keyboard shortcuts localization keys

Adds 12 new com_shortcut_* translation keys for the keyboard
shortcuts feature: group labels, action labels, and dialog title.

* style: fix keyboard shortcuts dialog dark mode

Replace token-based dark mode styling with explicit white-alpha
values for kbd badges, borders, and separators:
- Kbd: dark:bg-white/[0.06] dark:border-white/[0.08] dark:shadow-none
- Separators: dark:border-white/[0.06]
- Dialog border: dark:border-white/[0.06] dark:shadow-2xl

Ensures the key badges blend naturally into the dark surface
instead of appearing as harsh bright rectangles.

* feat(shortcuts): add definitions for 8 new keyboard shortcuts

Add shortcut definitions and localization keys for:
- Upload file (Cmd/Ctrl+Shift+U)
- Toggle right sidebar (Cmd/Ctrl+Shift+R)
- Regenerate response (Cmd/Ctrl+Shift+E)
- Edit last message (Cmd/Ctrl+Shift+I)
- Scroll to top (Cmd/Ctrl+Shift+↑)
- Archive conversation (Cmd/Ctrl+Shift+A)
- Delete conversation (Cmd/Ctrl+Shift+Backspace)

Addresses #3664

* feat(shortcuts): implement handlers for all new shortcuts

New handlers:
- Upload file: triggers attach-file button click
- Toggle right sidebar: clicks parameters-button
- Regenerate response: clicks regenerate-generation-button
- Edit last message: finds last user-turn and clicks edit button
- Scroll to top: scrolls main[role=main] to top
- Archive conversation: calls archive mutation + navigates to new chat
- Delete conversation: calls delete mutation + navigates to new chat

Improvements:
- Use getMainScrollContainer() helper targeting main[role=main]
  instead of fragile class-based selectors
- Use data-testid selectors instead of aria-label substring matching
  for stop-generation and model-selector buttons
- Use id-based selectors (button[id^=edit-]) for edit buttons
- Add isEditing guard to skip shortcuts when user is typing in
  inputs, textareas, or contentEditable elements
- Refactor handler from if/return chain to switch statement for
  cleaner flow control

* fix(shortcuts): increase dialog scroll height for expanded shortcut list

With 20 shortcuts across 3 groups, the previous 480px max was tight.
Increase to 560px / 70vh so all shortcuts are visible without
excessive scrolling.

* refactor(shortcuts): use data-testid selectors for reliable targeting

Add data-testid="nav-settings" to the Settings menu item in
AccountSettings so the open-settings shortcut no longer relies on
fragile text-content matching ('Settings' but not 'Keyboard').

* refactor(shortcuts): two-column layout for shortcuts dialog

Split the shortcuts dialog into a two-column grid layout:
- Left column: General + Navigation groups
- Right column: Chat group (which has the most shortcuts)

Reduces vertical height so the full list is visible without scrolling.
Widen dialog to max-w-4xl (w-11/12) to accommodate both columns.
Simplify Kbd/group styling for cleaner visual density.

* refactor(shortcuts): adjust padding in KeyboardShortcutsDialog content

* feat(shortcuts): customizable keyboard shortcuts with recorder UI

Add per-shortcut overrides stored in localStorage, a recorder component
for capturing new key combos with conflict detection, and a per-row
edit/reset affordance in the shortcuts dialog.

* test(shortcuts): fix specs broken by keyboard shortcut hooks

- ExpandedPanel: add customShortcuts atom to the store mock so
  useShortcutDisplay/useShortcutAriaKey can read state
- AttachFileMenu: update queries to the new 'Attach Files' aria-label
- Button (Generations): wrap renders in RecoilRoot now that the
  component reads shortcut state

* feat(shortcuts): add panel/submit/bookmark/continue/read-aloud shortcuts

- Wire stop, regenerate, continue, and read-aloud handlers to existing
  buttons via data-testid, fixing handlers that previously queried
  selectors with no matching DOM nodes.
- Add data-testid='nav-panel-${id}' to expanded sidebar nav buttons so
  the panel-opener shortcuts can target them.
- Add new shortcut definitions and handlers: submitMessage,
  bookmarkConversation, continueResponse, readAloudLastResponse, and
  the open* panel openers (assistants, agents, prompts, memories,
  parameters, files, bookmarks, MCP).
- Drop the toggleRightSidebar shortcut — there is no right sidebar to
  toggle in this codebase.
- Refresh the KeyboardShortcutsDialog layout and ShortcutRecorder for
  the new groups, tighten ShortcutKeyCombo styling, and surface the
  shortcuts hint chips in the account menu.

* chore(shortcuts): remove unused translation keys

Drop com_shortcut_dialog_subtitle, com_shortcut_not_set, and
com_shortcut_reset_aria — no remaining references in the codebase.

* fix(shortcuts): resolve keyboard shortcut and footer regressions

- Guard the temporary-chat toggle so the shortcut mirrors the UI, only
  toggling when the conversation has no messages and is not submitting.
- Stop Ctrl/Cmd+Enter from double-submitting: the main chat textarea
  already submits via its own handler, and submit is blocked from
  unrelated inputs while still working in the chat box.
- Ignore repeated keydown events (e.repeat) so held keys no longer
  re-run toggles or destructive actions.
- Scope archive/delete shortcuts to the conversation in the active
  route using useMatch, preventing mutations of a stale background
  conversation on non-chat routes.
- Keep the recorder conflict controls clickable by including the whole
  editing row in the outside-click containment check.
- Restore privacy policy and terms of service links on public share
  pages via an opt-in Footer prop.
- Expand the sidebar before activating panel shortcuts so they are
  visible on mobile, and avoid toggling an already-active panel.

* fix(shortcuts): reject bare non-printable shortcut bindings

A recorded non-printable key (Tab, Enter, Backspace, Delete, arrows,
Space) with no Cmd/Ctrl/Alt was treated as valid, so it could be saved
and then hijack navigation or fire destructive actions since the global
handler preventDefaults it outside text inputs. Require Shift at minimum
for these keys, which keeps Shift+Escape (focusChat) valid while
rejecting bare single-key bindings.

* style: fix import order drift across keyboard shortcut files

* fix(shortcuts): guard actions behind dialog and resolve reset conflicts

- Ignore global shortcut actions while the shortcuts dialog is open
  (except the toggle that closes it), so a combo like delete/archive
  can no longer fire on the conversation behind the modal.
- When resetting a shortcut to its default, unbind any other action
  whose custom binding collides with that restored default, so Reset
  after a Replace can't leave two rows sharing one binding with one
  action unreachable.

* fix(shortcuts): keep attach menu button accessible name stable

The shortcut pass changed the attach menu button's aria-label from the hardcoded "Attach File Options" to localize('com_sidepanel_attach_files') ("Attach Files"), which changed its accessible name and broke the provider-file e2e specs that locate it by name. Restore the original label and keep only the added aria-keyshortcuts.

* fix(shortcuts): gate temporary chat toggle to chat routes

The Root-level listener runs on non-chat routes (search, settings, panels) where the last loaded conversation may be empty, so Ctrl/Cmd+Shift+T could flip the hidden isTemporary state without the TemporaryChat control being visible. Require an active chat route (routeConvoId) before toggling.

* test(shortcuts): align attach menu spec with button accessible name

The attach menu button's aria-label was restored to "Attach File Options" (matching dev and the provider-file e2e specs), so update the unit test's button queries from /attach files/i to /attach file options/i. All 26 cases pass.

* fix(shortcuts): target conversation bookmark and reveal search panel

- Bookmark: query the unique #bookmark-menu-button so the shortcut
  bookmarks the current conversation. The previous
  querySelector('[data-testid="bookmark-menu"]') matched the sidebar
  tag-filter button first (same testid, earlier in the DOM), toggling
  the filter instead of bookmarking.
- Focus search: activate the conversations panel before focusing, since
  the search input only mounts there and the sidebar renders just the
  active panel. Route through the nav-panel-conversations button (the
  listener is outside ActivePanelProvider) and settle before focusing,
  so Ctrl/Cmd+/ works from any panel.

* fix(shortcuts): preserve footer links, cross-platform bindings, modal guard

- restore unconditional legal footer links (drop showLegalLinks gate)
- keep untouched platform's default when customizing a binding
- round-trip bindings whose key is the plus character
- suppress global shortcuts while any modal dialog is open
- tag read-aloud test id only on assistant turns

* fix(shortcuts): include non-Radix dialogs in the modal guard

The guard only matched Radix dialogs via data-state="open", missing
Headless UI dialogs (e.g. the redesigned Settings modal) that render
role="dialog" without data-state. Iterate all dialog/alertdialog nodes
and treat one as open unless it is inert or data-state="closed", which
also avoids false positives from always-mounted inert panels.

* fix(shortcuts): gate temporary chat toggle behind TEMPORARY_CHAT permission

* fix(shortcuts): only prevent native key event when shortcut action runs

* fix(shortcuts): rebind temporary chat, open settings without toggling menu, release no-op keys

* fix(shortcuts): confirm conversation delete, use clipboard fallback, add tests

* fix(shortcuts): navigate to new chat after keyboard-confirmed delete

* fix(shortcuts): copy last response via message button, guard unavailable controls

* fix(shortcuts): keep custom Enter-based submit bindings working in the composer

* fix(shortcuts): restrict shift-only bindings to safe keys

* fix(shortcuts): submit custom Enter chords in the composer without inserting a newline

* fix(shortcuts): block global shortcuts while a menu overlay is focused

* fix(shortcuts): rebind archive off the browser-reserved Ctrl+Shift+A

* fix(shortcuts): honor submitMessage overrides in the composer
2026-06-22 17:02:46 -04:00
..
public 🎨 chore: Update Agent Tool with new SVG assets (#12065) 2026-03-04 09:28:19 -05:00
scripts 🔧 refactor: Build Process and Static Asset Handling (#7605) 2025-05-28 11:48:04 -04:00
src ⌨️ feat: Keyboard Shortcuts (#12425) 2026-06-22 17:02:46 -04:00
sw 🛟 fix: Auto-Recover from Stale Service Worker Assets After Deploys (#13686) 2026-06-11 11:57:06 -04:00
test 🧑‍🎨 refactor: Prompts/Sidebar styles for improved UI Consistency (#12426) 2026-04-09 00:02:31 -04:00
babel.config.cjs 🧑‍🎨 refactor: Prompts/Sidebar styles for improved UI Consistency (#12426) 2026-04-09 00:02:31 -04:00
check_updates.sh
index.html 🛟 fix: Auto-Recover from Stale Service Worker Assets After Deploys (#13686) 2026-06-11 11:57:06 -04:00
jest.config.cjs v0.8.7-rc1 (#13592) 2026-06-15 13:10:30 -04:00
nginx.conf 🐳 feat: Bundle Admin Panel in Docker Compose Stacks (#13876) 2026-06-22 16:59:08 -04:00
package.json 👐 a11y: Bump @ariakit/react, Improve a11y of Token Usage, Archived Chats, Reduce Table Layout Shifts (#13874) 2026-06-21 12:53:24 -04:00
postcss.config.cjs
tailwind.config.cjs ⌨️ feat: Keyboard Shortcuts (#12425) 2026-06-22 17:02:46 -04:00
tsconfig.json 👷 ci: Type-check the Client Workspace (#13560) 2026-06-06 18:40:31 -04:00
vite.config.ts 🛟 fix: Auto-Recover from Stale Service Worker Assets After Deploys (#13686) 2026-06-11 11:57:06 -04:00