diff --git a/packages/app/src/components/composer.tsx b/packages/app/src/components/composer.tsx index db742ffef..b5d290255 100644 --- a/packages/app/src/components/composer.tsx +++ b/packages/app/src/components/composer.tsx @@ -14,7 +14,6 @@ import { Paperclip, } from "lucide-react-native"; import Animated from "react-native-reanimated"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useQuery } from "@tanstack/react-query"; import { FOOTER_HEIGHT, MAX_CONTENT_WIDTH } from "@/constants/layout"; import { generateMessageId, type StreamItem } from "@/types/stream"; @@ -169,7 +168,6 @@ export function Composer({ markScrollInvestigationRender(`Composer:${serverId}:${agentId}`); const { theme } = useUnistyles(); const buttonIconSize = isWeb ? theme.iconSize.md : theme.iconSize.lg; - const insets = useSafeAreaInsets(); const client = useHostRuntimeClient(serverId); const isConnected = useHostRuntimeIsConnected(serverId); const agentDirectoryStatus = useHostRuntimeAgentDirectoryStatus(serverId); @@ -882,9 +880,7 @@ export function Composer({ ); return ( - + setLightboxMetadata(null)} /> {/* Input area */} diff --git a/packages/app/src/panels/agent-panel.test.tsx b/packages/app/src/panels/agent-panel.test.tsx index b5fd07dbc..fe701287b 100644 --- a/packages/app/src/panels/agent-panel.test.tsx +++ b/packages/app/src/panels/agent-panel.test.tsx @@ -108,6 +108,10 @@ vi.mock("react-native-unistyles", () => { }; }); +vi.mock("react-native-safe-area-context", () => ({ + useSafeAreaInsets: () => ({ top: 0, right: 0, bottom: 0, left: 0 }), +})); + function createPanelTestStyles(factory: PanelTestStyleFactory | PanelTestStyles): PanelTestStyles { return typeof factory === "function" ? factory(theme) : factory; } diff --git a/packages/app/src/panels/agent-panel.tsx b/packages/app/src/panels/agent-panel.tsx index 70d8f630a..6533f52e9 100644 --- a/packages/app/src/panels/agent-panel.tsx +++ b/packages/app/src/panels/agent-panel.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react" import { ActivityIndicator, Text, View } from "react-native"; import ReanimatedAnimated from "react-native-reanimated"; import { StyleSheet, useUnistyles } from "react-native-unistyles"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; import { shallow, useShallow } from "zustand/shallow"; import { useStoreWithEqualityFn } from "zustand/traditional"; import invariant from "tiny-invariant"; @@ -1165,6 +1166,7 @@ function ActiveAgentComposer({ onComposerHeightChange: (height: number) => void; onMessageSent: () => void; }) { + const insets = useSafeAreaInsets(); const agentInputDraft = useAgentInputDraft({ draftKey: buildDraftStoreKey({ serverId, @@ -1174,24 +1176,26 @@ function ActiveAgentComposer({ }); return ( - + + + ); } @@ -1271,6 +1275,10 @@ const styles = StyleSheet.create((theme) => ({ content: { flex: 1, }, + inputAreaWrapper: { + width: "100%", + backgroundColor: theme.colors.surface0, + }, historySyncOverlay: { position: "absolute", top: 0, diff --git a/packages/app/src/screens/agent/draft-agent-screen.tsx b/packages/app/src/screens/agent/draft-agent-screen.tsx index f78d64f23..c1afafcd2 100644 --- a/packages/app/src/screens/agent/draft-agent-screen.tsx +++ b/packages/app/src/screens/agent/draft-agent-screen.tsx @@ -1221,7 +1221,7 @@ function DraftAgentScreenContent({ )} - + ({ useIsCompactFormFactor: () => false, })); +vi.mock("react-native-safe-area-context", () => ({ + useSafeAreaInsets: () => ({ top: 0, right: 0, bottom: 0, left: 0 }), +})); + vi.mock("lucide-react-native", () => { const createIcon = (name: string) => (props: Record) => React.createElement("span", { ...props, "data-icon": name }); diff --git a/packages/app/src/screens/new-workspace-screen.tsx b/packages/app/src/screens/new-workspace-screen.tsx index badd5dc0b..ecb03b6bf 100644 --- a/packages/app/src/screens/new-workspace-screen.tsx +++ b/packages/app/src/screens/new-workspace-screen.tsx @@ -1,6 +1,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Pressable, Text, View } from "react-native"; import { StyleSheet, useUnistyles } from "react-native-unistyles"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; import { createNameId } from "mnemonic-id"; import { useQuery } from "@tanstack/react-query"; import { ChevronDown, GitBranch, GitPullRequest } from "lucide-react-native"; @@ -12,12 +13,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip import { TitlebarDragRegion } from "@/components/desktop/titlebar-drag-region"; import { SidebarMenuToggle } from "@/components/headers/menu-header"; import { ScreenHeader } from "@/components/headers/screen-header"; -import { - HEADER_INNER_HEIGHT, - HEADER_INNER_HEIGHT_MOBILE, - HEADER_TOP_PADDING_MOBILE, - MAX_CONTENT_WIDTH, -} from "@/constants/layout"; +import { HEADER_INNER_HEIGHT, MAX_CONTENT_WIDTH, useIsCompactFormFactor } from "@/constants/layout"; import { useToast } from "@/contexts/toast-context"; import { useAgentInputDraft } from "@/hooks/use-agent-input-draft"; import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime"; @@ -107,6 +103,8 @@ export function NewWorkspaceScreen({ displayName: displayNameProp, }: NewWorkspaceScreenProps) { const { theme } = useUnistyles(); + const insets = useSafeAreaInsets(); + const isCompact = useIsCompactFormFactor(); const toast = useToast(); const mergeWorkspaces = useSessionStore((state) => state.mergeWorkspaces); const setAgents = useSessionStore((state) => state.setAgents); @@ -453,7 +451,13 @@ export function NewWorkspaceScreen({ leftStyle={styles.headerLeft} borderless /> - + ({ content: { position: "relative", flex: 1, - justifyContent: "center", alignItems: "center", - paddingBottom: { - xs: HEADER_INNER_HEIGHT_MOBILE + HEADER_TOP_PADDING_MOBILE + theme.spacing[6], - md: HEADER_INNER_HEIGHT + theme.spacing[6], - }, + }, + contentCentered: { + justifyContent: "center", + paddingBottom: HEADER_INNER_HEIGHT + theme.spacing[6], + }, + contentCompact: { + justifyContent: "flex-end", }, centered: { width: "100%", diff --git a/packages/app/src/screens/workspace/workspace-draft-agent-tab.tsx b/packages/app/src/screens/workspace/workspace-draft-agent-tab.tsx index 71e254cde..f89fc6137 100644 --- a/packages/app/src/screens/workspace/workspace-draft-agent-tab.tsx +++ b/packages/app/src/screens/workspace/workspace-draft-agent-tab.tsx @@ -1,6 +1,7 @@ import { useCallback, useMemo, useRef } from "react"; import { Keyboard, ScrollView, Text, View } from "react-native"; import { StyleSheet } from "react-native-unistyles"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; import invariant from "tiny-invariant"; import { Composer } from "@/components/composer"; import { FileDropZone } from "@/components/file-drop-zone"; @@ -48,6 +49,7 @@ export function WorkspaceDraftAgentTab({ onCreated, onOpenWorkspaceFile, }: WorkspaceDraftAgentTabProps) { + const insets = useSafeAreaInsets(); const client = useHostRuntimeClient(serverId); const isConnected = useHostRuntimeIsConnected(serverId); const workspaceAuthority = useWorkspaceExecutionAuthority(serverId, workspaceId); @@ -282,7 +284,7 @@ export function WorkspaceDraftAgentTab({ )} - +