feat: add command menu item to layout customization#18764
feat: add command menu item to layout customization#18764charlesBochet merged 57 commits intomainfrom
Conversation
…mand menus ### What - Added `EditActionsButton` to both `RecordIndexCommandMenu` and `RecordShowCommandMenu` components to enhance user interaction. ### Why - This addition allows users to access edit actions directly from the command menus, improving the overall usability of the application. ### Changes - Updated imports and JSX structure in `RecordIndexCommandMenu.tsx` and `RecordShowCommandMenu.tsx` to include the new button.
Move edit/, graphql mutation, selector state, and CommandMenuItemDraggable into server-items/ so all IS_COMMAND_MENU_ITEM_ENABLED code lives in one directory, cleanly separated from the legacy path.
…pdate SidePanelCommandMenuItemEditPage layout
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
There was a problem hiding this comment.
18 issues found across 55 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/edit/hooks/useResetCommandMenuItemsDraft.ts">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/edit/hooks/useResetCommandMenuItemsDraft.ts:18">
P1: Limit this reset to the currently edited command-menu context; it currently reverts hidden items from other pages/objects too.</violation>
</file>
<file name="packages/twenty-front/src/modules/side-panel/constants/SidePanelPagesConfig.tsx">
<violation number="1" location="packages/twenty-front/src/modules/side-panel/constants/SidePanelPagesConfig.tsx:40">
P1: Falling back to `SidePanelRootPage` here leaves the side panel in `CommandMenuDisplay` state, so feature-disabled users get the root content rendered under the command-menu search UI.</violation>
</file>
<file name="packages/twenty-front/src/modules/side-panel/hooks/useSidePanelContextChips.tsx">
<violation number="1" location="packages/twenty-front/src/modules/side-panel/hooks/useSidePanelContextChips.tsx:55">
P1: Filtering `CommandMenuDisplay` here shifts breadcrumb indices, so chip/history clicks navigate to the wrong page.</violation>
</file>
<file name="packages/twenty-front/src/pages/object-record/RecordShowPage.tsx">
<violation number="1" location="packages/twenty-front/src/pages/object-record/RecordShowPage.tsx:65">
P1: Don't hide the side-panel button in layout customization mode; on mobile this removes the only close/open control for the customization side panel.</violation>
</file>
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/components/MoreActionsButton.tsx">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/components/MoreActionsButton.tsx:104">
P3: This condition is dead in the current implementation: `MoreActionsButton` only mounts when layout customization is disabled, so the `alignWithSidePanelTopBar` branch can never run.</violation>
</file>
<file name="packages/twenty-front/src/modules/metadata-store/effect-components/MinimalMetadataLoadEffect.tsx">
<violation number="1" location="packages/twenty-front/src/modules/metadata-store/effect-components/MinimalMetadataLoadEffect.tsx:90">
P2: Changing the feature flag after the initial real load will not fetch `commandMenuItems`, because the effect bails out once `loadedState` already matches `desiredLoadState`.</violation>
</file>
<file name="packages/twenty-front/src/modules/layout-customization/hooks/useEnterLayoutCustomizationMode.ts">
<violation number="1" location="packages/twenty-front/src/modules/layout-customization/hooks/useEnterLayoutCustomizationMode.ts:58">
P2: Copy the main context into the side panel before auto-navigating to `CommandMenuEdit`; `navigateSidePanel()` does not refresh that context when the panel is already open, so this path can open the editor with stale side-panel state.</violation>
</file>
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/EditActionsButton.tsx">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/EditActionsButton.tsx:105">
P1: This resets the side-panel context to the main page before navigation, so editing from an already-open contextual command menu can switch to the wrong action set.</violation>
</file>
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/components/CommandMenuItemDraggable.tsx">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/components/CommandMenuItemDraggable.tsx:15">
P3: Remove the unused required `id` prop from this wrapper; it is never read or forwarded.</violation>
<violation number="2" location="packages/twenty-front/src/modules/command-menu-item/server-items/components/CommandMenuItemDraggable.tsx:50">
P2: `disabled` does not disable the row's icon-button actions, so a disabled item can still be mutated through its trailing buttons.</violation>
</file>
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/CommandMenuEditRecordSelectionDropdown.tsx">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/CommandMenuEditRecordSelectionDropdown.tsx:82">
P2: Exclusion mode is a bulk/select-all preview, so setting `numberOfSelectedRecords` to `1` makes bulk-only commands preview incorrectly.</violation>
</file>
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/edit/hooks/useReorderCommandMenuItemsInDraft.ts">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/edit/hooks/useReorderCommandMenuItemsInDraft.ts:25">
P1: This reorder uses the full pinned draft instead of the contextual items the user is dragging, so drops can resolve to the wrong position when hidden pinned items exist.</violation>
</file>
<file name="packages/twenty-front/src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts">
<violation number="1" location="packages/twenty-front/src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts:62">
P2: Saving command-menu drafts before page layouts creates another partial-commit path when a later layout save fails.</violation>
</file>
<file name="packages/twenty-front/src/modules/side-panel/components/SidePanelTopBar.tsx">
<violation number="1" location="packages/twenty-front/src/modules/side-panel/components/SidePanelTopBar.tsx:173">
P2: The new search box on `CommandMenuEdit` is non-functional because the edit page never consumes `sidePanelSearchState` or filters its items.</violation>
</file>
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/components/SidePanelCommandMenuItemDisplayPage.tsx">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/components/SidePanelCommandMenuItemDisplayPage.tsx:71">
P2: Only add the reset-context selectable ID when the reset action will actually render; otherwise the side panel can default-select a non-existent item.</violation>
</file>
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/SidePanelCommandMenuItemEditPage.tsx">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/SidePanelCommandMenuItemEditPage.tsx:226">
P2: Only show the label-options dropdown for items with a seeded short label. On commands without an `engineComponentKey`, the dropdown's hide/reset actions cannot change anything.</violation>
</file>
<file name="packages/twenty-shared/src/command-menu/constants/standard-command-menu-item-defaults.constant.ts">
<violation number="1" location="packages/twenty-shared/src/command-menu/constants/standard-command-menu-item-defaults.constant.ts:5">
P2: This introduces a duplicate source of truth for editable command defaults; the server constant still hardcodes many of the same `shortLabel` values and now gets overridden by this shared map.</violation>
</file>
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/CommandMenuItemOptionsDropdown.tsx">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/CommandMenuItemOptionsDropdown.tsx:44">
P2: Unhiding a label overwrites any custom short-label override with the seeded default.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
...front/src/modules/command-menu-item/server-items/edit/hooks/useResetCommandMenuItemsDraft.ts
Outdated
Show resolved
Hide resolved
packages/twenty-front/src/modules/side-panel/constants/SidePanelPagesConfig.tsx
Show resolved
Hide resolved
packages/twenty-front/src/modules/side-panel/hooks/useSidePanelContextChips.tsx
Show resolved
Hide resolved
packages/twenty-front/src/pages/object-record/RecordShowPage.tsx
Outdated
Show resolved
Hide resolved
...wenty-front/src/modules/command-menu-item/server-items/edit/components/EditActionsButton.tsx
Outdated
Show resolved
Hide resolved
.../modules/command-menu-item/server-items/edit/components/SidePanelCommandMenuItemEditPage.tsx
Outdated
Show resolved
Hide resolved
...ges/twenty-shared/src/command-menu/constants/standard-command-menu-item-defaults.constant.ts
Outdated
Show resolved
Hide resolved
...rc/modules/command-menu-item/server-items/edit/components/CommandMenuItemOptionsDropdown.tsx
Outdated
Show resolved
Hide resolved
...dules/command-menu-item/server-items/display/components/CommandMenuItemMoreActionsButton.tsx
Outdated
Show resolved
Hide resolved
...nty-front/src/modules/command-menu-item/server-items/components/CommandMenuItemDraggable.tsx
Outdated
Show resolved
Hide resolved
…d menu item hooks
…erences in SidePanelCommandMenuItemEditPage
There was a problem hiding this comment.
2 issues found across 12 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx">
<violation number="1" location="packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx:89">
P2: `PageHeaderToggleSidePanelButton` is no longer hidden in layout customization mode when the command-menu feature flag is off.</violation>
</file>
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/edit/hooks/useReorderCommandMenuItemsInDraft.ts">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/edit/hooks/useReorderCommandMenuItemsInDraft.ts:28">
P1: Filtering reorder candidates to `contextualItemIds` makes the new position ignore hidden pinned items, so dragging in one context can reorder the item across other contexts' pinned entries.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
...t/src/modules/command-menu-item/server-items/edit/hooks/useReorderCommandMenuItemsInDraft.ts
Outdated
Show resolved
Hide resolved
...ges/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx
Show resolved
Hide resolved
...t/src/modules/command-menu-item/server-items/edit/hooks/useReorderCommandMenuItemsInDraft.ts
Outdated
Show resolved
Hide resolved
…multiple record selection handling
…ents to support enhanced record selection and preview modes
There was a problem hiding this comment.
2 issues found across 9 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/edit/hooks/useReorderCommandMenuItemsInDraft.ts">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/edit/hooks/useReorderCommandMenuItemsInDraft.ts:50">
P1: Early return prevents reordering into an empty target context/section, so valid drag operations can be ignored.</violation>
</file>
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/EditActionsButton.tsx">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/EditActionsButton.tsx:105">
P1: Opening Command Menu Edit no longer syncs main context into the side-panel context, which can leave edit mode bound to stale object/view state.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
...t/src/modules/command-menu-item/server-items/edit/hooks/useReorderCommandMenuItemsInDraft.ts
Show resolved
Hide resolved
...ont/src/modules/command-menu-item/server-items/edit/components/CommandMenuItemEditButton.tsx
Outdated
Show resolved
Hide resolved
Made-with: Cursor
Made-with: Cursor
packages/twenty-front/src/modules/layout-customization/hooks/useEnterLayoutCustomizationMode.ts
Show resolved
Hide resolved
… lint rule Made-with: Cursor
.../modules/command-menu-item/server-items/edit/components/SidePanelCommandMenuItemEditPage.tsx
Show resolved
Hide resolved
…ection Delete useCommandMenuContextApiForEdition hook and inline only the two values the edit page needs (objectMetadataItem + pageType). Replace preview mode state with direct writes to edition targeted records rule and count states in the dropdown component. Made-with: Cursor
There was a problem hiding this comment.
2 issues found across 6 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx:77">
P2: Re-clicking “No record selected” overwrites the saved snapshot with empty values, losing the previous selection for restore.</violation>
<violation number="2" location="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx:90">
P1: Selecting “Records selected” always restores a cached snapshot, which can revert current selection state to stale data.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
...ommand-menu-item/server-items/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
Outdated
Show resolved
Hide resolved
...ommand-menu-item/server-items/edit/components/CommandMenuItemEditRecordSelectionDropdown.tsx
Outdated
Show resolved
Hide resolved
…atoms to plain state Read objectMetadataItemId from the side panel context store directly. Convert targeted records rule and selected records count from component states to plain Jotai atoms since there is only one edit panel. Made-with: Cursor
…ove empty shared barrel
Replace commandMenuItemEditTargetedRecordsRuleState and
commandMenuItemEditNumberOfSelectedRecordsState with a single
commandMenuItemEditSelectionModeState ('none' | 'selection').
Remove context store snapshotting from entry points.
Delete empty packages/twenty-shared/src/command-menu/ directory.
Made-with: Cursor
…ilter items - Remove CommandMenuContext dependency from SidePanelCommandMenuItemEditPage - Filter draft items directly by availabilityType and availabilityObjectMetadataId - Wire commandMenuItemEditSelectionModeState to control item visibility: selection mode shows GLOBAL + RECORD_SELECTION items, none mode shows GLOBAL + FALLBACK items - Replace CommandMenuItemDraggable with MenuItem in the 'other' section since those items are not draggable Made-with: Cursor
| const fallbackCommandMenuItems = fallbackItems | ||
| .map((item) => | ||
| buildCommandMenuItem({ | ||
| item, | ||
| scope: CommandMenuItemScope.Global, | ||
| isPinned: false, | ||
| typeOverride: CommandMenuItemType.Fallback, | ||
| }), | ||
| ) |
There was a problem hiding this comment.
Bug: Fallback command menu items have isPinned hardcoded to false, which ignores the user's saved pinning preference from the server.
Severity: MEDIUM
Suggested Fix
In the useCommandMenuItemsFromBackend.tsx hook, update the mapping for fallback items to use the server-provided value for the isPinned property. Change isPinned: false to isPinned: item.isPinned to correctly reflect the user's saved preference.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location:
packages/twenty-front/src/modules/command-menu-item/server-items/common/hooks/useCommandMenuItemsFromBackend.tsx#L220-L228
Potential issue: In `useCommandMenuItemsFromBackend.tsx`, fallback command menu items
have their `isPinned` property hardcoded to `false`. While the UI allows users to pin
these items and the preference is saved to the server, this hardcoded value prevents the
saved state from being reflected in the command menu. This results in a functional
discrepancy where a user's action to pin a fallback item, such as
`searchRecordsFallback`, is ignored upon rendering, making the item always appear as
unpinned. This creates a confusing user experience as their customization is not
respected.
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/SidePanelCommandMenuItemEditPage.tsx">
<violation number="1" location="packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/SidePanelCommandMenuItemEditPage.tsx:275">
P2: Unpinned items lost their options dropdown, so label customization actions are no longer accessible unless the item is pinned first.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| Icon={ItemIcon} | ||
| gripMode="onHover" | ||
| isIconDisplayedOnHoverOnly={false} | ||
| iconButtons={[ |
There was a problem hiding this comment.
P2: Unpinned items lost their options dropdown, so label customization actions are no longer accessible unless the item is pinned first.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-front/src/modules/command-menu-item/server-items/edit/components/SidePanelCommandMenuItemEditPage.tsx, line 275:
<comment>Unpinned items lost their options dropdown, so label customization actions are no longer accessible unless the item is pinned first.</comment>
<file context>
@@ -239,69 +238,65 @@ export const SidePanelCommandMenuItemEditPage = () => {
+ Icon={ItemIcon}
+ gripMode="onHover"
+ isIconDisplayedOnHoverOnly={false}
+ iconButtons={[
+ {
+ Icon: IconPinnedOff,
</file context>
- Display path (useCommandMenuItemsFromBackend) now reads commandMenuItemsSelector directly instead of going through the hook - Removed conditional logic and isLayoutCustomizationModeEnabled check from the hook — it now only returns the draft and isDirty - Edit page reads commandMenuItemsDraftState atom directly Made-with: Cursor
- Delete CommandMenuItemDraggable (no longer used anywhere) - Use MenuItemDraggable directly in pinned section of edit page - Extract matchesObjectMetadataId utility to deduplicate object matching filter between edit page and useCommandMenuItemsFromBackend Made-with: Cursor
useCommandMenuItemsFromBackend reads from commandMenuItemsDraftState when available, falling back to commandMenuItemsSelector. This ensures the pinned buttons in the page header stay in sync with edits made in the side panel (pin/unpin, reorder, label changes). Made-with: Cursor
In edit mode, hasRecordSelection is now derived from commandMenuItemEditSelectionModeState instead of the actual record selection count. Fallback items are also excluded in edit mode when selection mode is active, matching the edit page's filtering logic. Made-with: Cursor
- Revert useCommandMenuItemsFromBackend to pure display-only (no draft or selection mode awareness) - Create PinnedCommandMenuItemButtonsEditMode that reads directly from draft state + selection mode, builds preview buttons independently - Swap between the two in RecordIndexCommandMenu based on whether layout customization mode is active Made-with: Cursor
…own spacing Made-with: Cursor
…bjectMetadataId Made-with: Cursor
Made-with: Cursor
… entry Made-with: Cursor
|
LGTM, @ehconitin could you make another pass of QA here? |
|
@charlesBochet will do! |
|
Thanks @ehconitin for your contribution! |
|
Hey @ehconitin! After you've done the QA of your Pull Request, you can mark it as done here. Thank you! |

figma https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?node-id=93630-365000&t=QesAkY3JOyI9D3UU-0
CleanShot.2026-03-20.at.19.53.17.mp4
CleanShot.2026-03-20.at.19.54.31.mp4