Hide Changes tab when not git repo and add refresh button to Files pane

- Hide Changes tab completely when isGit is false instead of just muting it
- Automatically switch to Files tab if user was on Changes when it becomes hidden
- Add refresh button next to sort button in Files pane header
- Refresh button refreshes root directory, expanded directories, and file preview if open
This commit is contained in:
Mohamed Boudra
2026-02-12 14:05:46 +07:00
parent 5f864a4dd7
commit f55e010a19
2 changed files with 82 additions and 19 deletions
@@ -263,25 +263,33 @@ function SidebarContent({
const { status } = useCheckoutStatusQuery({ serverId, cwd }); const { status } = useCheckoutStatusQuery({ serverId, cwd });
const isGit = status?.isGit ?? false; const isGit = status?.isGit ?? false;
// Switch to Files tab if Changes tab is hidden and user was on it
useEffect(() => {
if (!isGit && activeTab === "changes") {
onTabPress("files");
}
}, [isGit, activeTab, onTabPress]);
return ( return (
<View style={styles.sidebarContent} pointerEvents="auto"> <View style={styles.sidebarContent} pointerEvents="auto">
{/* Header with tabs and close button */} {/* Header with tabs and close button */}
<View style={styles.header} testID="explorer-header"> <View style={styles.header} testID="explorer-header">
<View style={styles.tabsContainer}> <View style={styles.tabsContainer}>
<Pressable {isGit && (
style={[styles.tab, activeTab === "changes" && styles.tabActive]} <Pressable
onPress={() => onTabPress("changes")} style={[styles.tab, activeTab === "changes" && styles.tabActive]}
> onPress={() => onTabPress("changes")}
<Text
style={[
styles.tabText,
activeTab === "changes" && styles.tabTextActive,
!isGit && styles.tabTextMuted,
]}
> >
Changes <Text
</Text> style={[
</Pressable> styles.tabText,
activeTab === "changes" && styles.tabTextActive,
]}
>
Changes
</Text>
</Pressable>
)}
<Pressable <Pressable
style={[styles.tab, activeTab === "files" && styles.tabActive]} style={[styles.tab, activeTab === "files" && styles.tabActive]}
onPress={() => onTabPress("files")} onPress={() => onTabPress("files")}
@@ -33,6 +33,7 @@ import {
FolderOpen, FolderOpen,
Image as ImageIcon, Image as ImageIcon,
MoreVertical, MoreVertical,
RotateCw,
X, X,
} from "lucide-react-native"; } from "lucide-react-native";
import type { import type {
@@ -263,6 +264,26 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
setSortOption(SORT_OPTIONS[nextIndex].value); setSortOption(SORT_OPTIONS[nextIndex].value);
}, [sortOption, setSortOption]); }, [sortOption, setSortOption]);
const handleRefresh = useCallback(() => {
if (!agentId || !requestDirectoryListing) {
return;
}
// Refresh the root directory
requestDirectoryListing(agentId, ".", { recordHistory: false, setCurrentPath: false });
// Also refresh any expanded directories
expandedPaths.forEach((path) => {
if (path !== ".") {
requestDirectoryListing(agentId, path, { recordHistory: false, setCurrentPath: false });
}
});
// Refresh the preview if a file is selected
if (selectedEntryPath && requestFilePreview) {
requestFilePreview(agentId, selectedEntryPath);
}
}, [agentId, expandedPaths, requestDirectoryListing, requestFilePreview, selectedEntryPath]);
const currentSortLabel = SORT_OPTIONS.find((opt) => opt.value === sortOption)?.label ?? "Name"; const currentSortLabel = SORT_OPTIONS.find((opt) => opt.value === sortOption)?.label ?? "Name";
const treeRows = useMemo(() => { const treeRows = useMemo(() => {
@@ -603,9 +624,23 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
</GestureDetector> </GestureDetector>
<View style={styles.paneHeader} testID="files-pane-header"> <View style={styles.paneHeader} testID="files-pane-header">
<View style={styles.paneHeaderLeft} /> <View style={styles.paneHeaderLeft} />
<Pressable style={styles.sortButton} onPress={handleSortCycle}> <View style={styles.paneHeaderRight}>
<Text style={styles.sortButtonText}>{currentSortLabel}</Text> <Pressable
</Pressable> onPress={handleRefresh}
hitSlop={8}
style={({ hovered, pressed }) => [
styles.iconButton,
(hovered || pressed) && styles.iconButtonHovered,
]}
accessibilityRole="button"
accessibilityLabel="Refresh files"
>
<RotateCw size={16} color={theme.colors.foregroundMuted} />
</Pressable>
<Pressable style={styles.sortButton} onPress={handleSortCycle}>
<Text style={styles.sortButtonText}>{currentSortLabel}</Text>
</Pressable>
</View>
</View> </View>
<FlatList <FlatList
style={styles.treeList} style={styles.treeList}
@@ -622,9 +657,23 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
<View style={[styles.treePane, styles.treePaneFill]}> <View style={[styles.treePane, styles.treePaneFill]}>
<View style={styles.paneHeader} testID="files-pane-header"> <View style={styles.paneHeader} testID="files-pane-header">
<View style={styles.paneHeaderLeft} /> <View style={styles.paneHeaderLeft} />
<Pressable style={styles.sortButton} onPress={handleSortCycle}> <View style={styles.paneHeaderRight}>
<Text style={styles.sortButtonText}>{currentSortLabel}</Text> <Pressable
</Pressable> onPress={handleRefresh}
hitSlop={8}
style={({ hovered, pressed }) => [
styles.iconButton,
(hovered || pressed) && styles.iconButtonHovered,
]}
accessibilityRole="button"
accessibilityLabel="Refresh files"
>
<RotateCw size={16} color={theme.colors.foregroundMuted} />
</Pressable>
<Pressable style={styles.sortButton} onPress={handleSortCycle}>
<Text style={styles.sortButtonText}>{currentSortLabel}</Text>
</Pressable>
</View>
</View> </View>
<FlatList <FlatList
style={styles.treeList} style={styles.treeList}
@@ -1023,6 +1072,12 @@ const styles = StyleSheet.create((theme) => ({
flex: 1, flex: 1,
minWidth: 0, minWidth: 0,
}, },
paneHeaderRight: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[2],
flexShrink: 0,
},
previewHeaderRight: { previewHeaderRight: {
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",