Fix file explorer split behavior and compact tree rows
This commit is contained in:
@@ -127,6 +127,7 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
|
|||||||
const [menuAnchor, setMenuAnchor] = useState({ top: 0, left: 0 });
|
const [menuAnchor, setMenuAnchor] = useState({ top: 0, left: 0 });
|
||||||
const [menuHeight, setMenuHeight] = useState(0);
|
const [menuHeight, setMenuHeight] = useState(0);
|
||||||
const [containerWidth, setContainerWidth] = useState(0);
|
const [containerWidth, setContainerWidth] = useState(0);
|
||||||
|
const wasInlinePreviewVisibleRef = useRef(false);
|
||||||
|
|
||||||
// Bottom sheet for file preview (mobile)
|
// Bottom sheet for file preview (mobile)
|
||||||
const previewSheetRef = useRef<BottomSheetModal>(null);
|
const previewSheetRef = useRef<BottomSheetModal>(null);
|
||||||
@@ -337,6 +338,7 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!shouldShowInlinePreview) {
|
if (!shouldShowInlinePreview) {
|
||||||
|
wasInlinePreviewVisibleRef.current = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (containerWidth <= 0) {
|
if (containerWidth <= 0) {
|
||||||
@@ -345,12 +347,16 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
|
|||||||
|
|
||||||
const available = Math.max(0, containerWidth);
|
const available = Math.max(0, containerWidth);
|
||||||
const maxTree = Math.max(minTreeWidth, available - minPreviewWidth);
|
const maxTree = Math.max(minTreeWidth, available - minPreviewWidth);
|
||||||
const desired = Math.round(available * safeSplitRatio);
|
const isOpeningInlinePreview = !wasInlinePreviewVisibleRef.current;
|
||||||
|
const desired = isOpeningInlinePreview
|
||||||
|
? Math.round(available * safeSplitRatio)
|
||||||
|
: splitTreeWidth.value;
|
||||||
const clamped = Math.max(minTreeWidth, Math.min(maxTree, desired));
|
const clamped = Math.max(minTreeWidth, Math.min(maxTree, desired));
|
||||||
|
|
||||||
splitAvailableWidth.value = available;
|
splitAvailableWidth.value = available;
|
||||||
splitMaxTreeWidth.value = maxTree;
|
splitMaxTreeWidth.value = maxTree;
|
||||||
splitTreeWidth.value = clamped;
|
splitTreeWidth.value = clamped;
|
||||||
|
wasInlinePreviewVisibleRef.current = true;
|
||||||
}, [
|
}, [
|
||||||
containerWidth,
|
containerWidth,
|
||||||
minPreviewWidth,
|
minPreviewWidth,
|
||||||
@@ -380,7 +386,7 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
|
|||||||
splitStartTreeWidth.value = splitTreeWidth.value;
|
splitStartTreeWidth.value = splitTreeWidth.value;
|
||||||
})
|
})
|
||||||
.onUpdate((event) => {
|
.onUpdate((event) => {
|
||||||
const nextWidth = splitStartTreeWidth.value + event.translationX;
|
const nextWidth = splitStartTreeWidth.value - event.translationX;
|
||||||
const clamped = Math.max(
|
const clamped = Math.max(
|
||||||
minTreeWidth,
|
minTreeWidth,
|
||||||
Math.min(splitMaxTreeWidth.value, nextWidth)
|
Math.min(splitMaxTreeWidth.value, nextWidth)
|
||||||
@@ -543,43 +549,8 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
|
|||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View style={styles.desktopSplit}>
|
<View style={styles.desktopSplit}>
|
||||||
<Animated.View
|
|
||||||
style={[
|
|
||||||
styles.treePane,
|
|
||||||
shouldShowInlinePreview
|
|
||||||
? [styles.treePaneWithPreview, { minWidth: minTreeWidth }, treePaneAnimatedStyle]
|
|
||||||
: 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>
|
|
||||||
<FlatList
|
|
||||||
style={styles.treeList}
|
|
||||||
data={treeRows}
|
|
||||||
renderItem={renderTreeRow}
|
|
||||||
keyExtractor={(row) => row.entry.path}
|
|
||||||
contentContainerStyle={styles.entriesContent}
|
|
||||||
initialNumToRender={24}
|
|
||||||
maxToRenderPerBatch={40}
|
|
||||||
windowSize={12}
|
|
||||||
/>
|
|
||||||
</Animated.View>
|
|
||||||
|
|
||||||
{shouldShowInlinePreview ? (
|
{shouldShowInlinePreview ? (
|
||||||
<View style={styles.previewPane}>
|
<View style={styles.previewPane}>
|
||||||
<GestureDetector gesture={splitResizeGesture}>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
styles.splitResizeHandle,
|
|
||||||
Platform.OS === "web" && ({ cursor: "col-resize" } as any),
|
|
||||||
Platform.OS === "web" && ({ touchAction: "none", userSelect: "none" } as any),
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</GestureDetector>
|
|
||||||
<View style={styles.paneHeader} testID="preview-pane-header">
|
<View style={styles.paneHeader} testID="preview-pane-header">
|
||||||
<Text style={styles.previewHeaderText} numberOfLines={1}>
|
<Text style={styles.previewHeaderText} numberOfLines={1}>
|
||||||
{selectedEntryPath?.split("/").pop() ?? "Preview"}
|
{selectedEntryPath?.split("/").pop() ?? "Preview"}
|
||||||
@@ -606,6 +577,62 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
|
|||||||
<FilePreviewBody preview={preview} isLoading={isPreviewLoading} variant="inline" />
|
<FilePreviewBody preview={preview} isLoading={isPreviewLoading} variant="inline" />
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
{shouldShowInlinePreview ? (
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.treePane,
|
||||||
|
styles.treePaneWithPreview,
|
||||||
|
{ minWidth: minTreeWidth },
|
||||||
|
treePaneAnimatedStyle,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<GestureDetector gesture={splitResizeGesture}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.splitResizeHandle,
|
||||||
|
Platform.OS === "web" && ({ cursor: "col-resize" } as any),
|
||||||
|
Platform.OS === "web" && ({ touchAction: "none", userSelect: "none" } as any),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</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>
|
||||||
|
<FlatList
|
||||||
|
style={styles.treeList}
|
||||||
|
data={treeRows}
|
||||||
|
renderItem={renderTreeRow}
|
||||||
|
keyExtractor={(row) => row.entry.path}
|
||||||
|
contentContainerStyle={styles.entriesContent}
|
||||||
|
initialNumToRender={24}
|
||||||
|
maxToRenderPerBatch={40}
|
||||||
|
windowSize={12}
|
||||||
|
/>
|
||||||
|
</Animated.View>
|
||||||
|
) : (
|
||||||
|
<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>
|
||||||
|
<FlatList
|
||||||
|
style={styles.treeList}
|
||||||
|
data={treeRows}
|
||||||
|
renderItem={renderTreeRow}
|
||||||
|
keyExtractor={(row) => row.entry.path}
|
||||||
|
contentContainerStyle={styles.entriesContent}
|
||||||
|
initialNumToRender={24}
|
||||||
|
maxToRenderPerBatch={40}
|
||||||
|
windowSize={12}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1014,6 +1041,7 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
},
|
},
|
||||||
treePane: {
|
treePane: {
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
|
position: "relative",
|
||||||
},
|
},
|
||||||
treePaneFill: {
|
treePaneFill: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@@ -1022,6 +1050,8 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
flex: 0,
|
flex: 0,
|
||||||
flexGrow: 0,
|
flexGrow: 0,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
borderLeftWidth: 1,
|
||||||
|
borderLeftColor: theme.colors.border,
|
||||||
},
|
},
|
||||||
splitResizeHandle: {
|
splitResizeHandle: {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
@@ -1034,9 +1064,6 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
previewPane: {
|
previewPane: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
position: "relative",
|
|
||||||
borderLeftWidth: 1,
|
|
||||||
borderLeftColor: theme.colors.border,
|
|
||||||
},
|
},
|
||||||
paneHeader: {
|
paneHeader: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
@@ -1121,7 +1148,7 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
paddingVertical: theme.spacing[1],
|
paddingVertical: 2,
|
||||||
paddingRight: theme.spacing[2],
|
paddingRight: theme.spacing[2],
|
||||||
},
|
},
|
||||||
entryRowActive: {
|
entryRowActive: {
|
||||||
@@ -1143,8 +1170,8 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
fontSize: theme.fontSize.sm,
|
fontSize: theme.fontSize.sm,
|
||||||
},
|
},
|
||||||
menuButton: {
|
menuButton: {
|
||||||
width: 36,
|
width: 30,
|
||||||
height: 36,
|
height: 30,
|
||||||
borderRadius: theme.borderRadius.full,
|
borderRadius: theme.borderRadius.full,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
|||||||
Reference in New Issue
Block a user