Add standard command menu items (#18527)

## Add standard command menu items

### Summary

This PR introduces standard command menu items, migrating hardcoded
command menu actions to the backend command menu item architecture
powered by front components. It adds a new `twenty-standard-application`
package that defines, builds, and registers front components as standard
command menu items, gated behind the `IS_COMMAND_MENU_ITEM_ENABLED`
feature flag.

### Description

- **New `twenty-standard-application` package**: Contains front
component definitions with an esbuild-based build pipeline that
generates minified `.mjs` bundles and a manifest with checksums.
- **Server-side registration**: New constants register all items with
metadata (labels, icons, positions, availability types, conditional
expressions). A `StandardFrontComponentUploadService` uploads built
components to file storage.
- **`FALLBACK` availability type**: New enum value for command menu
items that appear as fallback options (e.g., "Search Records" fallback).
- **`CommandMenuContextApi` refactor**
- **Conditional availability enhancements**: New array-based helper
functions for evaluating multi-record conditions.
- **Frontend wiring** (twenty-front):
`useCommandMenuItemFrontComponentCommands`

## Next steps

Only simple commands have been implemented for now:
- **Navigation (9)** -- `CommandLink`: go-to-companies,
go-to-dashboards, go-to-notes, go-to-opportunities, go-to-people,
go-to-runs, go-to-settings, go-to-tasks, go-to-workflows
- **Side panel (4)** -- `CommandOpenSidePanelPage`: ask-ai,
search-records, search-records-fallback, view-previous-ai-chats

We still have to implement front components for all the following
commands:
All have placeholder `execute` logic (`async () => {}`) with a `// TODO:
implement execute logic` comment:

**Record (22)**
- `add-to-favorites`, `remove-from-favorites`
- `create-new-record`, `create-new-view`
- `delete-single-record`, `delete-multiple-records`
- `destroy-single-record`, `destroy-multiple-records`
- `restore-single-record`, `restore-multiple-records`
- `export-from-record-index`, `export-from-record-show`,
`export-multiple-records`, `export-note-to-pdf`, `export-view`
- `hide-deleted-records`, `see-deleted-records`
- `import-records`, `merge-multiple-records`, `update-multiple-records`
- `navigate-to-next-record`, `navigate-to-previous-record`

**Page layout (3)** -- `cancel-record-page-layout`,
`edit-record-page-layout`, `save-record-page-layout`

**Dashboard (4)** -- `cancel-dashboard-layout`, `duplicate-dashboard`,
`edit-dashboard-layout`, `save-dashboard-layout`

**Workflow (10)** -- `activate-workflow`, `add-node-workflow`,
`deactivate-workflow`, `discard-draft-workflow`, `duplicate-workflow`,
`see-active-version-workflow`, `see-runs-workflow`,
`see-versions-workflow`, `test-workflow`, `tidy-up-workflow`

**Workflow version (4)** -- `see-runs-workflow-version`,
`see-versions-workflow-version`, `see-workflow-workflow-version`,
`use-as-draft-workflow-version`

**Workflow run (3)** -- `see-version-workflow-run`,
`see-workflow-workflow-run`, `stop-workflow-run`
This commit is contained in:
Raphaël Bosi
2026-03-10 18:36:41 +01:00
committed by GitHub
parent 25d9f2fcce
commit 2de022afcf
252 changed files with 10972 additions and 253 deletions
@@ -1,16 +1,18 @@
import { useRunWorkflowRecordCommands } from '@/command-menu-item/record/workflow/hooks/useRunWorkflowRecordCommands';
import { useRunWorkflowRecordAgnosticCommands } from '@/command-menu-item/record-agnostic/workflow/hooks/useRunWorkflowRecordAgnosticCommands';
import {
CommandMenuContext,
type CommandMenuContextType,
} from '@/command-menu-item/contexts/CommandMenuContext';
import { useCommandMenuContextApi } from '@/command-menu-item/hooks/useCommandMenuContextApi';
import { useCommandMenuItemFrontComponentCommands } from '@/command-menu-item/hooks/useCommandMenuItemFrontComponentCommands';
import { useRegisteredCommandMenuItems } from '@/command-menu-item/hooks/useRegisteredCommandMenuItems';
import { useShouldCommandMenuItemBeRegisteredParams } from '@/command-menu-item/hooks/useShouldCommandMenuItemBeRegisteredParams';
import { useCommandMenuContextApi } from '@/command-menu-item/hooks/useCommandMenuContextApi';
import { useCommandMenuItemFrontComponentActions } from '@/command-menu-item/hooks/useCommandMenuItemFrontComponentActions';
import { useRunWorkflowRecordAgnosticCommands } from '@/command-menu-item/record-agnostic/workflow/hooks/useRunWorkflowRecordAgnosticCommands';
import { useRunWorkflowRecordCommands } from '@/command-menu-item/record/workflow/hooks/useRunWorkflowRecordCommands';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
export const CommandMenuContextProviderDefault = ({
objectMetadataItem,
@@ -56,7 +58,11 @@ export const CommandMenuContextProviderDefault = ({
const commandMenuContextApi = useCommandMenuContextApi();
const commandMenuItemFrontComponentActions =
useCommandMenuItemFrontComponentActions(commandMenuContextApi);
useCommandMenuItemFrontComponentCommands(commandMenuContextApi);
const isCommandMenuItemEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED,
);
return (
<CommandMenuContext.Provider
@@ -64,12 +70,13 @@ export const CommandMenuContextProviderDefault = ({
isInSidePanel,
displayType,
containerType,
commandMenuItems: [
...commandMenuItems,
...runWorkflowRecordCommands,
...runWorkflowRecordAgnosticCommands,
...commandMenuItemFrontComponentActions,
],
commandMenuItems: isCommandMenuItemEnabled
? commandMenuItemFrontComponentActions
: [
...commandMenuItems,
...runWorkflowRecordCommands,
...runWorkflowRecordAgnosticCommands,
],
}}
>
{children}
@@ -1,12 +1,12 @@
import { useRunWorkflowRecordAgnosticCommands } from '@/command-menu-item/record-agnostic/workflow/hooks/useRunWorkflowRecordAgnosticCommands';
import {
CommandMenuContext,
type CommandMenuContextType,
} from '@/command-menu-item/contexts/CommandMenuContext';
import { useCommandMenuContextApi } from '@/command-menu-item/hooks/useCommandMenuContextApi';
import { useCommandMenuItemFrontComponentCommands } from '@/command-menu-item/hooks/useCommandMenuItemFrontComponentCommands';
import { useRegisteredCommandMenuItems } from '@/command-menu-item/hooks/useRegisteredCommandMenuItems';
import { useShouldCommandMenuItemBeRegisteredParams } from '@/command-menu-item/hooks/useShouldCommandMenuItemBeRegisteredParams';
import { useCommandMenuContextApi } from '@/command-menu-item/hooks/useCommandMenuContextApi';
import { useCommandMenuItemFrontComponentActions } from '@/command-menu-item/hooks/useCommandMenuItemFrontComponentActions';
import { useRunWorkflowRecordAgnosticCommands } from '@/command-menu-item/record-agnostic/workflow/hooks/useRunWorkflowRecordAgnosticCommands';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
@@ -14,7 +14,9 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/use
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { isDefined } from 'twenty-shared/utils';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
type CommandMenuContextProviderWorkflowObjectsProps = {
objectMetadataItem: ObjectMetadataItem;
@@ -55,8 +57,30 @@ const CommandMenuContextProviderWorkflowObjectsContent = ({
const commandMenuContextApi = useCommandMenuContextApi();
const enrichedSelectedRecords = isDefined(workflowWithCurrentVersion)
? commandMenuContextApi.selectedRecords.map((record) =>
record.id === workflowWithCurrentVersion.id
? {
...record,
currentVersion: workflowWithCurrentVersion.currentVersion,
versions: workflowWithCurrentVersion.versions,
statuses: workflowWithCurrentVersion.statuses,
}
: record,
)
: commandMenuContextApi.selectedRecords;
const enrichedCommandMenuContextApi = {
...commandMenuContextApi,
selectedRecords: enrichedSelectedRecords,
};
const commandMenuItemFrontComponentActions =
useCommandMenuItemFrontComponentActions(commandMenuContextApi);
useCommandMenuItemFrontComponentCommands(enrichedCommandMenuContextApi);
const isCommandMenuItemEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED,
);
return (
<CommandMenuContext.Provider
@@ -64,11 +88,9 @@ const CommandMenuContextProviderWorkflowObjectsContent = ({
isInSidePanel,
displayType,
containerType,
commandMenuItems: [
...commandMenuItems,
...runWorkflowRecordAgnosticCommands,
...commandMenuItemFrontComponentActions,
],
commandMenuItems: isCommandMenuItemEnabled
? commandMenuItemFrontComponentActions
: [...commandMenuItems, ...runWorkflowRecordAgnosticCommands],
}}
>
{children}
@@ -104,7 +126,11 @@ const CommandMenuContextProviderWorkflowObjectsWithoutWorkflow = ({
const commandMenuContextApi = useCommandMenuContextApi();
const commandMenuItemFrontComponentActions =
useCommandMenuItemFrontComponentActions(commandMenuContextApi);
useCommandMenuItemFrontComponentCommands(commandMenuContextApi);
const isCommandMenuItemEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED,
);
return (
<CommandMenuContext.Provider
@@ -112,11 +138,9 @@ const CommandMenuContextProviderWorkflowObjectsWithoutWorkflow = ({
isInSidePanel,
displayType,
containerType,
commandMenuItems: [
...commandMenuItems,
...runWorkflowRecordAgnosticCommands,
...commandMenuItemFrontComponentActions,
],
commandMenuItems: isCommandMenuItemEnabled
? commandMenuItemFrontComponentActions
: [...commandMenuItems, ...runWorkflowRecordAgnosticCommands],
}}
>
{children}
@@ -1,8 +1,9 @@
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { objectPermissionsFamilySelector } from '@/auth/states/objectPermissionsFamilySelector';
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
@@ -12,16 +13,17 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
import { hasAnySoftDeleteFilterOnViewComponentSelector } from '@/object-record/record-filter/states/hasAnySoftDeleteFilterOnView';
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { recordStoreRecordsSelector } from '@/object-record/record-store/states/selectors/recordStoreRecordsSelector';
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { isNonEmptyArray } from '@sniptt/guards';
import { useStore } from 'jotai';
import { useContext } from 'react';
import {
CoreObjectNameSingular,
CommandMenuContextApiPageType,
type CommandMenuContextApi,
} from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
@@ -53,28 +55,35 @@ export const useCommandMenuContextApi = (): CommandMenuContextApi => {
const { sortedFavorites: favorites } = useFavorites();
const { navigationMenuItems } = usePrefetchedNavigationMenuItemsData();
const recordId =
const recordIds =
contextStoreTargetedRecordsRule.mode === 'selection'
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
? contextStoreTargetedRecordsRule.selectedRecordIds
: undefined;
const isFavorite = (() => {
if (!isDefined(recordId)) return false;
const favoriteRecordIds = (() => {
if (!isNonEmptyArray(recordIds)) {
return [];
}
if (isNavigationMenuItemEditingEnabled && isDefined(objectMetadataItem)) {
return !!navigationMenuItems?.find(
(item) =>
item.targetRecordId === recordId &&
item.targetObjectMetadataId === objectMetadataItem.id,
return recordIds.filter((recordId) =>
navigationMenuItems?.some(
(item) =>
item.targetRecordId === recordId &&
item.targetObjectMetadataId === objectMetadataItem.id,
),
);
}
return !!favorites?.find((favorite) => favorite.recordId === recordId);
return recordIds.filter((recordId) =>
favorites?.some((favorite) => favorite.recordId === recordId),
);
})();
const selectedRecord =
useAtomFamilyStateValue(recordStoreFamilyState, recordId ?? '') ||
undefined;
const selectedRecords = useAtomFamilySelectorValue(
recordStoreRecordsSelector,
{ recordIds: recordIds ?? [] },
);
const objectPermissionsFromHook = useObjectPermissionsForObject(
objectMetadataItem?.id ?? '',
@@ -92,12 +101,6 @@ export const useCommandMenuContextApi = (): CommandMenuContextApi => {
rowLevelPermissionPredicateGroups: [],
};
const isNoteOrTask =
objectMetadataItem?.nameSingular === CoreObjectNameSingular.Note ||
objectMetadataItem?.nameSingular === CoreObjectNameSingular.Task;
const isRemote = objectMetadataItem?.isRemote ?? false;
const { recordIndexId } = useRecordIndexIdFromCurrentContextStore();
const hasAnySoftDeleteFilterOnView = useAtomComponentSelectorValue(
@@ -105,9 +108,18 @@ export const useCommandMenuContextApi = (): CommandMenuContextApi => {
recordIndexId,
);
const isShowPage =
useAtomComponentStateValue(contextStoreCurrentViewTypeComponentState) ===
ContextStoreViewType.ShowPage;
const contextStoreCurrentViewType = useAtomComponentStateValue(
contextStoreCurrentViewTypeComponentState,
);
const contextStoreIsPageInEditMode = useAtomComponentStateValue(
contextStoreIsPageInEditModeComponentState,
);
const pageType =
contextStoreCurrentViewType === ContextStoreViewType.ShowPage
? CommandMenuContextApiPageType.RECORD_PAGE
: CommandMenuContextApiPageType.INDEX_PAGE;
const contextStoreNumberOfSelectedRecords = useAtomComponentStateValue(
contextStoreNumberOfSelectedRecordsComponentState,
@@ -139,18 +151,18 @@ export const useCommandMenuContextApi = (): CommandMenuContextApi => {
}
return {
isShowPage,
pageType,
isInSidePanel,
isFavorite,
isRemote,
isNoteOrTask,
isPageInEditMode: contextStoreIsPageInEditMode,
favoriteRecordIds,
isSelectAll,
hasAnySoftDeleteFilterOnView,
numberOfSelectedRecords: contextStoreNumberOfSelectedRecords,
objectPermissions,
selectedRecord: selectedRecord as CommandMenuContextApi['selectedRecord'],
selectedRecords,
featureFlags,
targetObjectReadPermissions,
targetObjectWritePermissions,
objectMetadataItem: objectMetadataItem ?? {},
};
};
@@ -2,17 +2,15 @@ import { Command } from '@/command-menu-item/display/components/Command';
import { HeadlessFrontComponentCommandMenuItem } from '@/command-menu-item/display/components/HeadlessFrontComponentCommandMenuItem';
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
import { useOpenFrontComponentInSidePanel } from '@/side-panel/hooks/useOpenFrontComponentInSidePanel';
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useMountHeadlessFrontComponent } from '@/front-components/hooks/useMountHeadlessFrontComponent';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useOpenFrontComponentInSidePanel } from '@/side-panel/hooks/useOpenFrontComponentInSidePanel';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useContext } from 'react';
import { type CommandMenuContextApi } from 'twenty-shared/types';
import {
evaluateConditionalAvailabilityExpression,
@@ -36,8 +34,8 @@ type CommandMenuItemWithFrontComponent = CommandMenuItemFieldsFragment & {
type BuildCommandMenuItemFromFrontComponentParams = {
item: CommandMenuItemWithFrontComponent;
type?: CommandMenuItemType;
scope: CommandMenuItemScope;
index: number;
isPinned: boolean;
getIcon: ReturnType<typeof useIcons>['getIcon'];
openFrontComponentInSidePanel: (params: {
@@ -61,8 +59,8 @@ type BuildCommandMenuItemFromFrontComponentParams = {
// once we have migrated all command menu items
const buildCommandMenuItemFromFrontComponent = ({
item,
type = CommandMenuItemType.FrontComponent,
scope,
index,
isPinned,
getIcon,
openFrontComponentInSidePanel,
@@ -95,12 +93,12 @@ const buildCommandMenuItemFromFrontComponent = ({
};
return {
type: CommandMenuItemType.FrontComponent,
type,
key: `command-menu-item-front-component-${item.id}`,
scope,
label: displayLabel,
shortLabel: displayLabel,
position: index,
shortLabel: item.shortLabel ?? undefined,
position: item.position,
isPinned,
Icon,
shouldBeRegistered: () =>
@@ -119,7 +117,7 @@ const buildCommandMenuItemFromFrontComponent = ({
};
};
export const useCommandMenuItemFrontComponentActions = (
export const useCommandMenuItemFrontComponentCommands = (
commandMenuContextApi: CommandMenuContextApi,
) => {
const { getIcon } = useIcons();
@@ -130,8 +128,6 @@ export const useCommandMenuItemFrontComponentActions = (
contextStoreIsPageInEditModeComponentState,
);
const { containerType } = useContext(CommandMenuContext);
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
contextStoreCurrentObjectMetadataItemIdComponentState,
);
@@ -151,6 +147,10 @@ export const useCommandMenuItemFrontComponentActions = (
? contextStoreTargetedRecordsRule.selectedRecordIds
: [];
const hasRecordSelection =
selectedRecordIds.length >= 1 ||
contextStoreTargetedRecordsRule.mode === 'exclusion';
const mountContext: HeadlessFrontComponentMountContext | undefined =
selectedRecordIds.length === 1 && isDefined(currentObjectMetadataItem)
? {
@@ -164,10 +164,7 @@ export const useCommandMenuItemFrontComponentActions = (
);
const { data } = useFindManyCommandMenuItemsQuery({
skip:
!isCommandMenuItemEnabled ||
(containerType !== 'command-menu-list' &&
containerType !== 'command-menu-show-page-dropdown'),
skip: !isCommandMenuItemEnabled,
});
const frontComponentItems =
@@ -176,35 +173,33 @@ export const useCommandMenuItemFrontComponentActions = (
isDefined(item.frontComponentId),
) ?? [];
const selectedRecordCount =
contextStoreTargetedRecordsRule.mode === 'selection'
? contextStoreTargetedRecordsRule.selectedRecordIds.length
: 0;
const objectMatches = (item: CommandMenuItemWithFrontComponent) =>
!isDefined(item.availabilityObjectMetadataId) ||
item.availabilityObjectMetadataId ===
contextStoreCurrentObjectMetadataItemId;
const globalItems = frontComponentItems.filter(
const frontComponentItemsWithObjectMatches =
frontComponentItems.filter(objectMatches);
const globalItems = frontComponentItemsWithObjectMatches.filter(
(item) => item.availabilityType === CommandMenuItemAvailabilityType.GLOBAL,
);
const recordScopedItems = frontComponentItems.filter((item) => {
if (!objectMatches(item)) return false;
return (
const recordScopedItems = frontComponentItemsWithObjectMatches.filter(
(item) =>
item.availabilityType ===
CommandMenuItemAvailabilityType.RECORD_SELECTION &&
selectedRecordCount >= 1
);
});
CommandMenuItemAvailabilityType.RECORD_SELECTION,
);
const globalCommandMenuItems = globalItems.map((item, index) =>
const fallbackItems = frontComponentItemsWithObjectMatches.filter(
(item) =>
item.availabilityType === CommandMenuItemAvailabilityType.FALLBACK,
);
const globalCommandMenuItems = globalItems.map((item) =>
buildCommandMenuItemFromFrontComponent({
item,
scope: CommandMenuItemScope.Global,
index,
isPinned: !contextStoreIsPageInEditMode && item.isPinned,
getIcon,
openFrontComponentInSidePanel,
@@ -213,19 +208,39 @@ export const useCommandMenuItemFrontComponentActions = (
}),
);
const recordScopedCommandMenuItems = recordScopedItems.map((item, index) =>
const recordScopedCommandMenuItems = hasRecordSelection
? recordScopedItems.map((item) =>
buildCommandMenuItemFromFrontComponent({
item,
scope: CommandMenuItemScope.RecordSelection,
isPinned: !contextStoreIsPageInEditMode && item.isPinned,
getIcon,
openFrontComponentInSidePanel,
mountHeadlessFrontComponent,
commandMenuContextApi,
mountContext,
}),
)
: [];
const fallbackCommandMenuItems = fallbackItems.map((item) =>
buildCommandMenuItemFromFrontComponent({
item,
scope: CommandMenuItemScope.RecordSelection,
index,
isPinned: !contextStoreIsPageInEditMode && item.isPinned,
type: CommandMenuItemType.Fallback,
scope: CommandMenuItemScope.Global,
isPinned: false,
getIcon,
openFrontComponentInSidePanel,
mountHeadlessFrontComponent,
commandMenuContextApi,
mountContext,
}),
);
return [...globalCommandMenuItems, ...recordScopedCommandMenuItems];
return [
...globalCommandMenuItems,
...recordScopedCommandMenuItems,
...fallbackCommandMenuItems,
]
.filter((item) => item.shouldBeRegistered())
.sort((a, b) => a.position - b.position);
};