From 8a3129ff15c138d082d322d59bf1c1a9b736ee05 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 24 Apr 2026 02:40:14 +0700 Subject: [PATCH] chore(lint): hoist inline arrays and objects in app (145 warnings) - Hoist static style arrays/objects to module-level consts - Memoize dynamic ones with useMemo and correct deps - jsx-no-new-array-as-prop: 145 -> 43 - jsx-no-new-object-as-prop: 64 -> 21 --- packages/app/src/app/_layout.tsx | 24 +-- packages/app/src/app/settings/[section].tsx | 4 +- .../app/src/app/settings/hosts/[serverId].tsx | 4 +- packages/app/src/app/settings/index.tsx | 4 +- .../src/components/adaptive-modal-sheet.tsx | 25 ++- .../app/src/components/add-host-modal.tsx | 11 +- .../app/src/components/agent-stream-view.tsx | 114 +++++++----- .../src/components/attachment-lightbox.tsx | 3 +- packages/app/src/components/composer.tsx | 5 +- .../desktop/titlebar-drag-region.tsx | 44 ++--- packages/app/src/components/diff-viewer.tsx | 100 ++++++----- .../app/src/components/draggable-list.web.tsx | 26 ++- .../app/src/components/file-explorer-pane.tsx | 7 +- packages/app/src/components/file-pane.tsx | 9 +- .../src/components/icons/editor-app-icons.tsx | 10 +- packages/app/src/components/left-sidebar.tsx | 47 +++-- packages/app/src/components/message.tsx | 170 ++++++++++-------- .../app/src/components/pair-link-modal.tsx | 6 +- packages/app/src/components/pr-pane.tsx | 14 +- .../src/components/project-picker-modal.tsx | 51 ++++-- .../components/provider-diagnostic-sheet.tsx | 33 ++-- packages/app/src/components/resize-handle.tsx | 56 +++--- packages/app/src/components/synced-loader.tsx | 14 +- .../app/src/components/terminal-emulator.tsx | 159 ++++++++-------- .../app/src/components/tool-call-sheet.tsx | 7 +- .../app/src/components/ui/autocomplete.tsx | 5 +- .../src/components/ui/segmented-control.tsx | 6 +- .../app/src/components/welcome-screen.tsx | 12 +- .../src/components/workspace-hover-card.tsx | 5 +- .../src/components/workspace-setup-dialog.tsx | 24 +-- .../contexts/horizontal-scroll-context.tsx | 19 +- .../components/pair-device-section.tsx | 11 +- packages/app/src/panels/file-panel.tsx | 9 +- packages/app/src/panels/setup-panel.tsx | 4 +- packages/app/src/panels/terminal-panel.tsx | 12 +- .../app/src/screens/new-workspace-screen.tsx | 20 ++- .../screens/settings/providers-section.tsx | 19 +- .../app/src/screens/startup-splash-screen.tsx | 16 +- .../workspace/workspace-desktop-tabs-row.tsx | 23 +-- .../workspace/workspace-draft-agent-tab.tsx | 40 +++-- .../workspace/workspace-scripts-button.tsx | 29 +-- .../workspace/workspace-tab-presentation.tsx | 8 +- 42 files changed, 719 insertions(+), 490 deletions(-) diff --git a/packages/app/src/app/_layout.tsx b/packages/app/src/app/_layout.tsx index 895e5e0aa..9c0894ffc 100644 --- a/packages/app/src/app/_layout.tsx +++ b/packages/app/src/app/_layout.tsx @@ -834,19 +834,23 @@ function FaviconStatusSync() { return null; } +const AGENT_SCREEN_OPTIONS = { gestureEnabled: false }; + function RootStack() { const storeReady = useStoreReady(); const { theme } = useUnistyles(); + const stackScreenOptions = useMemo( + () => ({ + headerShown: false, + animation: "none" as const, + contentStyle: { + backgroundColor: theme.colors.surface0, + }, + }), + [theme.colors.surface0], + ); return ( - + @@ -862,7 +866,7 @@ function RootStack() { outside this route-level native-stack API. */} - + diff --git a/packages/app/src/app/settings/[section].tsx b/packages/app/src/app/settings/[section].tsx index 8afddf1c0..e84d71434 100644 --- a/packages/app/src/app/settings/[section].tsx +++ b/packages/app/src/app/settings/[section].tsx @@ -1,4 +1,5 @@ import { useLocalSearchParams } from "expo-router"; +import { useMemo } from "react"; import SettingsScreen from "@/screens/settings-screen"; import { isSettingsSectionSlug, type SettingsSectionSlug } from "@/utils/host-routes"; @@ -6,6 +7,7 @@ export default function SettingsSectionRoute() { const params = useLocalSearchParams<{ section?: string }>(); const rawSection = typeof params.section === "string" ? params.section : ""; const section: SettingsSectionSlug = isSettingsSectionSlug(rawSection) ? rawSection : "general"; + const view = useMemo(() => ({ kind: "section" as const, section }), [section]); - return ; + return ; } diff --git a/packages/app/src/app/settings/hosts/[serverId].tsx b/packages/app/src/app/settings/hosts/[serverId].tsx index 02260acaa..9a3058c45 100644 --- a/packages/app/src/app/settings/hosts/[serverId].tsx +++ b/packages/app/src/app/settings/hosts/[serverId].tsx @@ -1,14 +1,16 @@ import { useLocalSearchParams } from "expo-router"; +import { useMemo } from "react"; import { HostRouteBootstrapBoundary } from "@/components/host-route-bootstrap-boundary"; import SettingsScreen from "@/screens/settings-screen"; export default function SettingsHostRoute() { const params = useLocalSearchParams<{ serverId?: string }>(); const serverId = typeof params.serverId === "string" ? params.serverId.trim() : ""; + const view = useMemo(() => ({ kind: "host" as const, serverId }), [serverId]); return ( - + ); } diff --git a/packages/app/src/app/settings/index.tsx b/packages/app/src/app/settings/index.tsx index 00e212c2a..09280b5c5 100644 --- a/packages/app/src/app/settings/index.tsx +++ b/packages/app/src/app/settings/index.tsx @@ -3,6 +3,8 @@ import { useIsCompactFormFactor } from "@/constants/layout"; import SettingsScreen from "@/screens/settings-screen"; import { buildSettingsSectionRoute } from "@/utils/host-routes"; +const ROOT_VIEW = { kind: "root" as const }; + export default function SettingsIndexRoute() { const isCompactLayout = useIsCompactFormFactor(); @@ -10,5 +12,5 @@ export default function SettingsIndexRoute() { return ; } - return ; + return ; } diff --git a/packages/app/src/components/adaptive-modal-sheet.tsx b/packages/app/src/components/adaptive-modal-sheet.tsx index c54f2fc2f..2cd7f6f9e 100644 --- a/packages/app/src/components/adaptive-modal-sheet.tsx +++ b/packages/app/src/components/adaptive-modal-sheet.tsx @@ -24,6 +24,7 @@ import { isWeb } from "@/constants/platform"; type EscHandler = () => void; const escStack: EscHandler[] = []; let escListenerAttached = false; +const ABSOLUTE_FILL_STYLE = { ...StyleSheet.absoluteFillObject }; function handleEscKeyDown(event: KeyboardEvent) { if (event.key !== "Escape") return; @@ -191,6 +192,10 @@ export function AdaptiveModalSheet({ const isMobile = useIsCompactFormFactor(); const titleColor = theme.colors.foreground; const resolvedSnapPoints = useMemo(() => snapPoints ?? ["65%", "90%"], [snapPoints]); + const handleIndicatorStyle = useMemo( + () => ({ backgroundColor: theme.colors.surface2 }), + [theme.colors.surface2], + ); const { sheetRef, handleSheetChange } = useIsolatedBottomSheetVisibility({ visible, isEnabled: isMobile, @@ -204,6 +209,12 @@ export function AdaptiveModalSheet({ [], ); + const titleStyle = useMemo(() => [styles.title, { color: titleColor }], [titleColor]); + const desktopCardStyle = useMemo( + () => [styles.desktopCard, desktopMaxWidth != null && { maxWidth: desktopMaxWidth }], + [desktopMaxWidth], + ); + useEffect(() => { if (!isWeb || isMobile || !visible) return; return pushEscHandler(onClose); @@ -220,14 +231,14 @@ export function AdaptiveModalSheet({ backdropComponent={renderBackdrop} enablePanDownToClose backgroundComponent={SheetBackground} - handleIndicatorStyle={{ backgroundColor: theme.colors.surface2 }} + handleIndicatorStyle={handleIndicatorStyle} keyboardBehavior="extend" keyboardBlurBehavior="restore" accessible={false} > - + {title} {subtitle} @@ -256,7 +267,7 @@ export function AdaptiveModalSheet({ <> - + {title} {subtitle} @@ -283,12 +294,8 @@ export function AdaptiveModalSheet({ const desktopContent = ( - - + + {onFilesDropped ? ( {cardInner} ) : ( diff --git a/packages/app/src/components/add-host-modal.tsx b/packages/app/src/components/add-host-modal.tsx index d49f0b232..37f644c87 100644 --- a/packages/app/src/components/add-host-modal.tsx +++ b/packages/app/src/components/add-host-modal.tsx @@ -10,6 +10,8 @@ import { DaemonConnectionTestError, connectToDaemon } from "@/utils/test-daemon- import { AdaptiveModalSheet, AdaptiveTextInput } from "./adaptive-modal-sheet"; import { Button } from "@/components/ui/button"; +const FLEX_ONE_STYLE = { flex: 1 } as const; + const styles = StyleSheet.create((theme) => ({ field: { gap: theme.spacing[2], @@ -274,11 +276,16 @@ export function AddHostModal({ visible, onClose, onCancel, onSaved }: AddHostMod -