Matthias Nott
2026-03-07 93670c15d9b6542b24078c9cef7b09e09fc8cb47
components/SessionDrawer.tsx
....@@ -25,7 +25,7 @@
2525 ScaleDecorator,
2626 } from "react-native-draggable-flatlist";
2727 import * as Haptics from "expo-haptics";
28
-import { WsSession } from "../types";
28
+import { WsSession, PaiProject } from "../types";
2929 import { useChat } from "../contexts/ChatContext";
3030 import { useTheme, type ThemeColors } from "../contexts/ThemeContext";
3131
....@@ -262,10 +262,14 @@
262262 renameSession,
263263 removeSession,
264264 createSession,
265
+ fetchProjects,
266
+ projects,
265267 unreadCounts,
266268 } = useChat();
267269 const { colors } = useTheme();
268270 const [editingId, setEditingId] = useState<string | null>(null);
271
+ const [showProjectPicker, setShowProjectPicker] = useState(false);
272
+ const [customPath, setCustomPath] = useState("");
269273 const slideAnim = useRef(new Animated.Value(-DRAWER_WIDTH)).current;
270274 const fadeAnim = useRef(new Animated.Value(0)).current;
271275 const [rendered, setRendered] = useState(false);
....@@ -326,6 +330,8 @@
326330
327331 const handleClose = useCallback(() => {
328332 setEditingId(null);
333
+ setShowProjectPicker(false);
334
+ setCustomPath("");
329335 Keyboard.dismiss();
330336 onClose();
331337 }, [onClose]);
....@@ -362,8 +368,18 @@
362368 );
363369
364370 const handleNewSession = useCallback(() => {
371
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
372
+ setShowProjectPicker((prev) => {
373
+ if (!prev) fetchProjects();
374
+ return !prev;
375
+ });
376
+ }, [fetchProjects]);
377
+
378
+ const launchSession = useCallback((opts?: { project?: string; path?: string }) => {
365379 Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
366
- createSession();
380
+ createSession(opts);
381
+ setShowProjectPicker(false);
382
+ setCustomPath("");
367383 handleClose();
368384 setTimeout(() => requestSessions(), 2500);
369385 }, [createSession, requestSessions, handleClose]);
....@@ -519,23 +535,120 @@
519535 />
520536 )}
521537
522
- {/* New session button */}
523
- <View style={{ alignItems: "center", paddingVertical: 12 }}>
524
- <Pressable
525
- onPress={handleNewSession}
526
- style={{
527
- flexDirection: "row",
528
- alignItems: "center",
529
- gap: 6,
530
- paddingHorizontal: 20,
531
- paddingVertical: 10,
532
- borderRadius: 24,
533
- backgroundColor: colors.accent,
534
- }}
535
- >
536
- <Text style={{ color: "#FFF", fontSize: 18, fontWeight: "700" }}>+</Text>
537
- <Text style={{ color: "#FFF", fontSize: 15, fontWeight: "600" }}>New Session</Text>
538
- </Pressable>
538
+ {/* New session button + project picker */}
539
+ <View style={{ paddingVertical: 8 }}>
540
+ <View style={{ alignItems: "center", paddingBottom: showProjectPicker ? 8 : 0 }}>
541
+ <Pressable
542
+ onPress={handleNewSession}
543
+ style={{
544
+ flexDirection: "row",
545
+ alignItems: "center",
546
+ gap: 6,
547
+ paddingHorizontal: 20,
548
+ paddingVertical: 10,
549
+ borderRadius: 24,
550
+ backgroundColor: colors.accent,
551
+ }}
552
+ >
553
+ <Text style={{ color: "#FFF", fontSize: 18, fontWeight: "700" }}>+</Text>
554
+ <Text style={{ color: "#FFF", fontSize: 15, fontWeight: "600" }}>New Session</Text>
555
+ </Pressable>
556
+ </View>
557
+
558
+ {showProjectPicker && (
559
+ <View style={{
560
+ marginHorizontal: 12,
561
+ borderRadius: 12,
562
+ backgroundColor: colors.bgTertiary,
563
+ overflow: "hidden",
564
+ }}>
565
+ {/* Home directory โ€” always first */}
566
+ <Pressable
567
+ onPress={() => launchSession({ path: "~" })}
568
+ style={({ pressed }) => ({
569
+ flexDirection: "row",
570
+ alignItems: "center",
571
+ paddingHorizontal: 16,
572
+ paddingVertical: 12,
573
+ backgroundColor: pressed ? colors.border : "transparent",
574
+ })}
575
+ >
576
+ <Text style={{ fontSize: 18, marginRight: 10 }}>๐Ÿ </Text>
577
+ <View style={{ flex: 1 }}>
578
+ <Text style={{ color: colors.text, fontSize: 15, fontWeight: "600" }}>Home</Text>
579
+ <Text style={{ color: colors.textMuted, fontSize: 12 }}>~/</Text>
580
+ </View>
581
+ </Pressable>
582
+
583
+ {/* PAI projects */}
584
+ {projects.map((p) => (
585
+ <Pressable
586
+ key={p.slug}
587
+ onPress={() => launchSession({ project: p.name })}
588
+ style={({ pressed }) => ({
589
+ flexDirection: "row",
590
+ alignItems: "center",
591
+ paddingHorizontal: 16,
592
+ paddingVertical: 12,
593
+ borderTopWidth: 1,
594
+ borderTopColor: colors.border,
595
+ backgroundColor: pressed ? colors.border : "transparent",
596
+ })}
597
+ >
598
+ <Text style={{ fontSize: 18, marginRight: 10 }}>๐Ÿ“‚</Text>
599
+ <View style={{ flex: 1 }}>
600
+ <Text style={{ color: colors.text, fontSize: 15, fontWeight: "500" }}>{p.name}</Text>
601
+ <Text style={{ color: colors.textMuted, fontSize: 12 }} numberOfLines={1}>
602
+ {p.path.replace(/^\/Users\/[^/]+/, "~")}
603
+ </Text>
604
+ </View>
605
+ </Pressable>
606
+ ))}
607
+
608
+ {/* Custom path input */}
609
+ <View style={{
610
+ flexDirection: "row",
611
+ alignItems: "center",
612
+ borderTopWidth: 1,
613
+ borderTopColor: colors.border,
614
+ paddingHorizontal: 16,
615
+ paddingVertical: 8,
616
+ }}>
617
+ <TextInput
618
+ value={customPath}
619
+ onChangeText={setCustomPath}
620
+ placeholder="Custom pathโ€ฆ"
621
+ placeholderTextColor={colors.textMuted}
622
+ autoCapitalize="none"
623
+ autoCorrect={false}
624
+ returnKeyType="go"
625
+ onSubmitEditing={() => {
626
+ if (customPath.trim()) launchSession({ path: customPath.trim() });
627
+ }}
628
+ style={{
629
+ flex: 1,
630
+ color: colors.text,
631
+ fontSize: 14,
632
+ paddingVertical: 6,
633
+ }}
634
+ />
635
+ {customPath.trim().length > 0 && (
636
+ <Pressable
637
+ onPress={() => launchSession({ path: customPath.trim() })}
638
+ style={{
639
+ paddingHorizontal: 12,
640
+ paddingVertical: 6,
641
+ borderRadius: 8,
642
+ backgroundColor: colors.accent,
643
+ marginLeft: 8,
644
+ }}
645
+ >
646
+ <Text style={{ color: "#FFF", fontSize: 13, fontWeight: "600" }}>Go</Text>
647
+ </Pressable>
648
+ )}
649
+ </View>
650
+ </View>
651
+ )}
539652 </View>
540653
541654 {/* Footer */}