Update files
This commit is contained in:
@@ -15,6 +15,11 @@ import {
|
|||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { ScrollView, Gesture, GestureDetector } from "react-native-gesture-handler";
|
import { ScrollView, Gesture, GestureDetector } from "react-native-gesture-handler";
|
||||||
import { StyleSheet, UnistylesRuntime, useUnistyles } from "react-native-unistyles";
|
import { StyleSheet, UnistylesRuntime, useUnistyles } from "react-native-unistyles";
|
||||||
|
import Animated, {
|
||||||
|
runOnJS,
|
||||||
|
useAnimatedStyle,
|
||||||
|
useSharedValue,
|
||||||
|
} from "react-native-reanimated";
|
||||||
import { Fonts } from "@/constants/theme";
|
import { Fonts } from "@/constants/theme";
|
||||||
import * as Clipboard from "expo-clipboard";
|
import * as Clipboard from "expo-clipboard";
|
||||||
import {
|
import {
|
||||||
@@ -309,7 +314,6 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
|
|||||||
Boolean(isExplorerLoading && pendingRequest?.mode === "list" && pendingRequest?.path === ".");
|
Boolean(isExplorerLoading && pendingRequest?.mode === "list" && pendingRequest?.path === ".");
|
||||||
|
|
||||||
const shouldShowInlinePreview = !isMobile && Boolean(selectedEntryPath);
|
const shouldShowInlinePreview = !isMobile && Boolean(selectedEntryPath);
|
||||||
const dividerWidth = 10;
|
|
||||||
const minTreeWidth = 220;
|
const minTreeWidth = 220;
|
||||||
const minPreviewWidth = 320;
|
const minPreviewWidth = 320;
|
||||||
|
|
||||||
@@ -317,60 +321,78 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
|
|||||||
? splitRatio
|
? splitRatio
|
||||||
: DEFAULT_EXPLORER_FILES_SPLIT_RATIO;
|
: DEFAULT_EXPLORER_FILES_SPLIT_RATIO;
|
||||||
|
|
||||||
const treePaneWidth = useMemo(() => {
|
const splitAvailableWidth = useSharedValue(0);
|
||||||
if (!shouldShowInlinePreview || containerWidth <= 0) {
|
const splitMaxTreeWidth = useSharedValue(minTreeWidth);
|
||||||
return null;
|
const splitTreeWidth = useSharedValue(minTreeWidth);
|
||||||
|
const splitStartTreeWidth = useSharedValue(minTreeWidth);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!shouldShowInlinePreview) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (containerWidth <= 0) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const available = Math.max(0, containerWidth - dividerWidth);
|
const available = Math.max(0, containerWidth);
|
||||||
const maxTree = Math.max(minTreeWidth, available - minPreviewWidth);
|
const maxTree = Math.max(minTreeWidth, available - minPreviewWidth);
|
||||||
const raw = Math.round(available * safeSplitRatio);
|
const desired = Math.round(available * safeSplitRatio);
|
||||||
return Math.max(minTreeWidth, Math.min(maxTree, raw));
|
const clamped = Math.max(minTreeWidth, Math.min(maxTree, desired));
|
||||||
}, [containerWidth, safeSplitRatio, shouldShowInlinePreview]);
|
|
||||||
|
|
||||||
const resizeStartRef = useRef<{ startWidth: number; available: number } | null>(null);
|
splitAvailableWidth.value = available;
|
||||||
|
splitMaxTreeWidth.value = maxTree;
|
||||||
|
splitTreeWidth.value = clamped;
|
||||||
|
}, [
|
||||||
|
containerWidth,
|
||||||
|
minPreviewWidth,
|
||||||
|
minTreeWidth,
|
||||||
|
safeSplitRatio,
|
||||||
|
shouldShowInlinePreview,
|
||||||
|
splitAvailableWidth,
|
||||||
|
splitMaxTreeWidth,
|
||||||
|
splitTreeWidth,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const treePaneAnimatedStyle = useAnimatedStyle(() => ({
|
||||||
|
width: splitTreeWidth.value,
|
||||||
|
flexBasis: splitTreeWidth.value,
|
||||||
|
flexGrow: 0,
|
||||||
|
flexShrink: 0,
|
||||||
|
}));
|
||||||
|
|
||||||
const splitResizeGesture = useMemo(() => {
|
const splitResizeGesture = useMemo(() => {
|
||||||
if (!shouldShowInlinePreview || containerWidth <= 0 || treePaneWidth === null) {
|
if (isMobile || !shouldShowInlinePreview) {
|
||||||
return Gesture.Pan().enabled(false);
|
return Gesture.Pan().enabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const available = Math.max(0, containerWidth - dividerWidth);
|
|
||||||
|
|
||||||
return Gesture.Pan()
|
return Gesture.Pan()
|
||||||
.enabled(!isMobile)
|
|
||||||
.runOnJS(true)
|
|
||||||
.activeOffsetX([-2, 2])
|
|
||||||
.failOffsetY([-5, 5])
|
|
||||||
.hitSlop({ left: 12, right: 12, top: 0, bottom: 0 })
|
.hitSlop({ left: 12, right: 12, top: 0, bottom: 0 })
|
||||||
.onBegin(() => {
|
.onStart(() => {
|
||||||
resizeStartRef.current = {
|
splitStartTreeWidth.value = splitTreeWidth.value;
|
||||||
startWidth: treePaneWidth,
|
|
||||||
available,
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
.onUpdate((event) => {
|
.onUpdate((event) => {
|
||||||
const start = resizeStartRef.current;
|
const nextWidth = splitStartTreeWidth.value + event.translationX;
|
||||||
if (!start) {
|
const clamped = Math.max(
|
||||||
return;
|
minTreeWidth,
|
||||||
}
|
Math.min(splitMaxTreeWidth.value, nextWidth)
|
||||||
const nextWidth = start.startWidth + (event.translationX ?? 0);
|
);
|
||||||
const maxTree = Math.max(minTreeWidth, start.available - minPreviewWidth);
|
splitTreeWidth.value = clamped;
|
||||||
const clamped = Math.max(minTreeWidth, Math.min(maxTree, nextWidth));
|
|
||||||
const nextRatio = start.available > 0 ? clamped / start.available : safeSplitRatio;
|
|
||||||
setSplitRatio(nextRatio);
|
|
||||||
})
|
})
|
||||||
.onFinalize(() => {
|
.onEnd(() => {
|
||||||
resizeStartRef.current = null;
|
const available = splitAvailableWidth.value;
|
||||||
|
const ratio = available > 0 ? splitTreeWidth.value / available : safeSplitRatio;
|
||||||
|
runOnJS(setSplitRatio)(ratio);
|
||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
containerWidth,
|
|
||||||
dividerWidth,
|
|
||||||
isMobile,
|
isMobile,
|
||||||
|
minTreeWidth,
|
||||||
|
safeSplitRatio,
|
||||||
setSplitRatio,
|
setSplitRatio,
|
||||||
shouldShowInlinePreview,
|
shouldShowInlinePreview,
|
||||||
safeSplitRatio,
|
splitAvailableWidth,
|
||||||
treePaneWidth,
|
splitMaxTreeWidth,
|
||||||
|
splitStartTreeWidth,
|
||||||
|
splitTreeWidth,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const renderTreeRow = useCallback(
|
const renderTreeRow = useCallback(
|
||||||
@@ -432,16 +454,6 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const listHeaderComponent = useMemo(() => {
|
|
||||||
return (
|
|
||||||
<View style={styles.headerContainer}>
|
|
||||||
<Pressable style={styles.sortButton} onPress={handleSortCycle}>
|
|
||||||
<Text style={styles.sortButtonText}>{currentSortLabel}</Text>
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}, [currentSortLabel, handleSortCycle]);
|
|
||||||
|
|
||||||
const handlePreviewSheetChange = useCallback(
|
const handlePreviewSheetChange = useCallback(
|
||||||
(index: number) => {
|
(index: number) => {
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
@@ -504,32 +516,36 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
|
|||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View style={styles.desktopSplit}>
|
<View style={styles.desktopSplit}>
|
||||||
<View
|
<Animated.View
|
||||||
style={[
|
style={[
|
||||||
styles.treePane,
|
styles.treePane,
|
||||||
shouldShowInlinePreview && styles.treePaneWithPreview,
|
shouldShowInlinePreview
|
||||||
shouldShowInlinePreview && treePaneWidth !== null
|
? [styles.treePaneWithPreview, { minWidth: minTreeWidth }, treePaneAnimatedStyle]
|
||||||
? { width: treePaneWidth, flex: 0, flexGrow: 0, flexShrink: 0 }
|
: styles.treePaneFill,
|
||||||
: null,
|
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
<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
|
<FlatList
|
||||||
|
style={styles.treeList}
|
||||||
data={treeRows}
|
data={treeRows}
|
||||||
renderItem={renderTreeRow}
|
renderItem={renderTreeRow}
|
||||||
keyExtractor={(row) => row.entry.path}
|
keyExtractor={(row) => row.entry.path}
|
||||||
contentContainerStyle={styles.entriesContent}
|
contentContainerStyle={styles.entriesContent}
|
||||||
ListHeaderComponent={listHeaderComponent}
|
|
||||||
initialNumToRender={24}
|
initialNumToRender={24}
|
||||||
maxToRenderPerBatch={40}
|
maxToRenderPerBatch={40}
|
||||||
windowSize={12}
|
windowSize={12}
|
||||||
/>
|
/>
|
||||||
</View>
|
</Animated.View>
|
||||||
|
|
||||||
{shouldShowInlinePreview ? (
|
{shouldShowInlinePreview ? (
|
||||||
<>
|
<View style={styles.previewPane}>
|
||||||
<GestureDetector gesture={splitResizeGesture}>
|
<GestureDetector gesture={splitResizeGesture}>
|
||||||
<View
|
<View
|
||||||
pointerEvents="box-only"
|
|
||||||
style={[
|
style={[
|
||||||
styles.splitResizeHandle,
|
styles.splitResizeHandle,
|
||||||
Platform.OS === "web" && ({ cursor: "col-resize" } as any),
|
Platform.OS === "web" && ({ cursor: "col-resize" } as any),
|
||||||
@@ -537,33 +553,20 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</GestureDetector>
|
</GestureDetector>
|
||||||
<View style={styles.previewPane}>
|
<View style={styles.paneHeader} testID="preview-pane-header">
|
||||||
<View style={styles.previewHeaderContainer}>
|
<Text style={styles.previewHeaderText} numberOfLines={1}>
|
||||||
<View style={styles.previewHeaderInner}>
|
{selectedEntryPath?.split("/").pop() ?? "Preview"}
|
||||||
<Pressable
|
</Text>
|
||||||
onPress={() => {
|
<View style={styles.previewHeaderRight}>
|
||||||
if (selectedEntryPath) {
|
{isPreviewLoading ? (
|
||||||
void Clipboard.setStringAsync(selectedEntryPath);
|
<ActivityIndicator size="small" color={theme.colors.foregroundMuted} />
|
||||||
}
|
) : null}
|
||||||
}}
|
|
||||||
style={({ hovered, pressed }) => [
|
|
||||||
styles.previewHeaderRow,
|
|
||||||
(hovered || pressed) && styles.previewHeaderRowHovered,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text style={styles.previewHeaderText} numberOfLines={1}>
|
|
||||||
{selectedEntryPath?.split("/").pop() ?? "Preview"}
|
|
||||||
</Text>
|
|
||||||
{isPreviewLoading ? (
|
|
||||||
<ActivityIndicator size="small" color={theme.colors.foregroundMuted} />
|
|
||||||
) : null}
|
|
||||||
</Pressable>
|
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={handleClosePreview}
|
onPress={handleClosePreview}
|
||||||
hitSlop={8}
|
hitSlop={8}
|
||||||
style={({ hovered, pressed }) => [
|
style={({ hovered, pressed }) => [
|
||||||
styles.iconButton,
|
styles.iconButton,
|
||||||
(hovered || pressed) && styles.previewHeaderRowHovered,
|
(hovered || pressed) && styles.iconButtonHovered,
|
||||||
]}
|
]}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel="Close preview"
|
accessibilityLabel="Close preview"
|
||||||
@@ -575,7 +578,6 @@ export function FileExplorerPane({ serverId, agentId }: FileExplorerPaneProps) {
|
|||||||
|
|
||||||
<FilePreviewBody preview={preview} isLoading={isPreviewLoading} variant="inline" />
|
<FilePreviewBody preview={preview} isLoading={isPreviewLoading} variant="inline" />
|
||||||
</View>
|
</View>
|
||||||
</>
|
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
@@ -697,22 +699,26 @@ function FilePreviewBody({
|
|||||||
if (preview.kind === "text") {
|
if (preview.kind === "text") {
|
||||||
if (variant === "sheet") {
|
if (variant === "sheet") {
|
||||||
return (
|
return (
|
||||||
<BottomSheetScrollView
|
<BottomSheetScrollView style={styles.previewContent}>
|
||||||
style={styles.previewContent}
|
<ScrollView
|
||||||
contentContainerStyle={styles.previewTextContent}
|
horizontal
|
||||||
>
|
nestedScrollEnabled
|
||||||
<ScrollView horizontal nestedScrollEnabled showsHorizontalScrollIndicator>
|
showsHorizontalScrollIndicator
|
||||||
|
contentContainerStyle={styles.previewCodeScrollContent}
|
||||||
|
>
|
||||||
<Text style={styles.codeText}>{preview.content}</Text>
|
<Text style={styles.codeText}>{preview.content}</Text>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</BottomSheetScrollView>
|
</BottomSheetScrollView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<RNScrollView
|
<RNScrollView style={styles.previewContent}>
|
||||||
style={styles.previewContent}
|
<RNScrollView
|
||||||
contentContainerStyle={styles.previewTextContent}
|
horizontal
|
||||||
>
|
nestedScrollEnabled
|
||||||
<RNScrollView horizontal nestedScrollEnabled showsHorizontalScrollIndicator>
|
showsHorizontalScrollIndicator
|
||||||
|
contentContainerStyle={styles.previewCodeScrollContent}
|
||||||
|
>
|
||||||
<Text style={styles.codeText}>{preview.content}</Text>
|
<Text style={styles.codeText}>{preview.content}</Text>
|
||||||
</RNScrollView>
|
</RNScrollView>
|
||||||
</RNScrollView>
|
</RNScrollView>
|
||||||
@@ -965,33 +971,56 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
minHeight: 0,
|
minHeight: 0,
|
||||||
},
|
},
|
||||||
treePane: {
|
treePane: {
|
||||||
flex: 1,
|
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
},
|
},
|
||||||
|
treePaneFill: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
treePaneWithPreview: {
|
treePaneWithPreview: {
|
||||||
|
flex: 0,
|
||||||
|
flexGrow: 0,
|
||||||
|
flexShrink: 0,
|
||||||
},
|
},
|
||||||
splitResizeHandle: {
|
splitResizeHandle: {
|
||||||
|
position: "absolute",
|
||||||
|
left: -5,
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
width: 10,
|
width: 10,
|
||||||
backgroundColor: theme.colors.surface0,
|
zIndex: 20,
|
||||||
borderLeftWidth: 1,
|
|
||||||
borderLeftColor: theme.colors.border,
|
|
||||||
borderRightWidth: 1,
|
|
||||||
borderRightColor: theme.colors.border,
|
|
||||||
},
|
},
|
||||||
previewPane: {
|
previewPane: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
|
position: "relative",
|
||||||
|
borderLeftWidth: 1,
|
||||||
|
borderLeftColor: theme.colors.border,
|
||||||
},
|
},
|
||||||
headerContainer: {
|
paneHeader: {
|
||||||
paddingHorizontal: theme.spacing[2],
|
|
||||||
paddingTop: theme.spacing[2],
|
|
||||||
paddingBottom: theme.spacing[1],
|
|
||||||
},
|
|
||||||
sortButton: {
|
|
||||||
alignSelf: "flex-end",
|
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
paddingVertical: theme.spacing[1],
|
justifyContent: "space-between",
|
||||||
|
height: 32 + theme.spacing[2] * 2,
|
||||||
|
paddingHorizontal: theme.spacing[3],
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: theme.colors.border,
|
||||||
|
backgroundColor: theme.colors.surface0,
|
||||||
|
},
|
||||||
|
paneHeaderLeft: {
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 0,
|
||||||
|
},
|
||||||
|
previewHeaderRight: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: theme.spacing[2],
|
||||||
|
flexShrink: 0,
|
||||||
|
},
|
||||||
|
sortButton: {
|
||||||
|
height: 32,
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
paddingHorizontal: theme.spacing[2],
|
paddingHorizontal: theme.spacing[2],
|
||||||
borderRadius: theme.borderRadius.md,
|
borderRadius: theme.borderRadius.md,
|
||||||
borderWidth: theme.borderWidth[1],
|
borderWidth: theme.borderWidth[1],
|
||||||
@@ -1001,6 +1030,10 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
color: theme.colors.foregroundMuted,
|
color: theme.colors.foregroundMuted,
|
||||||
fontSize: theme.fontSize.xs,
|
fontSize: theme.fontSize.xs,
|
||||||
},
|
},
|
||||||
|
treeList: {
|
||||||
|
flex: 1,
|
||||||
|
minHeight: 0,
|
||||||
|
},
|
||||||
entriesContent: {
|
entriesContent: {
|
||||||
paddingBottom: theme.spacing[4],
|
paddingBottom: theme.spacing[4],
|
||||||
},
|
},
|
||||||
@@ -1119,36 +1152,11 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
fontSize: theme.fontSize.lg,
|
fontSize: theme.fontSize.lg,
|
||||||
fontWeight: theme.fontWeight.semibold,
|
fontWeight: theme.fontWeight.semibold,
|
||||||
},
|
},
|
||||||
previewHeaderContainer: {
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: theme.colors.border,
|
|
||||||
},
|
|
||||||
previewHeaderInner: {
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
gap: theme.spacing[2],
|
|
||||||
paddingHorizontal: theme.spacing[2],
|
|
||||||
paddingVertical: theme.spacing[2],
|
|
||||||
},
|
|
||||||
previewHeaderRow: {
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 0,
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: theme.spacing[2],
|
|
||||||
borderRadius: theme.borderRadius.md,
|
|
||||||
paddingVertical: theme.spacing[1],
|
|
||||||
paddingHorizontal: theme.spacing[2],
|
|
||||||
},
|
|
||||||
previewHeaderRowHovered: {
|
|
||||||
backgroundColor: theme.colors.surface2,
|
|
||||||
},
|
|
||||||
previewHeaderText: {
|
previewHeaderText: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
color: theme.colors.foreground,
|
color: theme.colors.foreground,
|
||||||
fontSize: theme.fontSize.sm,
|
fontSize: theme.fontSize.sm,
|
||||||
fontWeight: theme.fontWeight.semibold,
|
fontWeight: theme.fontWeight.normal,
|
||||||
},
|
},
|
||||||
iconButton: {
|
iconButton: {
|
||||||
width: 32,
|
width: 32,
|
||||||
@@ -1157,11 +1165,16 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
},
|
},
|
||||||
|
iconButtonHovered: {
|
||||||
|
backgroundColor: theme.colors.surface2,
|
||||||
|
},
|
||||||
previewContent: {
|
previewContent: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
previewTextContent: {
|
previewCodeScrollContent: {
|
||||||
padding: theme.spacing[3],
|
paddingTop: theme.spacing[3],
|
||||||
|
paddingHorizontal: theme.spacing[3],
|
||||||
|
paddingBottom: theme.spacing[3] + theme.spacing[2],
|
||||||
},
|
},
|
||||||
codeText: {
|
codeText: {
|
||||||
color: theme.colors.foreground,
|
color: theme.colors.foreground,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ interface DesktopSidebarState {
|
|||||||
export type ExplorerTab = "changes" | "files";
|
export type ExplorerTab = "changes" | "files";
|
||||||
export type SortOption = "name" | "modified" | "size";
|
export type SortOption = "name" | "modified" | "size";
|
||||||
|
|
||||||
export const DEFAULT_EXPLORER_SIDEBAR_WIDTH = Platform.OS === "web" ? 520 : 400;
|
export const DEFAULT_EXPLORER_SIDEBAR_WIDTH = Platform.OS === "web" ? 640 : 400;
|
||||||
export const MIN_EXPLORER_SIDEBAR_WIDTH = 280;
|
export const MIN_EXPLORER_SIDEBAR_WIDTH = 280;
|
||||||
// Upper bound is intentionally generous; desktop resizing enforces a min-chat-width constraint.
|
// Upper bound is intentionally generous; desktop resizing enforces a min-chat-width constraint.
|
||||||
export const MAX_EXPLORER_SIDEBAR_WIDTH = 2000;
|
export const MAX_EXPLORER_SIDEBAR_WIDTH = 2000;
|
||||||
@@ -164,7 +164,7 @@ export const usePanelStore = create<PanelState>()(
|
|||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "panel-state",
|
name: "panel-state",
|
||||||
version: 2,
|
version: 3,
|
||||||
storage: createJSONStorage(() => AsyncStorage),
|
storage: createJSONStorage(() => AsyncStorage),
|
||||||
migrate: (persistedState, version) => {
|
migrate: (persistedState, version) => {
|
||||||
const state = persistedState as Partial<PanelState> & Record<string, unknown>;
|
const state = persistedState as Partial<PanelState> & Record<string, unknown>;
|
||||||
@@ -187,6 +187,16 @@ export const usePanelStore = create<PanelState>()(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version < 3) {
|
||||||
|
if (
|
||||||
|
Platform.OS === "web" &&
|
||||||
|
typeof state.explorerWidth === "number" &&
|
||||||
|
(state.explorerWidth === 400 || state.explorerWidth === 520)
|
||||||
|
) {
|
||||||
|
state.explorerWidth = DEFAULT_EXPLORER_SIDEBAR_WIDTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return state as PanelState;
|
return state as PanelState;
|
||||||
},
|
},
|
||||||
partialize: (state) => ({
|
partialize: (state) => ({
|
||||||
|
|||||||
Reference in New Issue
Block a user