tui: dynamic input textarea height and styling updates

Maintain a height of 4 up to half of the main content area

Add rounded border
This commit is contained in:
Matt Low 2024-03-29 19:25:16 +00:00
parent 5af857edae
commit 0b991800d6

View File

@ -126,9 +126,7 @@ var (
PaddingLeft(1). PaddingLeft(1).
PaddingRight(1). PaddingRight(1).
Background(lipgloss.Color("0")) Background(lipgloss.Color("0"))
footerStyle = lipgloss.NewStyle(). footerStyle = lipgloss.NewStyle()
BorderTop(true).
BorderStyle(lipgloss.NormalBorder())
) )
func (m model) Init() tea.Cmd { func (m model) Init() tea.Cmd {
@ -206,7 +204,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.width = msg.Width m.width = msg.Width
m.height = msg.Height m.height = msg.Height
m.content.Width = msg.Width m.content.Width = msg.Width
m.input.SetWidth(msg.Width) m.input.SetWidth(msg.Width-m.input.FocusedStyle.Base.GetHorizontalBorderSize())
m.rebuildMessageCache() m.rebuildMessageCache()
m.updateContent() m.updateContent()
case msgConversationLoaded: case msgConversationLoaded:
@ -295,6 +293,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
} }
prevInputLineCnt := m.input.LineCount()
inputCaptured := false inputCaptured := false
m.input, cmd = m.input.Update(msg) m.input, cmd = m.input.Update(msg)
if cmd != nil { if cmd != nil {
@ -313,12 +313,42 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.cache.header = m.headerView() m.cache.header = m.headerView()
m.cache.footer = m.footerView() m.cache.footer = m.footerView()
m.cache.error = m.errorView() m.cache.error = m.errorView()
fixedHeight := height(m.cache.header) + height(m.cache.error) + height(m.cache.footer)
// calculate clamped input height to accomodate input text
newHeight := max(4, min((m.height-fixedHeight-1)/2, m.input.LineCount()))
m.input.SetHeight(newHeight)
m.cache.input = m.inputView() m.cache.input = m.inputView()
fixedHeight := height(m.cache.header) + height(m.cache.error) + height(m.cache.input) + height(m.cache.footer)
m.content.Height = m.height - fixedHeight m.content.Height = m.height - height(m.cache.input) - fixedHeight
m.cache.content = m.contentView() m.cache.content = m.contentView()
} }
// this is a pretty nasty hack to ensure the input area viewport doesn't
// scroll below its content, which can happen when the input viewport
// height has grown, or previously entered lines have been deleted
if prevInputLineCnt != m.input.LineCount() {
// dist is the distance we'd need to scroll up from the current cursor
// position to position the last input line at the bottom of the
// viewport. if negative, we're already scrolled above the bottom
dist := m.input.Line() - (m.input.LineCount() - m.input.Height())
if dist > 0 {
for i := 0; i < dist; i++ {
// move cursor up until content reaches the bottom of the viewport
m.input.CursorUp()
}
m.input, cmd = m.input.Update(nil)
cmds = append(cmds, cmd)
for i := 0; i < dist; i++ {
// move cursor back down to its previous position
m.input.CursorDown()
}
m.input, cmd = m.input.Update(nil)
cmds = append(cmds, cmd)
}
}
return m, tea.Batch(cmds...) return m, tea.Batch(cmds...)
} }
@ -468,16 +498,13 @@ func initialModel(ctx *lmcli.Context, convShortname string) model {
m.input.Placeholder = "Enter a message" m.input.Placeholder = "Enter a message"
m.input.ShowLineNumbers = false m.input.ShowLineNumbers = false
m.input.SetHeight(4)
m.input.Focus() m.input.Focus()
m.input.FocusedStyle.CursorLine = lipgloss.NewStyle() m.input.FocusedStyle.CursorLine = lipgloss.NewStyle()
m.input.FocusedStyle.Base = lipgloss.NewStyle(). m.input.FocusedStyle.Base = lipgloss.NewStyle().
BorderTop(true). Border(lipgloss.RoundedBorder(), true, true, true, false)
BorderStyle(lipgloss.NormalBorder())
m.input.BlurredStyle.Base = lipgloss.NewStyle(). m.input.BlurredStyle.Base = lipgloss.NewStyle().
Faint(true). Faint(true).
BorderTop(true). Border(lipgloss.RoundedBorder(), true, true, true, false)
BorderStyle(lipgloss.NormalBorder())
m.spinner = spinner.New(spinner.WithSpinner( m.spinner = spinner.New(spinner.WithSpinner(
spinner.Spinner{ spinner.Spinner{