From e3d7d0199d09015f9cb4ba89126427087d1819a3 Mon Sep 17 00:00:00 2001 From: Abdul Rahman <81605929+abdulrahmancodes@users.noreply.github.com> Date: Tue, 21 Apr 2026 13:14:00 +0530 Subject: [PATCH] Fix side panel hotkeys breaking when opening records from table (#19849) ## Summary - Fix side panel hotkeys (Ctrl+K, Escape, etc.) breaking when opening records from the record index table - Ensure `side-panel-focus` is always restored in the focus stack when navigating within an already-open side panel - Remove stale `globalHotkeysConfig` on `record-index` focus item that persisted after the side panel closed ## Problem When clicking records in the table to open them in the side panel, `useLeaveTableFocus` called `resetFocusStackToRecordIndex` which wiped the entire focus stack, including the `side-panel-focus` entry. Since `openSidePanel` early-returned when the panel was already open, `side-panel-focus` was never restored. Additionally, `resetFocusStackToRecordIndex` set `enableGlobalHotkeysWithModifiers: false` on the remaining `record-index` item when the side panel was open, and this stale config persisted after the panel closed, permanently blocking all hotkeys. ## Fix - **`useNavigateSidePanel.ts`**: Move `pushFocusItemToFocusStack` before the `isSidePanelOpened` early-return so the side panel's focus entry is always present in the stack - **`useResetFocusStackToRecordIndex.ts`**: Always set `enableGlobalHotkeysWithModifiers: true` on the `record-index` item. Hotkey scoping when the side panel is open is handled by `side-panel-focus` sitting on top of the focus stack https://github.com/user-attachments/assets/ad25befb-338d-4166-9580-18d4e92d6f9b --- .../hooks/useResetFocusStackToRecordIndex.ts | 12 ++---------- .../side-panel/hooks/useNavigateSidePanel.ts | 14 +++++++------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useResetFocusStackToRecordIndex.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useResetFocusStackToRecordIndex.ts index cb7b7392dc..784bfc4d80 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useResetFocusStackToRecordIndex.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useResetFocusStackToRecordIndex.ts @@ -1,19 +1,11 @@ -import { isSidePanelOpenedState } from '@/side-panel/states/isSidePanelOpenedState'; import { PageFocusId } from '@/types/PageFocusId'; import { useResetFocusStackToFocusItem } from '@/ui/utilities/focus/hooks/useResetFocusStackToFocusItem'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; -import { useStore } from 'jotai'; export const useResetFocusStackToRecordIndex = () => { const { resetFocusStackToFocusItem } = useResetFocusStackToFocusItem(); - const store = useStore(); - const resetFocusStackToRecordIndex = () => { - const isSidePanelOpen = store.get(isSidePanelOpenedState.atom); - - const shouldEnableGlobalHotkeys = !isSidePanelOpen; - resetFocusStackToFocusItem({ focusStackItem: { focusId: PageFocusId.RecordIndex, @@ -22,8 +14,8 @@ export const useResetFocusStackToRecordIndex = () => { componentInstanceId: PageFocusId.RecordIndex, }, globalHotkeysConfig: { - enableGlobalHotkeysWithModifiers: shouldEnableGlobalHotkeys, - enableGlobalHotkeysConflictingWithKeyboard: shouldEnableGlobalHotkeys, + enableGlobalHotkeysWithModifiers: true, + enableGlobalHotkeysConflictingWithKeyboard: true, }, }, }); diff --git a/packages/twenty-front/src/modules/side-panel/hooks/useNavigateSidePanel.ts b/packages/twenty-front/src/modules/side-panel/hooks/useNavigateSidePanel.ts index c8915bb624..edb9bc060a 100644 --- a/packages/twenty-front/src/modules/side-panel/hooks/useNavigateSidePanel.ts +++ b/packages/twenty-front/src/modules/side-panel/hooks/useNavigateSidePanel.ts @@ -1,14 +1,14 @@ import { SIDE_PANEL_COMPONENT_INSTANCE_ID } from '@/side-panel/constants/SidePanelComponentInstanceId'; import { SIDE_PANEL_FOCUS_ID } from '@/side-panel/constants/SidePanelFocusId'; import { useSidePanelCloseAnimationCompleteCleanup } from '@/side-panel/hooks/useSidePanelCloseAnimationCompleteCleanup'; +import { hasUserSelectedSidePanelListItemState } from '@/side-panel/states/hasUserSelectedSidePanelListItemState'; +import { isSidePanelClosingState } from '@/side-panel/states/isSidePanelClosingState'; +import { isSidePanelOpenedState } from '@/side-panel/states/isSidePanelOpenedState'; import { sidePanelNavigationMorphItemsByPageState } from '@/side-panel/states/sidePanelNavigationMorphItemsByPageState'; import { sidePanelNavigationStackState } from '@/side-panel/states/sidePanelNavigationStackState'; import { sidePanelPageInfoState } from '@/side-panel/states/sidePanelPageInfoState'; import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState'; import { sidePanelShouldFocusTitleInputComponentState } from '@/side-panel/states/sidePanelShouldFocusTitleInputComponentState'; -import { hasUserSelectedSidePanelListItemState } from '@/side-panel/states/hasUserSelectedSidePanelListItemState'; -import { isSidePanelClosingState } from '@/side-panel/states/isSidePanelClosingState'; -import { isSidePanelOpenedState } from '@/side-panel/states/isSidePanelOpenedState'; import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import { useStore } from 'jotai'; @@ -43,10 +43,6 @@ export const useNavigateSidePanel = () => { }); } - if (isSidePanelOpened) { - return; - } - pushFocusItemToFocusStack({ focusId: SIDE_PANEL_FOCUS_ID, component: { @@ -58,6 +54,10 @@ export const useNavigateSidePanel = () => { }, }); + if (isSidePanelOpened) { + return; + } + store.set(isSidePanelOpenedState.atom, true); store.set(hasUserSelectedSidePanelListItemState.atom, false); }, [