Skip to content

feat: add command menu item to layout customization#18764

Merged
charlesBochet merged 57 commits intomainfrom
command-menu-item-edition
Mar 26, 2026
Merged

feat: add command menu item to layout customization#18764
charlesBochet merged 57 commits intomainfrom
command-menu-item-edition

Conversation

@ehconitin
Copy link
Copy Markdown
Contributor

@ehconitin ehconitin commented Mar 19, 2026

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

…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
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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.

…ents to support enhanced record selection and preview modes
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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.

…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
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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.

…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
Comment on lines +220 to +228
const fallbackCommandMenuItems = fallbackItems
.map((item) =>
buildCommandMenuItem({
item,
scope: CommandMenuItemScope.Global,
isPinned: false,
typeOverride: CommandMenuItemType.Fallback,
}),
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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={[
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 26, 2026

Choose a reason for hiding this comment

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

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>
Fix with Cubic

- 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
@charlesBochet
Copy link
Copy Markdown
Member

LGTM, @ehconitin could you make another pass of QA here?

@ehconitin
Copy link
Copy Markdown
Contributor Author

@charlesBochet will do!

@charlesBochet charlesBochet merged commit afeef8e into main Mar 26, 2026
103 checks passed
@charlesBochet charlesBochet deleted the command-menu-item-edition branch March 26, 2026 15:04
@github-actions
Copy link
Copy Markdown
Contributor

Thanks @ehconitin for your contribution!
This marks your 343rd PR on the repo. You're top 1% of all our contributors 🎉
See contributor page - Share on LinkedIn - Share on Twitter

Contributions

@twenty-eng-sync
Copy link
Copy Markdown

Hey @ehconitin! After you've done the QA of your Pull Request, you can mark it as done here. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants