tui: Chat view footer rewrite
Rewrote footer handling to better handle truncation, and use `ActiveModel` to return the (stylized) active model
This commit is contained in:
parent
69cdc0a5aa
commit
2fed682969
@ -54,6 +54,13 @@ func Height(str string) int {
|
|||||||
return strings.Count(str, "\n") + 1
|
return strings.Count(str, "\n") + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Width(str string) int {
|
||||||
|
if str == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return ansi.PrintableRuneWidth(str)
|
||||||
|
}
|
||||||
|
|
||||||
// truncate a string until its rendered cell width + the provided tail fits
|
// truncate a string until its rendered cell width + the provided tail fits
|
||||||
// within the given width
|
// within the given width
|
||||||
func TruncateToCellWidth(str string, width int, tail string) string {
|
func TruncateToCellWidth(str string, width int, tail string) string {
|
||||||
|
@ -239,7 +239,7 @@ func (m *Model) Content(width, height int) string {
|
|||||||
input := m.input.View()
|
input := m.input.View()
|
||||||
|
|
||||||
// remaining height towards content
|
// remaining height towards content
|
||||||
m.content.Width, m.content.Height = width, height - tuiutil.Height(input)
|
m.content.Width, m.content.Height = width, height-tuiutil.Height(input)
|
||||||
content := m.content.View()
|
content := m.content.View()
|
||||||
return lipgloss.JoinVertical(lipgloss.Left, content, input)
|
return lipgloss.JoinVertical(lipgloss.Left, content, input)
|
||||||
}
|
}
|
||||||
@ -261,6 +261,10 @@ func (m *Model) Footer(width int) string {
|
|||||||
segmentStyle := lipgloss.NewStyle().PaddingLeft(1).PaddingRight(1).Faint(true)
|
segmentStyle := lipgloss.NewStyle().PaddingLeft(1).PaddingRight(1).Faint(true)
|
||||||
segmentSeparator := "|"
|
segmentSeparator := "|"
|
||||||
|
|
||||||
|
// Left segments
|
||||||
|
|
||||||
|
leftSegments := []string{}
|
||||||
|
|
||||||
savingStyle := segmentStyle.Bold(true)
|
savingStyle := segmentStyle.Bold(true)
|
||||||
saving := ""
|
saving := ""
|
||||||
if m.persistence {
|
if m.persistence {
|
||||||
@ -268,6 +272,20 @@ func (m *Model) Footer(width int) string {
|
|||||||
} else {
|
} else {
|
||||||
saving = savingStyle.Foreground(lipgloss.Color("1")).Render("❌💾")
|
saving = savingStyle.Foreground(lipgloss.Color("1")).Render("❌💾")
|
||||||
}
|
}
|
||||||
|
leftSegments = append(leftSegments, saving)
|
||||||
|
|
||||||
|
// Right segments
|
||||||
|
|
||||||
|
rightSegments := []string{}
|
||||||
|
|
||||||
|
if m.elapsed > 0 && m.tokenCount > 0 {
|
||||||
|
throughput := fmt.Sprintf("%.0f t/sec", float64(m.tokenCount)/m.elapsed.Seconds())
|
||||||
|
rightSegments = append(rightSegments, segmentStyle.Render(throughput))
|
||||||
|
}
|
||||||
|
model := segmentStyle.Render(m.App.ActiveModel(lipgloss.NewStyle()))
|
||||||
|
rightSegments = append(rightSegments, model)
|
||||||
|
|
||||||
|
// Status
|
||||||
|
|
||||||
var status string
|
var status string
|
||||||
switch m.state {
|
switch m.state {
|
||||||
@ -277,39 +295,62 @@ func (m *Model) Footer(width int) string {
|
|||||||
status = "Press ctrl+s to send"
|
status = "Press ctrl+s to send"
|
||||||
}
|
}
|
||||||
|
|
||||||
leftSegments := []string{
|
return m.layoutFooter(width, leftSegments, status, rightSegments, segmentStyle, segmentSeparator)
|
||||||
saving,
|
}
|
||||||
segmentStyle.Render(status),
|
|
||||||
}
|
|
||||||
rightSegments := []string{}
|
|
||||||
|
|
||||||
if m.elapsed > 0 && m.tokenCount > 0 {
|
func (m *Model) layoutFooter(
|
||||||
throughput := fmt.Sprintf("%.0f t/sec", float64(m.tokenCount)/m.elapsed.Seconds())
|
width int,
|
||||||
rightSegments = append(rightSegments, segmentStyle.Render(throughput))
|
leftSegments []string,
|
||||||
|
status string,
|
||||||
|
rightSegments []string,
|
||||||
|
segmentStyle lipgloss.Style,
|
||||||
|
segmentSeparator string,
|
||||||
|
) string {
|
||||||
|
truncate := func(s string, w int) string {
|
||||||
|
return tuiutil.TruncateToCellWidth(s, w, "...")
|
||||||
}
|
}
|
||||||
|
padWidth := segmentStyle.GetHorizontalPadding()
|
||||||
if m.App.ProviderName != "" {
|
|
||||||
provider := fmt.Sprintf("Provider: %s", m.App.ProviderName)
|
|
||||||
rightSegments = append(rightSegments, segmentStyle.Render(provider))
|
|
||||||
}
|
|
||||||
|
|
||||||
model := fmt.Sprintf("Model: %s", m.App.Model)
|
|
||||||
rightSegments = append(rightSegments, segmentStyle.Render(model))
|
|
||||||
|
|
||||||
left := strings.Join(leftSegments, segmentSeparator)
|
left := strings.Join(leftSegments, segmentSeparator)
|
||||||
right := strings.Join(rightSegments, segmentSeparator)
|
right := strings.Join(rightSegments, segmentSeparator)
|
||||||
|
|
||||||
totalWidth := lipgloss.Width(left) + lipgloss.Width(right)
|
leftWidth := tuiutil.Width(left)
|
||||||
remaining := width - totalWidth
|
rightWidth := tuiutil.Width(right)
|
||||||
|
|
||||||
var padding string
|
availableWidth := width - leftWidth - rightWidth - tuiutil.Width(segmentSeparator)
|
||||||
if remaining > 0 {
|
|
||||||
padding = strings.Repeat(" ", remaining)
|
statusWidth := tuiutil.Width(status)
|
||||||
|
if availableWidth >= statusWidth+padWidth {
|
||||||
|
// Everything fits
|
||||||
|
availableWidth -= statusWidth + padWidth
|
||||||
|
padding := ""
|
||||||
|
if availableWidth > 0 {
|
||||||
|
padding = strings.Repeat(" ", availableWidth)
|
||||||
|
}
|
||||||
|
return footerStyle.Render(left + segmentSeparator + segmentStyle.Render(status) + padding + right)
|
||||||
}
|
}
|
||||||
|
|
||||||
footer := left + padding + right
|
if availableWidth > 4 {
|
||||||
if remaining < 0 {
|
// There is some space left for a truncated status
|
||||||
footer = tuiutil.TruncateToCellWidth(footer, width, "...")
|
truncatedStatus := truncate(status, availableWidth-padWidth)
|
||||||
|
return footerStyle.Width(width).Render(left + segmentSeparator + segmentStyle.Render(truncatedStatus) + right)
|
||||||
}
|
}
|
||||||
return footerStyle.Width(width).Render(footer)
|
|
||||||
|
if availableWidth >= 0 {
|
||||||
|
// Draw some dots...
|
||||||
|
dots := ""
|
||||||
|
if availableWidth == 1 {
|
||||||
|
dots = " "
|
||||||
|
} else if availableWidth > 1 {
|
||||||
|
dots = " " + strings.Repeat(".", availableWidth-1)
|
||||||
|
dots = lipgloss.NewStyle().Faint(true).Render(dots)
|
||||||
|
}
|
||||||
|
|
||||||
|
return footerStyle.Width(width).Render(left + segmentSeparator + dots + right)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trucate right segment so it fits
|
||||||
|
right = truncate(right, tuiutil.Width(right)+availableWidth-1)
|
||||||
|
padding := ""
|
||||||
|
return footerStyle.Width(width).Render(left + segmentSeparator + padding + right)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user