From b4978a1ef72ffb6a75f1c26d503ea4d46099b4da Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 24 Apr 2026 05:36:46 +0700 Subject: [PATCH] chore(lint): hoist jsx-as-prop in app components batch 2 Memoize inline JSX passed as props across agent-list, agent-status-bar, file-explorer-pane, git-actions-split-button, and message to satisfy react-perf/jsx-no-jsx-as-prop. --- packages/app/src/components/agent-list.tsx | 36 +++++----- .../app/src/components/agent-status-bar.tsx | 69 ++++++++++++++----- .../app/src/components/file-explorer-pane.tsx | 19 ++--- .../components/git-actions-split-button.tsx | 20 +++--- packages/app/src/components/message.tsx | 37 +++++----- 5 files changed, 113 insertions(+), 68 deletions(-) diff --git a/packages/app/src/components/agent-list.tsx b/packages/app/src/components/agent-list.tsx index e2bcafa1f..f9a8a2318 100644 --- a/packages/app/src/components/agent-list.tsx +++ b/packages/app/src/components/agent-list.tsx @@ -215,6 +215,11 @@ function SessionRow({ [isSelected], ); + const archivedIcon = useMemo( + () => , + [theme.fontSize.xs, theme.colors.foregroundMuted], + ); + return ( {agent.title || "New session"} - {agent.archivedAt ? ( - } - /> - ) : null} + {agent.archivedAt ? : null} {(agent.pendingPermissionCount ?? 0) > 0 ? ( ) : null} @@ -428,6 +428,19 @@ export function AgentList({ [isActionDaemonUnavailable], ); + const refreshControl = useMemo( + () => + onRefresh ? ( + + ) : undefined, + [onRefresh, isRefreshing, theme.colors.foregroundMuted, refreshColors], + ); + return ( <> - ) : undefined - } + refreshControl={refreshControl} /> void; - }) => { - const visuals = getModeVisuals(provider, option.id, providerDefinitions); - const IconComponent = visuals?.icon ? MODE_ICONS[visuals.icon] : ShieldCheck; - return ( - } - /> - ); - }, + }) => ( + + ), [provider, providerDefinitions, theme.colors.foreground], ); @@ -1021,6 +1019,40 @@ function ThinkingMenuItem({ ); } +function ModeComboboxOption({ + option, + selected, + active, + onPress, + provider, + providerDefinitions, + iconColor, +}: { + option: ComboboxOption; + selected: boolean; + active: boolean; + onPress: () => void; + provider: string; + providerDefinitions: AgentProviderDefinition[]; + iconColor: string; +}) { + const visuals = getModeVisuals(provider, option.id, providerDefinitions); + const IconComponent = visuals?.icon ? MODE_ICONS[visuals.icon] : ShieldCheck; + const leadingSlot = useMemo( + () => , + [IconComponent, iconColor], + ); + return ( + + ); +} + function ModeMenuItem({ mode, provider, @@ -1042,12 +1074,13 @@ function ModeMenuItem({ onSelectMode?.(mode.id); }, [mode.id, onSelectMode]); + const leadingIcon = useMemo( + () => , + [Icon, theme.colors.foreground], + ); + return ( - } - > + {mode.label} ); diff --git a/packages/app/src/components/file-explorer-pane.tsx b/packages/app/src/components/file-explorer-pane.tsx index 82c978612..e3f86ee6e 100644 --- a/packages/app/src/components/file-explorer-pane.tsx +++ b/packages/app/src/components/file-explorer-pane.tsx @@ -142,6 +142,15 @@ function TreeRowItem({ [isExpanded], ); + const copyLeading = useMemo( + () => , + [theme.colors.foregroundMuted], + ); + const downloadLeading = useMemo( + () => , + [theme.colors.foregroundMuted], + ); + return ( {depth > 0 && Array.from({ length: depth }, (_, i) => )} @@ -187,17 +196,11 @@ function TreeRowItem({ - } - onSelect={handleCopy} - > + Copy path {entry.kind === "file" ? ( - } - onSelect={handleDownload} - > + Download ) : null} diff --git a/packages/app/src/components/git-actions-split-button.tsx b/packages/app/src/components/git-actions-split-button.tsx index 823194293..5113a3f65 100644 --- a/packages/app/src/components/git-actions-split-button.tsx +++ b/packages/app/src/components/git-actions-split-button.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo, type ReactElement } from "react"; +import { useCallback, useMemo } from "react"; import { View, Text, @@ -17,6 +17,7 @@ import { } from "@/components/ui/dropdown-menu"; import { Shortcut } from "@/components/ui/shortcut"; import { useShortcutKeys } from "@/hooks/use-shortcut-keys"; +import type { ShortcutKey } from "@/utils/format-shortcut"; import { useToast } from "@/contexts/toast-context"; import type { GitAction, GitActions } from "@/components/git-actions-policy"; @@ -28,7 +29,7 @@ interface GitActionsSplitButtonProps { interface GitActionMenuItemProps { action: GitAction; onSelect: (action: GitAction) => void; - trailing?: ReactElement | null; + archiveShortcutKeys?: ShortcutKey[][] | null; needsSeparator?: boolean; showSeparator?: boolean; closeOnSelect?: boolean; @@ -37,12 +38,19 @@ interface GitActionMenuItemProps { function GitActionMenuItem({ action, onSelect, - trailing, + archiveShortcutKeys, needsSeparator, showSeparator, closeOnSelect, }: GitActionMenuItemProps) { const handleSelect = useCallback(() => onSelect(action), [onSelect, action]); + const trailing = useMemo( + () => + action.id === "archive-worktree" && archiveShortcutKeys ? ( + + ) : undefined, + [action.id, archiveShortcutKeys], + ); return ( {needsSeparator && showSeparator ? : null} @@ -154,11 +162,7 @@ export function GitActionsSplitButton({ gitActions, hideLabels }: GitActionsSpli key={action.id} action={action} onSelect={handleActionSelect} - trailing={ - action.id === "archive-worktree" && archiveShortcutKeys ? ( - - ) : undefined - } + archiveShortcutKeys={archiveShortcutKeys} needsSeparator={ action.id === "merge-from-base" || action.id === "archive-worktree" } diff --git a/packages/app/src/components/message.tsx b/packages/app/src/components/message.tsx index bb08ed67f..9a9ccb070 100644 --- a/packages/app/src/components/message.tsx +++ b/packages/app/src/components/message.tsx @@ -1129,26 +1129,27 @@ const NativeExpandableBadgeShimmer = memo(function NativeExpandableBadgeShimmer( [nativeShimmerPeakStyle, peakWidth, rowHeight], ); + const maskElement = useMemo( + () => ( + + + {label} + + {secondaryLabel ? ( + + {secondaryLabel} + + ) : ( + + )} + + ), + [nativeShimmerMaskStyle, nativeLabelMaskStyle, nativeSecondaryMaskStyle, label, secondaryLabel], + ); + return ( - - - {label} - - {secondaryLabel ? ( - - {secondaryLabel} - - ) : ( - - )} - - } - > +