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 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 (
<View style={styles.sidebarContent} pointerEvents="auto">
{/* Header with tabs and close button */}
<View style={styles.header} testID="explorer-header">
<View style={styles.tabsContainer}>
<Pressable
style={[styles.tab, activeTab === "changes" && styles.tabActive]}
onPress={() => onTabPress("changes")}
>
<Text
style={[
styles.tabText,
activeTab === "changes" && styles.tabTextActive,
!isGit && styles.tabTextMuted,
]}
{isGit && (
<Pressable
style={[styles.tab, activeTab === "changes" && styles.tabActive]}
onPress={() => onTabPress("changes")}
>
Changes
</Text>
</Pressable>
<Text
style={[
styles.tabText,
activeTab === "changes" && styles.tabTextActive,
]}
>
Changes
</Text>
</Pressable>
)}
<Pressable
style={[styles.tab, activeTab === "files" && styles.tabActive]}
onPress={() => onTabPress("files")}
@@ -33,6 +33,7 @@ import {
FolderOpen,
Image as ImageIcon,
MoreVertical,
RotateCw,
X,
} from "lucide-react-native";
import type {
@@ -263,6 +264,26 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
setSortOption(SORT_OPTIONS[nextIndex].value);
}, [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 treeRows = useMemo(() => {
@@ -603,9 +624,23 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
</GestureDetector>
<View style={styles.paneHeader} testID="files-pane-header">
<View style={styles.paneHeaderLeft} />
<Pressable style={styles.sortButton} onPress={handleSortCycle}>
<Text style={styles.sortButtonText}>{currentSortLabel}</Text>
</Pressable>
<View style={styles.paneHeaderRight}>
<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>
<FlatList
style={styles.treeList}
@@ -622,9 +657,23 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
<View style={[styles.treePane, styles.treePaneFill]}>
<View style={styles.paneHeader} testID="files-pane-header">
<View style={styles.paneHeaderLeft} />
<Pressable style={styles.sortButton} onPress={handleSortCycle}>
<Text style={styles.sortButtonText}>{currentSortLabel}</Text>
</Pressable>
<View style={styles.paneHeaderRight}>
<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>
<FlatList
style={styles.treeList}
@@ -1023,6 +1072,12 @@ const styles = StyleSheet.create((theme) => ({
flex: 1,
minWidth: 0,
},
paneHeaderRight: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[2],
flexShrink: 0,
},
previewHeaderRight: {
flexDirection: "row",
alignItems: "center",