@@ -4,8 +4,10 @@ import (
44 "strings"
55
66 tea "charm.land/bubbletea/v2"
7+ "charm.land/lipgloss/v2"
78 "github.com/charmbracelet/crush/internal/ui/common"
89 "github.com/charmbracelet/crush/internal/ui/styles"
10+ "github.com/charmbracelet/x/ansi"
911)
1012
1113// InputCursor adjusts the cursor position for an input field within a dialog.
@@ -43,9 +45,15 @@ type RenderContext struct {
4345 // Width is the total width of the dialog including any margins, borders,
4446 // and paddings.
4547 Width int
48+ // Gap is the gap between content parts. Zero means no gap.
49+ Gap int
4650 // Title is the title of the dialog. This will be styled using the default
4751 // dialog title style and prepended to the content parts slice.
4852 Title string
53+ // TitleInfo is additional information to display next to the title. This
54+ // part is displayed as is, any styling must be applied before setting this
55+ // field.
56+ TitleInfo string
4957 // Parts are the rendered parts of the dialog.
5058 Parts []string
5159 // Help is the help view content. This will be appended to the content parts
@@ -76,55 +84,47 @@ func (rc *RenderContext) Render() string {
7684
7785 parts := []string{}
7886 if len(rc.Title) > 0 {
87+ var titleInfoWidth int
88+ if len(rc.TitleInfo) > 0 {
89+ titleInfoWidth = lipgloss.Width(rc.TitleInfo)
90+ }
7991 title := common.DialogTitle(rc.Styles, rc.Title,
8092 max(0, rc.Width-dialogStyle.GetHorizontalFrameSize()-
81− titleStyle.GetHorizontalFrameSize()))
82− parts = append(parts, titleStyle.Render(title), "")
93+ titleStyle.GetHorizontalFrameSize()-
94+ titleInfoWidth))
95+ if len(rc.TitleInfo) > 0 {
96+ title += rc.TitleInfo
97+ }
98+ parts = append(parts, titleStyle.Render(title))
99+ if rc.Gap > 0 {
100+ parts = append(parts, make([]string, rc.Gap)...)
101+ }
83102 }
84103
85− for i, p := range rc.Parts {
86− if len(p) > 0 {
87− parts = append(parts, p)
88− }
89− if i < len(rc.Parts)-1 {
90− parts = append(parts, "")
104+ if rc.Gap <= 0 {
105+ parts = append(parts, rc.Parts...)
106+ } else {
107+ for i, p := range rc.Parts {
108+ if len(p) > 0 {
109+ parts = append(parts, p)
110+ }
111+ if i < len(rc.Parts)-1 {
112+ parts = append(parts, make([]string, rc.Gap)...)
113+ }
91114 }
92115 }
93116
94117 if len(rc.Help) > 0 {
95− parts = append(parts, "")
118+ if rc.Gap > 0 {
119+ parts = append(parts, make([]string, rc.Gap)...)
120+ }
96121 helpStyle := rc.Styles.Dialog.HelpView
97122 helpStyle = helpStyle.Width(rc.Width - dialogStyle.GetHorizontalFrameSize())
98− parts = append(parts, helpStyle.Render(rc.Help))
123+ helpView := ansi.Truncate(helpStyle.Render(rc.Help), rc.Width, "")
124+ parts = append(parts, helpView)
99125 }
100126
101127 content := strings.Join(parts, "\n")
102128
103129 return dialogStyle.Render(content)
104130}
105−
106−// HeaderInputListHelpView generates a view for dialogs with a header, input,
107−// list, and help sections.
108−func HeaderInputListHelpView(t *styles.Styles, width, listHeight int, header, input, list, help string) string {
109− rc := NewRenderContext(t, width)
110−
111− titleStyle := t.Dialog.Title
112− inputStyle := t.Dialog.InputPrompt
113− listStyle := t.Dialog.List.Height(listHeight)
114− listContent := listStyle.Render(list)
115−
116− if len(header) > 0 {
117− rc.AddPart(titleStyle.Render(header))
118− }
119− if len(input) > 0 {
120− rc.AddPart(inputStyle.Render(input))
121− }
122− if len(list) > 0 {
123− rc.AddPart(listContent)
124− }
125− if len(help) > 0 {
126− rc.Help = help
127− }
128−
129− return rc.Render()
130−}