From a5723bf087476bda8624cb0a6a891ad32417a0a4 Mon Sep 17 00:00:00 2001 From: Den <2119348+dzianisv@users.noreply.github.com> Date: Wed, 15 Jul 2026 12:06:46 -0700 Subject: [PATCH] feat: improve project picker with recents and server projects (#52) Adds recent and server-project discovery to the new-session directory picker. Reviewed against current main; Android, iOS Simulator, and mandatory CUA checks are green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- app/(tabs)/index.tsx | 156 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 145 insertions(+), 11 deletions(-) diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index facc4f5..3b6caec 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,8 +1,9 @@ -import { useCallback, useState, useRef } from "react" +import { useCallback, useState, useRef, useEffect } from "react" import { View, Text, FlatList, + ScrollView, TouchableOpacity, StyleSheet, useColorScheme, @@ -20,7 +21,7 @@ import { useSessions } from "../../src/stores/sessions" import { useConnections } from "../../src/stores/connections" import { useCatalog } from "../../src/stores/catalog" import type BottomSheet from "@gorhom/bottom-sheet" -import type { Session } from "../../src/lib/sdk" +import type { Session, Project } from "../../src/lib/sdk" import { DirectorySwitcher } from "../../src/components/chat" function formatTime(timestamp: number): string { @@ -112,6 +113,7 @@ export default function SessionsScreen() { const [renaming, setRenaming] = useState(null) const [renameText, setRenameText] = useState("") const renamingInFlight = useRef(false) + const [serverProjects, setServerProjects] = useState([]) const { sessions, isLoading, error, loadSessions, createSession, deleteSession } = useSessions() const { @@ -129,6 +131,15 @@ export default function SessionsScreen() { const dirSheetRef = useRef(null) const [refreshing, setRefreshing] = useState(false) + // Fetch server-known projects when the new session modal opens + useEffect(() => { + if (!showNewSession || !client) return + client.project + .list() + .then(setServerProjects) + .catch(() => setServerProjects([])) + }, [showNewSession, client]) + const handleSwitchDirectory = useCallback( async (dir?: string) => { await switchDirectory(dir) @@ -370,19 +381,106 @@ export default function SessionsScreen() { - - {/* Current directory */} - Current Directory - - + + {/* Current directory — tapping creates session immediately */} + Current Project + onCreateInDirectory()} + disabled={isCreating} + > + {currentProject?.path?.absolute || activeConnection?.directory || "Server default"} - + + - {/* Custom directory input */} + {/* Recent projects */} + {recentDirectories.length > 0 && ( + <> + + Recent Projects + + {recentDirectories.map((dir) => { + const short = dir.split("/").filter(Boolean).pop() || dir + const isCurrent = + dir === (currentProject?.path?.absolute || activeConnection?.directory) + return ( + onCreateInDirectory(dir)} + disabled={isCreating} + > + + + + {short} + + + {dir} + + + {isCurrent && } + + ) + })} + + )} + + {/* Server-known projects (excluding current) */} + {serverProjects.filter((p) => p.path?.absolute !== currentProject?.path?.absolute).length > 0 && ( + <> + + Server Projects + + {serverProjects + .filter((p) => p.path?.absolute !== currentProject?.path?.absolute) + .map((p) => { + const short = p.name || p.path?.absolute?.split("/").filter(Boolean).pop() || p.id + return ( + onCreateInDirectory(p.path?.absolute)} + disabled={isCreating} + > + + + + {short} + + {p.path?.absolute && ( + + {p.path.absolute} + + )} + + + ) + })} + + )} + + {/* Manual path input fallback */} - Or use a different folder + Enter Path Manually )} - + {customDir.trim() ? ( @@ -730,6 +828,42 @@ const styles = StyleSheet.create({ modalBody: { marginBottom: 24, }, + modalScrollBody: { + maxHeight: 420, + marginBottom: 16, + }, + projectRow: { + flexDirection: "row", + alignItems: "center", + gap: 12, + paddingHorizontal: 14, + paddingVertical: 11, + borderRadius: 10, + backgroundColor: "#f5f5f5", + marginBottom: 6, + }, + projectRowDark: { + backgroundColor: "#2a2a2a", + }, + projectRowActive: { + backgroundColor: "#f5f3ff", + }, + projectRowContent: { + flex: 1, + }, + projectRowName: { + fontSize: 14, + fontWeight: "600", + color: "#0a0a0a", + }, + projectRowNameActive: { + color: "#8b5cf6", + }, + projectRowPath: { + fontSize: 11, + color: "#999999", + marginTop: 1, + }, modalLabel: { fontSize: 13, fontWeight: "600",