@@ -4,8 +4,10 @@ import (
6 tea "charm.land/bubbletea/v2"
6 tea "charm.land/bubbletea/v2"
7+ "charm.land/lipgloss/v2"
7 "github.com/charmbracelet/crush/internal/ui/common"
8 "github.com/charmbracelet/crush/internal/ui/common"
8 "github.com/charmbracelet/crush/internal/ui/styles"
9 "github.com/charmbracelet/crush/internal/ui/styles"
10+ "github.com/charmbracelet/x/ansi"
11// InputCursor adjusts the cursor position for an input field within a dialog.
13// InputCursor adjusts the cursor position for an input field within a dialog.
@@ -43,9 +45,15 @@ type RenderContext struct {
43 // Width is the total width of the dialog including any margins, borders,
45 // Width is the total width of the dialog including any margins, borders,
44 // and paddings.
46 // and paddings.
48+ // Gap is the gap between content parts. Zero means no gap.
46 // Title is the title of the dialog. This will be styled using the default
50 // Title is the title of the dialog. This will be styled using the default
47 // dialog title style and prepended to the content parts slice.
51 // dialog title style and prepended to the content parts slice.
48 Title string
52 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
49 // Parts are the rendered parts of the dialog.
57 // Parts are the rendered parts of the dialog.
50 Parts []string
58 Parts []string
51 // Help is the help view content. This will be appended to the content parts
59 // Help is the help view content. This will be appended to the content parts
@@ -76,55 +84,47 @@ func (rc *RenderContext) Render() string {
77 parts := []string{}
85 parts := []string{}
78 if len(rc.Title) > 0 {
86 if len(rc.Title) > 0 {
87+ var titleInfoWidth int
88+ if len(rc.TitleInfo) > 0 {
89+ titleInfoWidth = lipgloss.Width(rc.TitleInfo)
79 title := common.DialogTitle(rc.Styles, rc.Title,
91 title := common.DialogTitle(rc.Styles, rc.Title,
80 max(0, rc.Width-dialogStyle.GetHorizontalFrameSize()-
92 max(0, rc.Width-dialogStyle.GetHorizontalFrameSize()-
81− titleStyle.GetHorizontalFrameSize()))
93+ titleStyle.GetHorizontalFrameSize()-
95+ if len(rc.TitleInfo) > 0 {
96+ title += rc.TitleInfo
82− parts = append(parts, titleStyle.Render(title), "")
98+ parts = append(parts, titleStyle.Render(title))
100+ parts = append(parts, make([]string, rc.Gap)...)
105+ parts = append(parts, rc.Parts...)
85− for i, p := range rc.Parts {
107+ for i, p := range rc.Parts {
86− if len(p) > 0 {
108+ if len(p) > 0 {
87− parts = append(parts, p)
109+ parts = append(parts, p)
89− if i < len(rc.Parts)-1 {
111+ if i < len(rc.Parts)-1 {
90− parts = append(parts, "")
112+ parts = append(parts, make([]string, rc.Gap)...)
94 if len(rc.Help) > 0 {
117 if len(rc.Help) > 0 {
95− parts = append(parts, "")
119+ parts = append(parts, make([]string, rc.Gap)...)
96 helpStyle := rc.Styles.Dialog.HelpView
121 helpStyle := rc.Styles.Dialog.HelpView
97 helpStyle = helpStyle.Width(rc.Width - dialogStyle.GetHorizontalFrameSize())
122 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)
101 content := strings.Join(parts, "\n")
127 content := strings.Join(parts, "\n")
103 return dialogStyle.Render(content)
129 return dialogStyle.Render(content)
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)
111− titleStyle := t.Dialog.Title
112− inputStyle := t.Dialog.InputPrompt
113− listStyle := t.Dialog.List.Height(listHeight)
114− listContent := listStyle.Render(list)
116− if len(header) > 0 {
117− rc.AddPart(titleStyle.Render(header))
120− rc.AddPart(inputStyle.Render(input))
123− rc.AddPart(listContent)