Skip to content

Commit 516251e

Browse files
committed
Make project picker screen work when there's not much screen height
1 parent 02a537b commit 516251e

File tree

1 file changed

+95
-78
lines changed

1 file changed

+95
-78
lines changed

cli/src/components/project-picker-screen.tsx

Lines changed: 95 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,20 @@ const LAYOUT = {
2727
PREFERRED_CONTENT_WIDTH: 60,
2828
CONTENT_PADDING: 4,
2929

30-
// Height thresholds for showing UI elements
31-
MIN_HEIGHT_FOR_LOGO: 14,
32-
MIN_HEIGHT_FOR_HELP_TEXT: 18,
33-
MIN_HEIGHT_FOR_RECENTS: 10,
30+
// Essential element heights (always shown)
31+
INPUT_HEIGHT: 1,
32+
BOTTOM_BAR_HEIGHT: 2,
33+
MIN_LIST_HEIGHT: 2, // Minimum rows to show in file picker
34+
MAX_LIST_HEIGHT: 12,
3435

35-
// Thresholds for number of recent projects to show
36-
HEIGHT_FOR_3_RECENTS: 16,
37-
HEIGHT_FOR_2_RECENTS: 12,
36+
// Compact mode threshold - below this, remove padding/margins
37+
COMPACT_MODE_THRESHOLD: 12,
3838

39-
// Approximate heights of UI elements (in rows)
40-
BASE_HEIGHT: 10, // input (~3) + bottom bar (~3) + padding (~4)
39+
// Decorative element heights
4140
LOGO_HEIGHT: 8,
4241
HELP_TEXT_HEIGHT: 2,
4342

44-
// Directory list constraints
45-
MIN_LIST_HEIGHT: 4,
46-
MAX_LIST_HEIGHT: 12,
47-
48-
// Bottom bar height (border + content)
49-
BOTTOM_BAR_HEIGHT: 2,
50-
51-
// Spacing constants
43+
// Spacing constants (used in normal mode)
5244
MAIN_CONTENT_PADDING: 2,
5345
LOGO_MARGIN_TOP: 1,
5446
LOGO_MARGIN_BOTTOM: 1,
@@ -114,54 +106,82 @@ export const ProjectPickerScreen: React.FC<ProjectPickerScreenProps> = ({
114106
}, [])
115107

116108
// Use the terminal layout hook for responsive breakpoints
117-
const { width, terminalWidth, terminalHeight } = useTerminalLayout()
118-
const isNarrow = width.is('xs')
119-
const contentMaxWidth = Math.min(terminalWidth - LAYOUT.CONTENT_PADDING, LAYOUT.MAX_CONTENT_WIDTH)
109+
const { terminalWidth, terminalHeight } = useTerminalLayout()
110+
const contentMaxWidth = Math.min(
111+
terminalWidth - LAYOUT.CONTENT_PADDING,
112+
LAYOUT.MAX_CONTENT_WIDTH,
113+
)
120114
const contentWidth = Math.min(LAYOUT.PREFERRED_CONTENT_WIDTH, contentMaxWidth)
121115

122-
// Granular responsive breakpoints for showing UI elements
123-
const canShowLogo = terminalHeight >= LAYOUT.MIN_HEIGHT_FOR_LOGO
124-
const canShowHelpText = terminalHeight >= LAYOUT.MIN_HEIGHT_FOR_HELP_TEXT
125-
const canShowRecents = terminalHeight >= LAYOUT.MIN_HEIGHT_FOR_RECENTS && recentProjects.length > 0
126-
127-
// Limit number of recent projects based on available height
128-
const maxRecentsToShow = terminalHeight >= LAYOUT.HEIGHT_FOR_3_RECENTS ? 3
129-
: terminalHeight >= LAYOUT.HEIGHT_FOR_2_RECENTS ? 2
130-
: 1
131-
132-
// Calculate available height for directory list dynamically
133-
const logoHeight = canShowLogo ? LAYOUT.LOGO_HEIGHT : 0
134-
const helpTextHeight = canShowHelpText ? LAYOUT.HELP_TEXT_HEIGHT : 0
135-
const recentsHeight = canShowRecents ? Math.min(recentProjects.length, maxRecentsToShow) + 1 : 0 // +1 for header
136-
const fixedElementsHeight = LAYOUT.BASE_HEIGHT + logoHeight + helpTextHeight + recentsHeight
137-
const availableListHeight = terminalHeight - fixedElementsHeight
138-
139-
// Only show file picker if we have at least MIN_LIST_HEIGHT rows available
140-
const canShowFilePicker = availableListHeight >= LAYOUT.MIN_LIST_HEIGHT
141-
// Clamp the height between min and max
142-
const maxListHeight = canShowFilePicker
143-
? Math.min(availableListHeight, LAYOUT.MAX_LIST_HEIGHT)
144-
: 0
145-
146-
// Calculate actual content height to determine if we should center vertically
147-
const actualListHeight = canShowFilePicker ? Math.min(filteredDirectoryItems.length + 2, maxListHeight + 2) : 0 // +2 for border
148-
const inputHeight = 3 // input box with border
149-
const totalContentHeight =
150-
LAYOUT.MAIN_CONTENT_PADDING * 2 + // top and bottom padding
151-
logoHeight +
152-
(canShowLogo ? LAYOUT.LOGO_MARGIN_TOP + LAYOUT.LOGO_MARGIN_BOTTOM : 0) +
153-
helpTextHeight +
154-
(canShowHelpText ? LAYOUT.HELP_TEXT_MARGIN_BOTTOM : 0) +
155-
inputHeight +
156-
actualListHeight +
157-
recentsHeight +
158-
(canShowRecents ? LAYOUT.RECENTS_MARGIN_TOP : 0)
159-
160-
// Available space for content (total height minus bottom bar)
161-
const availableContentHeight = terminalHeight - LAYOUT.BOTTOM_BAR_HEIGHT
162-
163-
// Center content when it doesn't fill the available space
164-
const shouldCenterContent = totalContentHeight < availableContentHeight
116+
// Compact mode: remove padding/margins when space is tight
117+
const isCompactMode = terminalHeight < LAYOUT.COMPACT_MODE_THRESHOLD
118+
const mainPadding = isCompactMode ? 0 : LAYOUT.MAIN_CONTENT_PADDING
119+
120+
// Calculate essential height first (these always show)
121+
// Essential = input (1) + file picker border (2) + bottom bar (2) + minimal padding
122+
const essentialHeight =
123+
LAYOUT.INPUT_HEIGHT + 2 + LAYOUT.BOTTOM_BAR_HEIGHT + (isCompactMode ? 0 : 2)
124+
125+
// Calculate remaining height for file picker and optional elements
126+
const remainingHeight = terminalHeight - essentialHeight
127+
128+
// File picker gets priority - calculate how much space it needs
129+
const filePickerHeight = Math.max(
130+
LAYOUT.MIN_LIST_HEIGHT,
131+
Math.min(remainingHeight, LAYOUT.MAX_LIST_HEIGHT),
132+
)
133+
134+
// After file picker, calculate space for optional elements
135+
const spaceAfterFilePicker = remainingHeight - filePickerHeight
136+
137+
// Determine which optional elements can fit (priority: recents first, then logo, then help text)
138+
const logoHeightNeeded =
139+
LAYOUT.LOGO_HEIGHT +
140+
(isCompactMode ? 0 : LAYOUT.LOGO_MARGIN_TOP + LAYOUT.LOGO_MARGIN_BOTTOM)
141+
const helpTextHeightNeeded =
142+
LAYOUT.HELP_TEXT_HEIGHT +
143+
(isCompactMode ? 0 : LAYOUT.HELP_TEXT_MARGIN_BOTTOM)
144+
145+
// Allocate space for optional elements based on available space
146+
let availableForOptional = spaceAfterFilePicker
147+
148+
// Try to fit recents first (most useful)
149+
let recentsToShow = 0
150+
if (recentProjects.length > 0 && availableForOptional >= 2) {
151+
// Calculate how many recents fit
152+
const baseRecentsHeight =
153+
1 + (isCompactMode ? 0 : LAYOUT.RECENTS_MARGIN_TOP) // header + margin
154+
const remainingForRecents = availableForOptional - baseRecentsHeight
155+
recentsToShow = Math.min(
156+
recentProjects.length,
157+
Math.max(0, remainingForRecents),
158+
3,
159+
)
160+
if (recentsToShow > 0) {
161+
availableForOptional -=
162+
recentsToShow + 1 + (isCompactMode ? 0 : LAYOUT.RECENTS_MARGIN_TOP)
163+
}
164+
}
165+
166+
// Try to fit logo (decorative but nice)
167+
const canShowLogo = !isCompactMode && availableForOptional >= logoHeightNeeded
168+
if (canShowLogo) {
169+
availableForOptional -= logoHeightNeeded
170+
}
171+
172+
// Try to fit help text (least important)
173+
const canShowHelpText =
174+
!isCompactMode && availableForOptional >= helpTextHeightNeeded
175+
176+
const canShowRecents = recentsToShow > 0
177+
const maxRecentsToShow = recentsToShow
178+
179+
// File picker is always shown if there's any space
180+
const canShowFilePicker = remainingHeight >= LAYOUT.MIN_LIST_HEIGHT
181+
const maxListHeight = filePickerHeight
182+
183+
// Center content only in non-compact mode when there's extra space
184+
const shouldCenterContent = !isCompactMode && spaceAfterFilePicker > 10
165185

166186
// Logo setup
167187
const blockColor = getLogoBlockColor(theme.name)
@@ -284,21 +304,21 @@ export const ProjectPickerScreen: React.FC<ProjectPickerScreenProps> = ({
284304
alignItems: 'center',
285305
justifyContent: shouldCenterContent ? 'center' : 'flex-start',
286306
width: '100%',
287-
padding: LAYOUT.MAIN_CONTENT_PADDING,
288-
gap: 1,
307+
padding: mainPadding,
308+
gap: isCompactMode ? 0 : 1,
289309
flexGrow: 1,
290310
flexShrink: 1,
291311
}}
292312
>
293-
{/* Logo - show when terminal height >= 14 */}
313+
{/* Logo - show when there's enough space after essentials */}
294314
{canShowLogo && (
295315
<box
296316
style={{
297317
flexDirection: 'column',
298318
alignItems: 'center',
299319
width: '100%',
300-
marginTop: LAYOUT.LOGO_MARGIN_TOP,
301-
marginBottom: LAYOUT.LOGO_MARGIN_BOTTOM,
320+
marginTop: isCompactMode ? 0 : LAYOUT.LOGO_MARGIN_TOP,
321+
marginBottom: isCompactMode ? 0 : LAYOUT.LOGO_MARGIN_BOTTOM,
302322
flexShrink: 0,
303323
}}
304324
>
@@ -308,14 +328,14 @@ export const ProjectPickerScreen: React.FC<ProjectPickerScreenProps> = ({
308328
</box>
309329
)}
310330

311-
{/* Help text - show when terminal height >= 18 */}
331+
{/* Help text - show only when there's plenty of space */}
312332
{canShowHelpText && (
313333
<box
314334
style={{
315335
flexDirection: 'column',
316336
alignItems: 'center',
317337
maxWidth: contentMaxWidth,
318-
marginBottom: LAYOUT.HELP_TEXT_MARGIN_BOTTOM,
338+
marginBottom: isCompactMode ? 0 : LAYOUT.HELP_TEXT_MARGIN_BOTTOM,
319339
flexShrink: 0,
320340
}}
321341
>
@@ -339,7 +359,7 @@ export const ProjectPickerScreen: React.FC<ProjectPickerScreenProps> = ({
339359
onSubmit={() => {}} // Enter key handled by onKeyIntercept
340360
onPaste={() => {}} // Paste not needed for path input
341361
onKeyIntercept={handleSearchKeyIntercept}
342-
placeholder="Type path or filter (Tab to complete)..."
362+
placeholder="Select project directory..."
343363
focused={true}
344364
maxHeight={1}
345365
minHeight={1}
@@ -372,13 +392,13 @@ export const ProjectPickerScreen: React.FC<ProjectPickerScreenProps> = ({
372392
</box>
373393
)}
374394

375-
{/* Recent Projects - show when terminal height >= 10 */}
395+
{/* Recent Projects - show when there's space after file picker */}
376396
{canShowRecents && (
377397
<box
378398
style={{
379399
flexDirection: 'column',
380400
width: contentWidth,
381-
marginTop: LAYOUT.RECENTS_MARGIN_TOP,
401+
marginTop: isCompactMode ? 0 : LAYOUT.RECENTS_MARGIN_TOP,
382402
flexShrink: 0,
383403
gap: 0,
384404
}}
@@ -390,7 +410,7 @@ export const ProjectPickerScreen: React.FC<ProjectPickerScreenProps> = ({
390410
style={{
391411
flexDirection: 'row',
392412
gap: 1,
393-
paddingLeft: LAYOUT.RECENTS_PADDING_LEFT,
413+
paddingLeft: isCompactMode ? 0 : LAYOUT.RECENTS_PADDING_LEFT,
394414
height: 1,
395415
}}
396416
>
@@ -405,7 +425,6 @@ export const ProjectPickerScreen: React.FC<ProjectPickerScreenProps> = ({
405425
))}
406426
</box>
407427
)}
408-
409428
</box>
410429

411430
{/* Bottom bar - fixed at bottom with Open button */}
@@ -434,9 +453,7 @@ export const ProjectPickerScreen: React.FC<ProjectPickerScreenProps> = ({
434453
>
435454
{/* Current directory path */}
436455
<box style={{ flexGrow: 1, flexShrink: 1, overflow: 'hidden' }}>
437-
<text style={{ fg: theme.muted }}>
438-
{formatCwd(currentPath)}
439-
</text>
456+
<text style={{ fg: theme.muted }}>{formatCwd(currentPath)}</text>
440457
</box>
441458

442459
{/* Open button */}

0 commit comments

Comments
 (0)