tui: add a "scroll bar" and error view

This commit is contained in:
Matt Low 2024-03-14 02:54:25 +00:00
parent 387dd7534c
commit 000a2ec6f2

View File

@ -29,6 +29,9 @@ const (
)
type model struct {
width int
height int
ctx *lmcli.Context
convShortname string
@ -135,8 +138,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
m.content.Width = msg.Width
m.content.Height = msg.Height - m.input.Height() - lipgloss.Height(m.footerView()) - lipgloss.Height(m.headerView())
m.content.Height = msg.Height - m.getFixedComponentHeight()
m.input.SetWidth(msg.Width-1)
m.updateContent()
case msgConversationLoaded:
@ -202,6 +207,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, wrapError(err))
}
}
case msgError:
m.err = error(msg)
}
if len(cmds) > 0 {
@ -229,15 +236,45 @@ func (m model) View() string {
// without this, the below view functions may do weird things
return ""
}
m.content.Height = m.height - m.getFixedComponentHeight()
sections := make([]string, 0, 6)
error := m.errorView()
scrollbar := m.scrollbarView()
sections = append(sections, m.headerView())
if scrollbar != "" {
sections = append(sections, scrollbar)
}
sections = append(sections, m.contentView())
if error != "" {
sections = append(sections, error)
}
sections = append(sections, m.inputView())
sections = append(sections, m.footerView())
return lipgloss.JoinVertical(
lipgloss.Left,
m.headerView(),
m.contentView(),
m.inputView(),
m.footerView(),
sections...,
)
}
func (m *model) getFixedComponentHeight() int {
h := 0
h += m.input.Height()
h += lipgloss.Height(m.headerView())
h += lipgloss.Height(m.footerView())
scrollbar := m.scrollbarView()
if scrollbar != "" {
h += lipgloss.Height(scrollbar)
}
errorView := m.errorView()
if errorView != "" {
h += lipgloss.Height(errorView)
}
return h
}
func (m *model) headerView() string {
titleStyle := lipgloss.NewStyle().
PaddingLeft(1).
@ -258,6 +295,32 @@ func (m *model) contentView() string {
return m.content.View()
}
func (m *model) errorView() string {
if m.err == nil {
return ""
}
return lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("1")).
Width(m.content.Width).
AlignHorizontal(lipgloss.Center).
Render(fmt.Sprintf("%s", m.err))
}
func (m *model) scrollbarView() string {
if m.content.AtTop() {
return ""
}
count := int(m.content.ScrollPercent() * float64(m.content.Width-2))
fill := strings.Repeat("-", count)
return lipgloss.NewStyle().
PaddingLeft(1).
PaddingRight(1).
Width(m.content.Width).
Render(fill)
}
func (m *model) inputView() string {
return m.input.View()
}
@ -284,8 +347,8 @@ func (m *model) footerView() string {
left := strings.Join(leftSegments, segmentSeparator)
right := strings.Join(rightSegments, segmentSeparator)
totalWidth := lipgloss.Width(left + right)
remaining := m.content.Width - totalWidth
totalWidth := lipgloss.Width(left) + lipgloss.Width(right)
remaining := m.width - totalWidth
var padding string
if remaining > 0 {
@ -295,7 +358,7 @@ func (m *model) footerView() string {
footer := left + padding + right
if remaining < 0 {
ellipses := "... "
footer = footer[:m.content.Width-len(ellipses)] + ellipses
footer = footer[:m.width-len(ellipses)] + ellipses
}
return footerStyle.Render(footer)
}