fix(filepicker): general fixes to size and position

Andrey Nering created

* The image and dialog are now rendered at a fixed height. Before, the
  height could change as you navigate, which did not look well in
  practice.
* We'll now hide the preview if the terminal height is not enought for
  it.
* Better position. Before, in some scenarios the dialog was been rendered
  too close to the bottom, instead of centered.

Change summary

internal/tui/components/dialogs/filepicker/filepicker.go | 36 ++++++++-
1 file changed, 29 insertions(+), 7 deletions(-)

Detailed changes

internal/tui/components/dialogs/filepicker/filepicker.go 🔗

@@ -24,6 +24,7 @@ const (
 	MaxAttachmentSize   = int64(5 * 1024 * 1024) // 5MB
 	FilePickerID        = "filepicker"
 	fileSelectionHeight = 10
+	previewHeight       = 20
 )
 
 type FilePickedMsg struct {
@@ -160,13 +161,25 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 func (m *model) View() string {
 	t := styles.CurrentTheme()
 
-	content := lipgloss.JoinVertical(
-		lipgloss.Left,
+	strs := []string{
 		t.S().Base.Padding(0, 1, 1, 1).Render(core.Title("Add Image", m.width-4)),
-		m.imagePreview(),
+	}
+
+	// hide image preview if the terminal is too small
+	if x, y := m.imagePreviewSize(); x > 0 && y > 0 {
+		strs = append(strs, m.imagePreview())
+	}
+
+	strs = append(
+		strs,
 		m.filePicker.View(),
 		t.S().Base.Width(m.width-2).PaddingLeft(1).AlignHorizontal(lipgloss.Left).Render(m.help.View(m.keyMap)),
 	)
+
+	content := lipgloss.JoinVertical(
+		lipgloss.Left,
+		strs...,
+	)
 	return m.style().Render(content)
 }
 
@@ -180,12 +193,15 @@ func (m *model) currentImage() string {
 }
 
 func (m *model) imagePreview() string {
+	const padding = 2
+
 	t := styles.CurrentTheme()
 	w, h := m.imagePreviewSize()
+
 	if m.currentImage() == "" {
 		imgPreview := t.S().Base.
-			Width(w).
-			Height(h).
+			Width(w - padding).
+			Height(h - padding).
 			Background(t.BgOverlay)
 
 		return m.imagePreviewStyle().Render(imgPreview.Render())
@@ -200,7 +216,10 @@ func (m *model) imagePreviewStyle() lipgloss.Style {
 }
 
 func (m *model) imagePreviewSize() (int, int) {
-	return m.width - 4, min(20, m.wHeight/2)
+	if m.wHeight-fileSelectionHeight-8 > previewHeight {
+		return m.width - 4, previewHeight
+	}
+	return 0, 0
 }
 
 func (m *model) style() lipgloss.Style {
@@ -218,7 +237,10 @@ func (m *model) ID() dialogs.DialogID {
 
 // Position implements FilePicker.
 func (m *model) Position() (int, int) {
-	row := m.wHeight/4 - 2 // just a bit above the center
+	_, imageHeight := m.imagePreviewSize()
+	dialogHeight := fileSelectionHeight + imageHeight + 4
+	row := (m.wHeight - dialogHeight) / 2
+
 	col := m.wWidth / 2
 	col -= m.width / 2
 	return row, col