|
| 1 | +import type { Session } from "core/index.js"; |
1 | 2 | import { format, isThisWeek, isThisYear, isToday, isYesterday } from "date-fns"; |
2 | 3 | import { Box, Text, useInput } from "ink"; |
3 | | -import React, { useMemo, useState } from "react"; |
| 4 | +import React, { useEffect, useMemo, useState } from "react"; |
4 | 5 |
|
5 | | -import { ExtendedSessionMetadata } from "../session.js"; |
| 6 | +import { ExtendedSessionMetadata, loadSessionById } from "../session.js"; |
6 | 7 |
|
7 | 8 | import { useTerminalSize } from "./hooks/useTerminalSize.js"; |
| 9 | +import { SessionPreview } from "./SessionPreview.js"; |
8 | 10 | import { defaultBoxStyles } from "./styles.js"; |
9 | 11 |
|
10 | 12 | interface SessionSelectorProps { |
@@ -40,7 +42,19 @@ export function SessionSelector({ |
40 | 42 | onExit, |
41 | 43 | }: SessionSelectorProps) { |
42 | 44 | const [selectedIndex, setSelectedIndex] = useState(0); |
43 | | - const { rows: terminalHeight } = useTerminalSize(); |
| 45 | + const [previewSession, setPreviewSession] = useState<Session | null>(null); |
| 46 | + const { rows: terminalHeight, columns: terminalWidth } = useTerminalSize(); |
| 47 | + |
| 48 | + // Load the selected session for preview |
| 49 | + useEffect(() => { |
| 50 | + const selectedSession = sessions[selectedIndex]; |
| 51 | + if (selectedSession && !selectedSession.isRemote) { |
| 52 | + const session = loadSessionById(selectedSession.sessionId); |
| 53 | + setPreviewSession(session); |
| 54 | + } else { |
| 55 | + setPreviewSession(null); |
| 56 | + } |
| 57 | + }, [selectedIndex, sessions]); |
44 | 58 |
|
45 | 59 | // Calculate how many sessions we can display based on terminal height and scrolling |
46 | 60 | const { displaySessions, scrollOffset } = useMemo(() => { |
@@ -104,54 +118,90 @@ export function SessionSelector({ |
104 | 118 | const hasMoreAbove = scrollOffset > 0; |
105 | 119 | const hasMoreBelow = scrollOffset + displaySessions.length < sessions.length; |
106 | 120 |
|
| 121 | + // Determine if we should show preview (only if terminal is wide enough) |
| 122 | + const showPreview = terminalWidth > 100; |
| 123 | + const listWidth = showPreview |
| 124 | + ? Math.floor(terminalWidth * 0.3) |
| 125 | + : terminalWidth; |
| 126 | + |
107 | 127 | return ( |
108 | | - <Box {...defaultBoxStyles("blue")}> |
109 | | - <Text color="blue" bold> |
110 | | - Recent Sessions{" "} |
111 | | - {sessions.length > displaySessions.length && |
112 | | - `(${selectedIndex + 1}/${sessions.length})`} |
113 | | - </Text> |
114 | | - <Text color="gray">↑/↓ to navigate, Enter to select, Esc to exit</Text> |
115 | | - <Text> </Text> |
116 | | - |
117 | | - {hasMoreAbove && ( |
118 | | - <Text color="gray" italic> |
119 | | - ⬆ {scrollOffset} more sessions above... |
| 128 | + <Box flexDirection="row" width={terminalWidth}> |
| 129 | + {/* Left side: Session list */} |
| 130 | + <Box {...defaultBoxStyles("blue")} width={listWidth}> |
| 131 | + <Text color="blue" bold> |
| 132 | + Recent Sessions{" "} |
| 133 | + {sessions.length > displaySessions.length && |
| 134 | + `(${selectedIndex + 1}/${sessions.length})`} |
120 | 135 | </Text> |
121 | | - )} |
| 136 | + <Text color="gray">↑/↓ to navigate, Enter to select, Esc to exit</Text> |
| 137 | + <Text> </Text> |
| 138 | + |
| 139 | + {hasMoreAbove && ( |
| 140 | + <Text color="gray" italic> |
| 141 | + ⬆ {scrollOffset} more sessions above... |
| 142 | + </Text> |
| 143 | + )} |
| 144 | + |
| 145 | + {displaySessions.map((session, index) => { |
| 146 | + const globalIndex = index + scrollOffset; |
| 147 | + const isSelected = globalIndex === selectedIndex; |
| 148 | + const indicator = isSelected ? "➤ " : " "; |
| 149 | + const color = isSelected ? "blue" : "white"; |
| 150 | + |
| 151 | + return ( |
| 152 | + <Box key={session.sessionId} flexDirection="column"> |
| 153 | + <Box paddingRight={3}> |
| 154 | + <Text bold={isSelected} color={color} wrap="truncate-end"> |
| 155 | + {indicator} |
| 156 | + {formatMessage(session.title)} |
| 157 | + </Text> |
| 158 | + </Box> |
| 159 | + <Box marginLeft={2}> |
| 160 | + <Text color="gray"> |
| 161 | + {formatTimestamp(new Date(session.dateCreated))} |
| 162 | + {session.isRemote ? " (remote)" : " (local)"} |
| 163 | + </Text> |
| 164 | + </Box> |
| 165 | + {index < displaySessions.length - 1 && ( |
| 166 | + <Text key={`spacer-${session.sessionId}`}> </Text> |
| 167 | + )} |
| 168 | + </Box> |
| 169 | + ); |
| 170 | + })} |
| 171 | + |
| 172 | + {hasMoreBelow && ( |
| 173 | + <Text color="gray" italic> |
| 174 | + ⬇ {sessions.length - scrollOffset - displaySessions.length} more |
| 175 | + sessions below... |
| 176 | + </Text> |
| 177 | + )} |
| 178 | + </Box> |
122 | 179 |
|
123 | | - {displaySessions.map((session, index) => { |
124 | | - const globalIndex = index + scrollOffset; |
125 | | - const isSelected = globalIndex === selectedIndex; |
126 | | - const indicator = isSelected ? "➤ " : " "; |
127 | | - const color = isSelected ? "blue" : "white"; |
128 | | - |
129 | | - return ( |
130 | | - <Box key={session.sessionId} flexDirection="column"> |
131 | | - <Box paddingRight={3}> |
132 | | - <Text bold={isSelected} color={color} wrap="truncate-end"> |
133 | | - {indicator} |
134 | | - {formatMessage(session.title)} |
| 180 | + {/* Right side: Preview panel */} |
| 181 | + {showPreview && ( |
| 182 | + <Box marginLeft={1} flexGrow={1} width="100%"> |
| 183 | + {previewSession ? ( |
| 184 | + <SessionPreview |
| 185 | + chatHistory={previewSession.history} |
| 186 | + sessionTitle={previewSession.title} |
| 187 | + /> |
| 188 | + ) : ( |
| 189 | + <Box |
| 190 | + {...defaultBoxStyles("blue")} |
| 191 | + flexDirection="column" |
| 192 | + width="100%" |
| 193 | + > |
| 194 | + <Text color="blue" bold> |
| 195 | + Preview |
135 | 196 | </Text> |
136 | | - </Box> |
137 | | - <Box marginLeft={2}> |
138 | 197 | <Text color="gray"> |
139 | | - {formatTimestamp(new Date(session.dateCreated))} |
140 | | - {session.isRemote ? " (remote)" : " (local)"} |
| 198 | + {sessions[selectedIndex]?.isRemote |
| 199 | + ? "(remote session preview not available)" |
| 200 | + : "(loading...)"} |
141 | 201 | </Text> |
142 | 202 | </Box> |
143 | | - {index < displaySessions.length - 1 && ( |
144 | | - <Text key={`spacer-${session.sessionId}`}> </Text> |
145 | | - )} |
146 | | - </Box> |
147 | | - ); |
148 | | - })} |
149 | | - |
150 | | - {hasMoreBelow && ( |
151 | | - <Text color="gray" italic> |
152 | | - ⬇ {sessions.length - scrollOffset - displaySessions.length} more |
153 | | - sessions below... |
154 | | - </Text> |
| 203 | + )} |
| 204 | + </Box> |
155 | 205 | )} |
156 | 206 | </Box> |
157 | 207 | ); |
|
0 commit comments