From 01338d2a55904fc4444af2531f70d55d765b13a3 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 24 Apr 2026 01:41:00 +0700 Subject: [PATCH] chore(lint): hoist/memoize inline arrays and objects in app JSX --- .../app/src/components/tool-call-details.tsx | 148 ++++++++++-------- packages/app/src/screens/settings-screen.tsx | 93 +++++------ 2 files changed, 129 insertions(+), 112 deletions(-) diff --git a/packages/app/src/components/tool-call-details.tsx b/packages/app/src/components/tool-call-details.tsx index fbc5614ee..ecf0cfe4b 100644 --- a/packages/app/src/components/tool-call-details.tsx +++ b/packages/app/src/components/tool-call-details.tsx @@ -55,20 +55,69 @@ export function ToolCallDetailsContent({ detail?.type === "sub_agent"); const codeBlockStyle = isFullBleed ? styles.fullBleedBlock : styles.diffContainer; + const sectionFillStyle = useMemo( + () => [styles.section, shouldFill && styles.fillHeight], + [shouldFill], + ); + const codeBlockFillStyle = useMemo( + () => [codeBlockStyle, shouldFill && styles.fillHeight], + [codeBlockStyle, shouldFill], + ); + const codeVerticalScrollStyle = useMemo( + () => [ + styles.codeVerticalScroll, + resolvedMaxHeight !== undefined && { maxHeight: resolvedMaxHeight }, + shouldFill && styles.fillHeight, + webScrollbarStyle, + ], + [resolvedMaxHeight, shouldFill, webScrollbarStyle], + ); + const scrollAreaFillStyle = useMemo( + () => [ + styles.scrollArea, + resolvedMaxHeight !== undefined && { maxHeight: resolvedMaxHeight }, + shouldFill && styles.fillHeight, + webScrollbarStyle, + ], + [resolvedMaxHeight, shouldFill, webScrollbarStyle], + ); + const scrollAreaStyle = useMemo( + () => [ + styles.scrollArea, + resolvedMaxHeight !== undefined && { maxHeight: resolvedMaxHeight }, + webScrollbarStyle, + ], + [resolvedMaxHeight, webScrollbarStyle], + ); + const jsonScrollCombined = useMemo( + () => [styles.jsonScroll, webScrollbarStyle], + [webScrollbarStyle], + ); + const jsonScrollErrorCombined = useMemo( + () => [styles.jsonScroll, styles.jsonScrollError, webScrollbarStyle], + [webScrollbarStyle], + ); + const fullBleedContainerStyle = useMemo( + () => [ + isFullBleed ? styles.fullBleedContainer : styles.paddedContainer, + shouldFill && styles.fillHeight, + ], + [isFullBleed, shouldFill], + ); + const loadingContainerStyle = useMemo( + () => [styles.loadingContainer, fillAvailableHeight && styles.fillHeight], + [fillAvailableHeight], + ); + if (detail?.type === "shell") { const command = detail.command.replace(/\n+$/, ""); const commandOutput = (detail.output ?? "").replace(/^\n+/, ""); const hasOutput = commandOutput.length > 0; sections.push( - - + + 0; sections.push( - - + + - + + + {diffLines ? ( - + + {detail.content ? ( + + @@ -406,15 +426,15 @@ export function ToolCallDetailsContent({ if (errorText) { sections.push( - Error + Error - + {errorText} @@ -425,7 +445,7 @@ export function ToolCallDetailsContent({ if (sections.length === 0) { if (showLoadingSkeleton) { return ( - + @@ -435,16 +455,7 @@ export function ToolCallDetailsContent({ return No additional details available; } - return ( - - {sections} - - ); + return {sections}; } // ---- Styles ---- @@ -598,3 +609,6 @@ const styles = StyleSheet.create((theme) => { }, }; }); + +const SECTION_TITLE_ERROR_STYLE = [styles.sectionTitle, styles.errorText]; +const SCROLL_TEXT_ERROR_STYLE = [styles.scrollText, styles.errorText]; diff --git a/packages/app/src/screens/settings-screen.tsx b/packages/app/src/screens/settings-screen.tsx index 91a032ca1..5b2cb09ac 100644 --- a/packages/app/src/screens/settings-screen.tsx +++ b/packages/app/src/screens/settings-screen.tsx @@ -126,18 +126,18 @@ function ThemeIcon({ } function ThemeSwatch({ color, size }: { color: string; size: number }) { - return ( - + const swatchStyle = useMemo( + () => ({ + width: size, + height: size, + borderRadius: size / 2, + backgroundColor: color, + borderWidth: 1, + borderColor: "rgba(255,255,255,0.15)", + }), + [color, size], ); + return ; } function themeTriggerStyle({ pressed }: PressableStateCallbackType) { @@ -166,6 +166,18 @@ const THEME_LABELS: Record = { auto: "System", }; +const ROW_WITH_BORDER_STYLE = [settingsStyles.row, settingsStyles.rowBorder]; + +const SEND_BEHAVIOR_OPTIONS = [ + { value: "interrupt" as const, label: "Interrupt" }, + { value: "queue" as const, label: "Queue" }, +]; + +const RELEASE_CHANNEL_OPTIONS = [ + { value: "stable" as const, label: "Stable" }, + { value: "beta" as const, label: "Beta" }, +]; + // --------------------------------------------------------------------------- // Section components // --------------------------------------------------------------------------- @@ -252,7 +264,7 @@ function GeneralSection({ - + Default send @@ -263,10 +275,7 @@ function GeneralSection({ size="sm" value={settings.sendBehavior} onValueChange={handleSendBehaviorChange} - options={[ - { value: "interrupt", label: "Interrupt" }, - { value: "queue", label: "Queue" }, - ]} + options={SEND_BEHAVIOR_OPTIONS} /> @@ -401,7 +410,7 @@ function DesktopAppUpdateRow() { return ( <> - + Release channel @@ -412,13 +421,10 @@ function DesktopAppUpdateRow() { size="sm" value={settings.releaseChannel} onValueChange={handleReleaseChannelChange} - options={[ - { value: "stable", label: "Stable" }, - { value: "beta", label: "Beta" }, - ]} + options={RELEASE_CHANNEL_OPTIONS} /> - + App updates {statusText} @@ -503,10 +509,15 @@ function SidebarSectionButton({ const handlePress = useCallback(() => { onSelect(itemId); }, [onSelect, itemId]); + const accessibilityState = useMemo(() => ({ selected: isSelected }), [isSelected]); + const labelStyle = useMemo( + () => [sidebarStyles.label, isSelected && { color: theme.colors.foreground }], + [isSelected, theme.colors.foreground], + ); return ( @@ -514,10 +525,7 @@ function SidebarSectionButton({ size={theme.iconSize.md} color={isSelected ? theme.colors.foreground : theme.colors.foregroundMuted} /> - + {label} @@ -537,10 +545,15 @@ function SidebarHostItem({ serverId, label, isSelected, isLocal, onSelect }: Sid const handlePress = useCallback(() => { onSelect(serverId); }, [onSelect, serverId]); + const accessibilityState = useMemo(() => ({ selected: isSelected }), [isSelected]); + const labelStyle = useMemo( + () => [sidebarStyles.label, isSelected && { color: theme.colors.foreground }], + [isSelected, theme.colors.foreground], + ); return ( - + {label} {isLocal ? ( @@ -604,13 +614,14 @@ function SettingsSidebar({ const containerStyle = isDesktop ? sidebarStyles.desktopContainer : sidebarStyles.mobileContainer; const selectedSectionId = view.kind === "section" ? view.section : null; const selectedServerId = view.kind === "host" ? view.serverId : null; + const paddingTopStyle = useMemo(() => ({ height: padding.top }), [padding.top]); return ( {isDesktop ? ( <> - {padding.top > 0 ? : null} + {padding.top > 0 ? : null} ) : null} {isDesktop ? ( @@ -685,6 +696,7 @@ export default function SettingsScreen({ view }: SettingsScreenProps) { const appVersionText = formatVersionWithPrefix(appVersion); const isCompactLayout = useIsCompactFormFactor(); const insets = useSafeAreaInsets(); + const insetBottomStyle = useMemo(() => ({ paddingBottom: insets.bottom }), [insets.bottom]); const hosts = useHosts(); const hostServerIds = useMemo(() => hosts.map((host) => host.serverId), [hosts]); const anyOnlineServerId = useAnyOnlineHostServerId(hostServerIds); @@ -935,10 +947,7 @@ export default function SettingsScreen({ view }: SettingsScreenProps) { return ( - + - + {content} {addHostModals} @@ -1009,10 +1015,7 @@ export default function SettingsScreen({ view }: SettingsScreenProps) { } leftStyle={desktopStyles.detailLeft} /> - + {content}