diff --git a/packages/app/src/components/file-explorer-pane.tsx b/packages/app/src/components/file-explorer-pane.tsx index 4b685dacb..3ad814a77 100644 --- a/packages/app/src/components/file-explorer-pane.tsx +++ b/packages/app/src/components/file-explorer-pane.tsx @@ -16,18 +16,17 @@ import { View, useWindowDimensions, } from "react-native"; -import { StyleSheet, useUnistyles } from "react-native-unistyles"; +import { StyleSheet, UnistylesRuntime, useUnistyles } from "react-native-unistyles"; import { Fonts } from "@/constants/theme"; import * as Clipboard from "expo-clipboard"; import { BottomSheetModal, BottomSheetScrollView, BottomSheetBackdrop, - BottomSheetView, } from "@gorhom/bottom-sheet"; import { + ArrowLeft, ChevronDown, - Download, File, FileText, Folder, @@ -64,6 +63,8 @@ export function FileExplorerPane({ agentId, }: FileExplorerPaneProps) { const { theme } = useUnistyles(); + const isMobile = + UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm"; const { connectionStates } = useDaemonConnections(); const daemonProfile = connectionStates.get(serverId)?.daemon; @@ -335,12 +336,15 @@ export function FileExplorerPane({ // Open/close preview sheet based on selection useEffect(() => { + if (!isMobile) { + return; + } if (selectedEntryPath) { previewSheetRef.current?.present(); } else { previewSheetRef.current?.dismiss(); } - }, [selectedEntryPath]); + }, [isMobile, selectedEntryPath]); useEffect(() => { if (shouldShowPreview) { @@ -484,6 +488,12 @@ export function FileExplorerPane({ [] ); + const inlinePreviewTitle = useMemo(() => { + return selectedEntryPath?.split("/").pop() ?? "Preview"; + }, [selectedEntryPath]); + + const shouldShowInlinePreview = !isMobile && Boolean(selectedEntryPath); + const handleRetryDirectory = useCallback(() => { if (!agentId || !requestDirectoryListing) { return; @@ -693,7 +703,68 @@ export function FileExplorerPane({ - {error ? ( + {shouldShowInlinePreview ? ( + + + + + + + + + {inlinePreviewTitle} + + {selectedEntryPath ? ( + + {selectedEntryPath} + + ) : null} + + + + {isPreviewLoading && !preview ? ( + + + Loading file... + + ) : !preview ? ( + + No preview available yet + + ) : preview.kind === "text" ? ( + + + {preview.content} + + + ) : preview.kind === "image" && preview.content ? ( + + + + ) : ( + + Binary preview unavailable + + {formatFileSize({ size: preview.size })} + + + )} + + ) : error ? ( {error} @@ -809,64 +880,66 @@ export function FileExplorerPane({ - - - - {selectedEntryPath?.split("/").pop() ?? "Preview"} - - - - - - {isPreviewLoading && !preview ? ( - - - Loading file... - - ) : !preview ? ( - - No preview available yet - - ) : preview.kind === "text" ? ( - - - {preview.content} - - - ) : preview.kind === "image" && preview.content ? ( - - - - ) : ( - - Binary preview unavailable - - {formatFileSize({ size: preview.size })} + {isMobile ? ( + + + + {selectedEntryPath?.split("/").pop() ?? "Preview"} + + + - )} - + {isPreviewLoading && !preview ? ( + + + Loading file... + + ) : !preview ? ( + + No preview available yet + + ) : preview.kind === "text" ? ( + + + {preview.content} + + + ) : preview.kind === "image" && preview.content ? ( + + + + ) : ( + + Binary preview unavailable + + {formatFileSize({ size: preview.size })} + + + )} + + ) : null} ); } @@ -1220,6 +1293,40 @@ const styles = StyleSheet.create((theme) => ({ color: theme.colors.foregroundMuted, fontSize: theme.fontSize.xs, }, + inlinePreviewContainer: { + flex: 1, + borderRadius: theme.borderRadius.lg, + borderWidth: theme.borderWidth[1], + borderColor: theme.colors.border, + backgroundColor: theme.colors.surface2, + overflow: "hidden", + }, + inlinePreviewHeader: { + flexDirection: "row", + alignItems: "center", + gap: theme.spacing[1], + paddingHorizontal: theme.spacing[3], + paddingVertical: theme.spacing[2], + borderBottomWidth: theme.borderWidth[1], + borderBottomColor: theme.colors.border, + }, + inlinePreviewBackButton: { + padding: theme.spacing[1], + }, + inlinePreviewTitleContainer: { + flex: 1, + minWidth: 0, + }, + inlinePreviewTitle: { + color: theme.colors.foreground, + fontSize: theme.fontSize.base, + fontWeight: theme.fontWeight.semibold, + }, + inlinePreviewSubtitle: { + color: theme.colors.foregroundMuted, + fontSize: theme.fontSize.xs, + fontFamily: Fonts.mono, + }, sheetBackground: { backgroundColor: theme.colors.surface2, },