Push the whole composer up with the keyboard on mobile

The keyboard shift was applied only to the input box inside the
composer, so the subagents track and the draft import-session pill —
rendered as siblings above the composer — stayed behind the keyboard.
Lift the shift to the wrapper that holds the full composer column and
add an opt-out prop so the composer skips its own shift when a parent
owns it.
This commit is contained in:
Mohamed Boudra
2026-05-29 12:20:00 +07:00
parent bb2f540f29
commit 1350167dda
3 changed files with 24 additions and 8 deletions
@@ -1,7 +1,9 @@
import { useCallback, useEffect, useMemo, useRef } from "react";
import { Keyboard, ScrollView, Text, View } from "react-native";
import ReanimatedAnimated from "react-native-reanimated";
import { StyleSheet } from "react-native-unistyles";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useKeyboardShiftStyle } from "@/hooks/use-keyboard-shift-style";
import invariant from "tiny-invariant";
import { Composer } from "@/composer";
import { DraftAgentModeControl } from "@/composer/agent-controls/mode-control";
@@ -584,9 +586,13 @@ export function WorkspaceDraftAgentTab({
[composerState],
);
const { style: composerKeyboardStyle } = useKeyboardShiftStyle({
mode: "translate",
});
const inputAreaWrapperStyle = useMemo(
() => [styles.inputAreaWrapper, { paddingBottom: insets.bottom }],
[insets.bottom],
() => [styles.inputAreaWrapper, { paddingBottom: insets.bottom }, composerKeyboardStyle],
[insets.bottom, composerKeyboardStyle],
);
const handleDropdownCloseFocus = useCallback(() => {
@@ -656,7 +662,7 @@ export function WorkspaceDraftAgentTab({
)}
</View>
<View style={inputAreaWrapperStyle}>
<ReanimatedAnimated.View style={inputAreaWrapperStyle}>
{importPillPress ? (
<View style={styles.importPillRow}>
<View style={styles.importPillContent}>
@@ -667,6 +673,7 @@ export function WorkspaceDraftAgentTab({
<Composer
agentId={tabId}
serverId={serverId}
externalKeyboardShift
isPaneFocused={isPaneFocused}
onSubmitMessage={handleCreateFromInput}
isSubmitLoading={isSubmitting}
@@ -686,7 +693,7 @@ export function WorkspaceDraftAgentTab({
agentControls={composerAgentControls}
footer={composerFooter}
/>
</View>
</ReanimatedAnimated.View>
</View>
</FileDropZone>
);
+4
View File
@@ -665,6 +665,8 @@ interface ComposerProps {
inputWrapperStyle?: import("react-native").ViewStyle;
/** Rendered below the input, inside the keyboard-shifted container. */
footer?: ReactNode;
/** When true, a parent wrapper owns the keyboard shift, so the composer skips its own. */
externalKeyboardShift?: boolean;
}
const EMPTY_ARRAY: readonly QueuedMessage[] = [];
@@ -859,6 +861,7 @@ export function Composer({
agentControls,
inputWrapperStyle,
footer,
externalKeyboardShift,
}: ComposerProps) {
const buttonIconSize = resolveComposerButtonIconSize();
const client = useHostRuntimeClient(serverId);
@@ -1307,6 +1310,7 @@ export function Composer({
const { style: keyboardAnimatedStyle } = useKeyboardShiftStyle({
mode: "translate",
enabled: !externalKeyboardShift,
});
const isVoiceModeForAgent = resolveIsVoiceModeForAgent(voice, serverId, agentId);
+9 -4
View File
@@ -1392,9 +1392,13 @@ function ActiveAgentComposer({
],
);
const { style: composerKeyboardStyle } = useKeyboardShiftStyle({
mode: "translate",
});
const inputAreaStyle = useMemo(
() => [styles.inputAreaWrapper, { paddingBottom: insets.bottom }],
[insets.bottom],
() => [styles.inputAreaWrapper, { paddingBottom: insets.bottom }, composerKeyboardStyle],
[insets.bottom, composerKeyboardStyle],
);
const composerFooter = useMemo(
@@ -1406,7 +1410,7 @@ function ActiveAgentComposer({
);
return (
<View style={inputAreaStyle}>
<ReanimatedAnimated.View style={inputAreaStyle}>
<SubagentsTrack
rows={subagentRows}
onOpenSubagent={handleOpenSubagent}
@@ -1415,6 +1419,7 @@ function ActiveAgentComposer({
<Composer
agentId={agentId}
serverId={serverId}
externalKeyboardShift
isPaneFocused={isPaneFocused}
value={agentInputDraft.text}
onChangeText={agentInputDraft.setText}
@@ -1434,7 +1439,7 @@ function ActiveAgentComposer({
onClientSlashCommand={handleClientSlashCommand}
footer={composerFooter}
/>
</View>
</ReanimatedAnimated.View>
);
}