Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions apps/mobile/src/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
useClerkSettingsSheetDetent,
} from "../features/cloud/ClerkSettingsSheetDetent";
import { useAgentNotificationNavigation } from "../features/agent-awareness/notificationNavigation";
import { pushScreenAnimation } from "../lib/pushScreenAnimation";
import { useHeaderBlurEffect } from "../lib/useHeaderBlurEffect";
import { useThemeColor } from "../lib/useThemeColor";

function AppNavigator() {
Expand All @@ -45,7 +47,9 @@ function AppNavigatorContent() {
const { collapse, isExpanded } = useClerkSettingsSheetDetent();
const colorScheme = useColorScheme();
const statusBarBg = useThemeColor("--color-status-bar");
const screenColor = useThemeColor("--color-screen");
const sheetStyle = useResolveClassNames("bg-sheet");
const headerBlurEffect = useHeaderBlurEffect();
useAgentNotificationNavigation();
useThreadOutboxDrain();

Expand Down Expand Up @@ -96,9 +100,10 @@ function AppNavigatorContent() {
<Stack.Screen
name="index"
options={{
contentStyle: { backgroundColor: "transparent" },
contentStyle: { backgroundColor: screenColor },
headerShown: true,
headerTransparent: true,
headerBlurEffect,
headerShadowVisible: false,
}}
/>
Expand All @@ -112,9 +117,10 @@ function AppNavigatorContent() {
<Stack.Screen
name="threads/[environmentId]/[threadId]"
options={{
animation: "slide_from_right",
contentStyle: { backgroundColor: "transparent" },
animation: pushScreenAnimation,
contentStyle: { backgroundColor: screenColor },
gestureEnabled: true,
fullScreenGestureEnabled: true,
headerShown: false,
}}
/>
Expand Down
3 changes: 2 additions & 1 deletion apps/mobile/src/app/connections/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Stack from "expo-router/stack";
import { useResolveClassNames } from "uniwind";
import { pushScreenAnimation } from "../../lib/pushScreenAnimation";
import { useThemeColor } from "../../lib/useThemeColor";

export const unstable_settings = {
Expand All @@ -23,7 +24,7 @@ export default function ConnectionsLayout() {
}}
>
<Stack.Screen name="index" options={{ animation: "none" }} />
<Stack.Screen name="new" options={{ animation: "slide_from_right" }} />
<Stack.Screen name="new" options={{ animation: pushScreenAnimation }} />
</Stack>
);
}
14 changes: 9 additions & 5 deletions apps/mobile/src/app/new/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Stack from "expo-router/stack";
import { useResolveClassNames } from "uniwind";

import { NewTaskFlowProvider } from "../../features/threads/new-task-flow-provider";
import { pushScreenAnimation } from "../../lib/pushScreenAnimation";
import { useThemeColor } from "../../lib/useThemeColor";

export const unstable_settings = {
Expand Down Expand Up @@ -29,21 +30,24 @@ export default function NewTaskLayout() {
<Stack.Screen name="index" options={{ animation: "none", title: "Choose project" }} />
<Stack.Screen
name="add-project/index"
options={{ animation: "slide_from_right", title: "New project" }}
options={{ animation: pushScreenAnimation, title: "New project" }}
/>
<Stack.Screen
name="add-project/repository"
options={{ animation: "slide_from_right", title: "Repository" }}
options={{ animation: pushScreenAnimation, title: "Repository" }}
/>
<Stack.Screen
name="add-project/destination"
options={{ animation: "slide_from_right", title: "Clone destination" }}
options={{ animation: pushScreenAnimation, title: "Clone destination" }}
/>
<Stack.Screen
name="add-project/local"
options={{ animation: "slide_from_right", title: "Local folder" }}
options={{ animation: pushScreenAnimation, title: "Local folder" }}
/>
<Stack.Screen
name="draft"
options={{ animation: pushScreenAnimation, title: "New task" }}
/>
<Stack.Screen name="draft" options={{ animation: "slide_from_right", title: "New task" }} />
</Stack>
</NewTaskFlowProvider>
);
Expand Down
11 changes: 6 additions & 5 deletions apps/mobile/src/app/settings/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useCallback } from "react";
import { useResolveClassNames } from "uniwind";

import { useClerkSettingsSheetDetent } from "../../features/cloud/ClerkSettingsSheetDetent";
import { pushScreenAnimation } from "../../lib/pushScreenAnimation";
import { useThemeColor } from "../../lib/useThemeColor";

export const unstable_settings = {
Expand Down Expand Up @@ -37,25 +38,25 @@ export default function SettingsLayout() {
<Stack.Screen name="index" options={{ animation: "none", title: "Settings" }} />
<Stack.Screen
name="environments"
options={{ animation: "slide_from_right", title: "Environments" }}
options={{ animation: pushScreenAnimation, title: "Environments" }}
/>
<Stack.Screen
name="environment-new"
options={{ animation: "slide_from_right", title: "Add Environment" }}
options={{ animation: pushScreenAnimation, title: "Add Environment" }}
/>
<Stack.Screen
name="waitlist"
options={{ animation: "slide_from_right", title: "Join the waitlist" }}
options={{ animation: pushScreenAnimation, title: "Join the waitlist" }}
/>
<Stack.Screen
name="archive"
listeners={{ transitionEnd: handleExpandedRouteTransitionEnd }}
options={{ animation: "slide_from_right", title: "Archived Threads" }}
options={{ animation: pushScreenAnimation, title: "Archived Threads" }}
/>
<Stack.Screen
name="auth"
listeners={{ transitionEnd: handleExpandedRouteTransitionEnd }}
options={{ animation: "slide_from_right", title: "Sign in" }}
options={{ animation: pushScreenAnimation, title: "Sign in" }}
/>
</Stack>
);
Expand Down
19 changes: 15 additions & 4 deletions apps/mobile/src/app/threads/[environmentId]/[threadId]/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import Stack from "expo-router/stack";
import { StyleSheet } from "react-native";
import { useResolveClassNames } from "uniwind";

import { pushScreenAnimation } from "../../../../lib/pushScreenAnimation";
import { useHeaderBlurEffect } from "../../../../lib/useHeaderBlurEffect";

export default function ThreadLayout() {
const headerBlurEffect = useHeaderBlurEffect();
const sheetStyle = StyleSheet.flatten(useResolveClassNames("bg-sheet"));
const headerBg = {
backgroundColor: (sheetStyle as { backgroundColor?: string })?.backgroundColor,
Expand All @@ -16,6 +20,7 @@ export default function ThreadLayout() {
contentStyle: { backgroundColor: "transparent" },
headerShown: true,
headerTransparent: true,
headerBlurEffect,
headerShadowVisible: false,
headerTitle: "",
}}
Expand Down Expand Up @@ -45,8 +50,9 @@ export default function ThreadLayout() {
<Stack.Screen
name="review"
options={{
animation: "slide_from_right",
animation: pushScreenAnimation,
contentStyle: sheetStyle,
fullScreenGestureEnabled: true,
headerBackButtonDisplayMode: "minimal",
headerShown: true,
headerTitle: "Files changed",
Expand All @@ -58,8 +64,9 @@ export default function ThreadLayout() {
<Stack.Screen
name="files/index"
options={{
animation: "slide_from_right",
animation: pushScreenAnimation,
contentStyle: sheetStyle,
fullScreenGestureEnabled: true,
headerBackButtonDisplayMode: "minimal",
headerShown: true,
headerTitle: "Files",
Expand All @@ -71,8 +78,9 @@ export default function ThreadLayout() {
<Stack.Screen
name="files/[...path]"
options={{
animation: "slide_from_right",
animation: pushScreenAnimation,
contentStyle: sheetStyle,
fullScreenGestureEnabled: true,
headerBackButtonDisplayMode: "minimal",
headerShown: true,
headerTitle: "File",
Expand All @@ -95,8 +103,11 @@ export default function ThreadLayout() {
<Stack.Screen
name="terminal"
options={{
animation: "slide_from_right",
animation: pushScreenAnimation,
contentStyle: { backgroundColor: "#050505" },
// No fullScreenGestureEnabled here: the terminal consumes
// horizontal pans (readline editing, mouse reporting), so
// back-swipe stays confined to the screen edge.
headerBackButtonDisplayMode: "minimal",
headerShown: true,
headerShadowVisible: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import Stack from "expo-router/stack";
import { StyleSheet } from "react-native";
import { useResolveClassNames } from "uniwind";

import { pushScreenAnimation } from "../../../../../lib/pushScreenAnimation";

export const unstable_settings = {
anchor: "index",
};
Expand All @@ -23,7 +25,7 @@ export default function GitSheetLayout() {
<Stack.Screen
name="commit"
options={{
animation: "slide_from_right",
animation: pushScreenAnimation,
headerShown: true,
headerTitle: "Commit changes",
headerBackTitle: "",
Expand All @@ -34,7 +36,7 @@ export default function GitSheetLayout() {
<Stack.Screen
name="branches"
options={{
animation: "slide_from_right",
animation: pushScreenAnimation,
headerShown: true,
headerTitle: "Branches & worktrees",
headerBackTitle: "",
Expand All @@ -45,7 +47,7 @@ export default function GitSheetLayout() {
<Stack.Screen
name="review"
options={{
animation: "slide_from_right",
animation: pushScreenAnimation,
headerShown: true,
headerTitle: "Review changes",
headerBackTitle: "",
Expand Down
3 changes: 3 additions & 0 deletions apps/mobile/src/features/files/ThreadFilesRouteScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { cn } from "../../lib/cn";
import { tryOpenExternalUrl } from "../../lib/openExternalUrl";
import { buildThreadFilesNavigation } from "../../lib/routes";
import { MOBILE_TYPOGRAPHY } from "../../lib/typography";
import { useHeaderBlurEffect } from "../../lib/useHeaderBlurEffect";
import { useThemeColor } from "../../lib/useThemeColor";
import { useThreadSelection } from "../../state/use-thread-selection";
import { useSelectedThreadWorktree } from "../../state/use-selected-thread-worktree";
Expand Down Expand Up @@ -444,6 +445,7 @@ function FilesToolbarBottomFade() {

export function ThreadFilesTreeScreen() {
const router = useRouter();
const headerBlurEffect = useHeaderBlurEffect();
const [searchQuery, setSearchQuery] = useState("");
const { cwd, environmentId, projectName, selectedThread, threadId } = useThreadFilesWorkspace();
const entriesQuery = useEnvironmentQuery(
Expand Down Expand Up @@ -481,6 +483,7 @@ export function ThreadFilesTreeScreen() {
title: "Files",
headerShown: true,
headerTransparent: true,
headerBlurEffect,
headerStyle: { backgroundColor: "transparent" },
headerShadowVisible: false,
headerTitle: () => <FilesHeaderTitle projectName={projectName} />,
Expand Down
3 changes: 3 additions & 0 deletions apps/mobile/src/features/home/HomeHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { Stack } from "expo-router";
import { Text as RNText, View } from "react-native";

import { useHeaderBlurEffect } from "../../lib/useHeaderBlurEffect";
import { useThemeColor } from "../../lib/useThemeColor";
import { MOBILE_TYPOGRAPHY } from "../../lib/typography";
import type { HomeProjectSortOrder } from "./homeThreadList";
Expand Down Expand Up @@ -72,6 +73,7 @@ export function HomeHeader(props: {
readonly onOpenSettings: () => void;
readonly onStartNewTask: () => void;
}) {
const headerBlurEffect = useHeaderBlurEffect();
const iconColor = useThemeColor("--color-icon");
const mutedColor = useThemeColor("--color-foreground-muted");
const subtleColor = useThemeColor("--color-subtle");
Expand All @@ -88,6 +90,7 @@ export function HomeHeader(props: {
headerShown: true,
headerTransparent: true,
headerStyle: { backgroundColor: "transparent" },
headerBlurEffect,
headerShadowVisible: false,
headerTintColor: iconColor,
headerTitle: "",
Expand Down
3 changes: 3 additions & 0 deletions apps/mobile/src/features/review/ReviewSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { AppText as Text } from "../../components/AppText";
import { environmentCatalog } from "../../connection/catalog";
import { useEnvironmentPresentation } from "../../state/presentation";
import { useAtomCommand } from "../../state/use-atom-command";
import { useHeaderBlurEffect } from "../../lib/useHeaderBlurEffect";
import { useThemeColor } from "../../lib/useThemeColor";
import { MOBILE_TYPOGRAPHY } from "../../lib/typography";
import { useThreadDraftForThread } from "../../state/use-thread-composer-state";
Expand Down Expand Up @@ -113,6 +114,7 @@ function ReviewSelectionActionBar(props: {
export function ReviewSheet() {
const insets = useSafeAreaInsets();
const colorScheme = useColorScheme();
const headerBlurEffect = useHeaderBlurEffect();
const headerForeground = String(useThemeColor("--color-foreground"));
const headerMuted = String(useThemeColor("--color-foreground-muted"));
const headerIcon = String(useThemeColor("--color-icon"));
Expand Down Expand Up @@ -241,6 +243,7 @@ export function ReviewSheet() {
<Stack.Screen
options={{
headerTransparent: true,
headerBlurEffect,
headerShadowVisible: false,
headerTintColor: headerIcon,
headerStyle: {
Expand Down
6 changes: 5 additions & 1 deletion apps/mobile/src/features/threads/ThreadDetailScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,11 @@ export const ThreadDetailScreen = memo(function ThreadDetailScreen(props: Thread
() =>
Gesture.Pan()
.enabled(!isSplitLayout)
.hitSlop({ left: 0, width: 40 })
// Confine the drawer pan to the top-left corner where it completes
// (see the `event.y` check below). Covering the full left edge stole
// the touch from the native swipe-back gesture, leaving back
// navigation dead below the header.
.hitSlop({ left: 0, width: 40, top: 0, height: drawerGestureThreshold })
.activeOffsetX([10, 999])
.failOffsetY([-24, 24])
.onEnd((event) => {
Expand Down
3 changes: 3 additions & 0 deletions apps/mobile/src/features/threads/ThreadRouteScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { EnvironmentId, type ProjectScript } from "@t3tools/contracts";
import { projectScriptCwd, projectScriptRuntimeEnv } from "@t3tools/shared/projectScripts";
import { Pressable, ScrollView, Text as RNText, View } from "react-native";
import { useWorkspaceState } from "../../state/workspace";
import { useHeaderBlurEffect } from "../../lib/useHeaderBlurEffect";
import { useThemeColor } from "../../lib/useThemeColor";
import { useEnvironmentQuery } from "../../state/query";
import { dismissGitActionResult, useGitActionProgress } from "../../state/use-vcs-action-state";
Expand Down Expand Up @@ -104,6 +105,7 @@ export function ThreadRouteScreen() {
const iconColor = String(useThemeColor("--color-icon"));
const foregroundColor = String(useThemeColor("--color-foreground"));
const secondaryFg = String(useThemeColor("--color-foreground-secondary"));
const headerBlurEffect = useHeaderBlurEffect();

/* ─── Git status for native header trigger ───────────────────────── */
const gitStatus = useEnvironmentQuery(
Expand Down Expand Up @@ -326,6 +328,7 @@ export function ThreadRouteScreen() {
headerShown: true,
headerTransparent: true,
headerStyle: { backgroundColor: "transparent" },
headerBlurEffect,
headerShadowVisible: false,
headerTintColor: iconColor,
headerBackTitle: "",
Expand Down
12 changes: 12 additions & 0 deletions apps/mobile/src/lib/pushScreenAnimation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Platform } from "react-native";

/**
* Stack animation for pushed (non-sheet) screens.
*
* iOS keeps the default push animation: forcing `slide_from_right` switches
* react-native-screens to its custom swipe animator, which paints a black
* void behind the outgoing screen during interactive swipe-back. The native
* default is visually the same slide with proper parallax over the previous
* screen. Android has no interactive pop, so it keeps `slide_from_right`.
*/
export const pushScreenAnimation = Platform.OS === "ios" ? "default" : "slide_from_right";
15 changes: 15 additions & 0 deletions apps/mobile/src/lib/useHeaderBlurEffect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useColorScheme } from "react-native";

/**
* Blur effect for transparent navigation headers.
*
* Recent iOS betas stopped drawing the implicit material behind transparent
* navigation bars, and the trait-adaptive `systemChromeMaterial` resolves to
* its light variant there even while the app renders in dark mode — so pick
* the light/dark variant explicitly from the app color scheme.
*/
export function useHeaderBlurEffect() {
return useColorScheme() === "dark"
? ("systemChromeMaterialDark" as const)
: ("systemChromeMaterialLight" as const);
}
Loading