apps/blobs/tui_view.go 3.6 K raw
1
package main
2
3
import (
4
	"fmt"
5
6
	tea "charm.land/bubbletea/v2"
7
	"charm.land/lipgloss/v2"
8
	sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
9
)
10
11
var (
12
	paneBorder       = sharedtui.Border(lipgloss.RoundedBorder())
13
	paneBorderActive = sharedtui.BorderActive(lipgloss.RoundedBorder())
14
	tuiTitleStyle    = sharedtui.TitleStyle
15
	tuiOKStyle       = sharedtui.StatusOKStyle
16
	tuiErrStyle      = sharedtui.StatusErrStyle
17
	tuiHintStyle     = sharedtui.HintStyle
18
	tuiModalStyle    = sharedtui.ModalStyle
19
	tuiStatusModal   = sharedtui.StatusModalStyle
20
)
21
22
func paneFrameW() int { return paneBorder.GetHorizontalFrameSize() }
23
func paneFrameH() int { return paneBorder.GetVerticalFrameSize() }
24
25
func (m tuiModel) View() tea.View {
26
	if !m.ready {
27
		return tea.View{Content: "loading...", AltScreen: true}
28
	}
29
30
	bodyH := m.height - 2
31
	if bodyH < 5 {
32
		bodyH = 5
33
	}
34
35
	var body string
36
	switch m.state {
37
	case stateBuckets:
38
		body = paneBorderActive.Width(m.width).Height(bodyH).Render(m.bucketsList.View())
39
	case stateBrowse:
40
		body = m.renderBrowse()
41
	}
42
43
	footer := m.renderFooter()
44
	base := lipgloss.JoinVertical(lipgloss.Left, body, footer)
45
46
	var overlays []*lipgloss.Layer
47
	if m.showHelp {
48
		overlays = append(overlays, centerLay(m.width, m.height,
49
			tuiModalStyle.Render(m.help.FullHelpView(m.keys.FullHelp())), 1))
50
	}
51
	if m.confirmDelete {
52
		name := ""
53
		if f, ok := m.selectedFile(); ok {
54
			name = f.Name
55
		}
56
		overlays = append(overlays, centerLay(m.width, m.height,
57
			tuiModalStyle.Render(fmt.Sprintf("Delete %q?\n\ny / n", name)), 2))
58
	}
59
	if m.uploadPrompt == uploadPromptActive {
60
		dest := m.currentBucket + ":/" + m.currentPrefix
61
		overlays = append(overlays, centerLay(m.width, m.height,
62
			tuiModalStyle.Render("Upload to "+dest+"\n\n"+m.uploadInput.View()+"\n\nenter=upload  esc=cancel"), 2))
63
	}
64
	if m.status != "" {
65
		st := tuiOKStyle
66
		if !m.statusOK {
67
			st = tuiErrStyle
68
		}
69
		overlays = append(overlays, bottomLay(m.width, m.height,
70
			tuiStatusModal.Render(st.Render(m.status)), 3))
71
	}
72
73
	content := base
74
	if len(overlays) > 0 {
75
		layers := append([]*lipgloss.Layer{lipgloss.NewLayer(base)}, overlays...)
76
		canvas := lipgloss.NewCanvas(m.width, m.height)
77
		canvas.Compose(lipgloss.NewCompositor(layers...))
78
		content = canvas.Render()
79
	}
80
81
	return tea.View{Content: content, AltScreen: true}
82
}
83
84
func (m tuiModel) renderBrowse() string {
85
	bodyH := m.height - 2
86
	if bodyH < 5 {
87
		bodyH = 5
88
	}
89
	if !m.showPreview {
90
		return paneBorderActive.Width(m.width).Height(bodyH).Render(m.browseList.View())
91
	}
92
	listW := m.width / 2
93
	previewW := m.width - listW
94
	left := paneBorderActive.Width(listW).Height(bodyH).Render(m.browseList.View())
95
	header := tuiTitleStyle.Render("preview")
96
	previewBody := m.preview.View()
97
	inner := lipgloss.JoinVertical(lipgloss.Left, header, previewBody)
98
	right := paneBorder.Width(previewW).Height(bodyH).Render(inner)
99
	return lipgloss.JoinHorizontal(lipgloss.Top, left, right)
100
}
101
102
func (m tuiModel) renderFooter() string {
103
	label := m.currentBucket
104
	if m.state == stateBrowse {
105
		label += ":/" + m.currentPrefix
106
	} else {
107
		label = "buckets"
108
	}
109
	help := m.help.ShortHelpView(m.keys.ShortHelp())
110
	return tuiHintStyle.Render(fmt.Sprintf("[%s] %s", label, help))
111
}
112
113
func centerLay(w, h int, content string, z int) *lipgloss.Layer {
114
	cw, ch := lipgloss.Width(content), lipgloss.Height(content)
115
	x := (w - cw) / 2
116
	y := (h - ch) / 2
117
	if x < 0 {
118
		x = 0
119
	}
120
	if y < 0 {
121
		y = 0
122
	}
123
	return lipgloss.NewLayer(content).X(x).Y(y).Z(z)
124
}
125
126
func bottomLay(w, h int, content string, z int) *lipgloss.Layer {
127
	cw, ch := lipgloss.Width(content), lipgloss.Height(content)
128
	x := (w - cw) / 2
129
	y := h - ch - 1
130
	if x < 0 {
131
		x = 0
132
	}
133
	if y < 0 {
134
		y = 0
135
	}
136
	return lipgloss.NewLayer(content).X(x).Y(y).Z(z)
137
}