on main help sign in
fix(ui): accurately calculate help and main height in UI layout
commit6b8ca6a7c512daea687c8389657db2a421707c31 authorAyman Bagabas <ayman.bagabas@gmail.com> dateTue 28 Oct 2025, 18:34 (1 year ago) parent1c968b8
2 files changed+87−30
internal/ui/model/editor.go+39−2 internal/ui/model/ui.go+48−28
internal/ui/model/editor.go+39−2
@@ -15,6 +15,11 @@ type EditorKeyMap struct {
1515 SendMessage key.Binding
1616 OpenEditor key.Binding
1717 Newline key.Binding
18+
19+ // Attachments key maps
20+ AttachmentDeleteMode key.Binding
21+ Escape key.Binding
22+ DeleteAllAttachments key.Binding
1823}
1924
2025func DefaultEditorKeyMap() EditorKeyMap {
@@ -38,6 +43,18 @@ func DefaultEditorKeyMap() EditorKeyMap {
3843 // to reflect that.
3944 key.WithHelp("ctrl+j", "newline"),
4045 ),
46+ AttachmentDeleteMode: key.NewBinding(
47+ key.WithKeys("ctrl+r"),
48+ key.WithHelp("ctrl+r+{i}", "delete attachment at index i"),
49+ ),
50+ Escape: key.NewBinding(
51+ key.WithKeys("esc", "alt+esc"),
52+ key.WithHelp("esc", "cancel delete mode"),
53+ ),
54+ DeleteAllAttachments: key.NewBinding(
55+ key.WithKeys("r"),
56+ key.WithHelp("ctrl+r+r", "delete all attachments"),
57+ ),
4158 }
4259}
4360
@@ -49,6 +66,8 @@ type EditorModel struct {
4966 keyMap EditorKeyMap
5067 textarea *textarea.Model
5168
69+ attachments []any // TODO: Implement attachments
70+
5271 readyPlaceholder string
5372 workingPlaceholder string
5473}
@@ -110,12 +129,30 @@ func (m *EditorModel) View() string {
110129
111130// ShortHelp returns the short help view for the editor model.
112131func (m *EditorModel) ShortHelp() []key.Binding {
113− return nil
132+ k := m.keyMap
133+ binds := []key.Binding{
134+ k.AddFile,
135+ k.SendMessage,
136+ k.OpenEditor,
137+ k.Newline,
138+ }
139+
140+ if len(m.attachments) > 0 {
141+ binds = append(binds,
142+ k.AttachmentDeleteMode,
143+ k.DeleteAllAttachments,
144+ k.Escape,
145+ )
146+ }
147+
148+ return binds
114149}
115150
116151// FullHelp returns the full help view for the editor model.
117152func (m *EditorModel) FullHelp() [][]key.Binding {
118− return nil
153+ return [][]key.Binding{
154+ m.ShortHelp(),
155+ }
119156}
120157
121158// Cursor returns the relative cursor position of the editor.
internal/ui/model/ui.go+48−28
@@ -59,6 +59,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
5959 case tea.WindowSizeMsg:
6060 m.updateLayout(msg.Width, msg.Height)
6161 m.editor.SetSize(m.layout.editor.Dx(), m.layout.editor.Dy())
62+ m.help.Width = m.layout.help.Dx()
6263 case tea.KeyPressMsg:
6364 if m.dialog.HasDialogs() {
6465 m.updateDialogs(msg, &cmds)
@@ -75,6 +76,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
7576 case key.Matches(msg, m.keyMap.Help):
7677 m.showFullHelp = !m.showFullHelp
7778 m.help.ShowAll = m.showFullHelp
79+ m.updateLayout(m.layout.area.Dx(), m.layout.area.Dy())
7880 case key.Matches(msg, m.keyMap.Quit):
7981 if !m.dialog.ContainsDialog(dialog.QuitDialogID) {
8082 m.dialog.AddDialog(dialog.NewQuit(m.com))
@@ -130,27 +132,28 @@ func (m *UI) View() tea.View {
130132 v.Cursor = cur
131133 }
132134
133− layers = append(layers, lipgloss.NewLayer(
134− lipgloss.NewStyle().Width(chatRect.Dx()).
135− Height(chatRect.Dy()).
136− Background(lipgloss.ANSIColor(rand.Intn(256))).
137− Render(" Main View "),
138− ).X(chatRect.Min.X).Y(chatRect.Min.Y),
139− lipgloss.NewLayer(
140− lipgloss.NewStyle().Width(sideRect.Dx()).
141− Height(sideRect.Dy()).
142− Background(lipgloss.ANSIColor(rand.Intn(256))).
143− Render(" Side View "),
144− ).X(sideRect.Min.X).Y(sideRect.Min.Y),
145− lipgloss.NewLayer(m.editor.View()).
146− X(editRect.Min.X).Y(editRect.Min.Y),
147− lipgloss.NewLayer(
148− lipgloss.NewStyle().Width(helpRect.Dx()).
149− Height(helpRect.Dy()).
150− Background(lipgloss.ANSIColor(rand.Intn(256))).
151− Render(m.help.View(helpKeyMap)),
152− ).X(helpRect.Min.X).Y(helpRect.Min.Y),
153− )
135+ mainLayer := lipgloss.NewLayer("").X(area.Min.X).Y(area.Min.Y).
136+ Width(area.Dx()).Height(area.Dy()).
137+ AddLayers(
138+ lipgloss.NewLayer(
139+ lipgloss.NewStyle().Width(chatRect.Dx()).
140+ Height(chatRect.Dy()).
141+ Background(lipgloss.ANSIColor(rand.Intn(256))).
142+ Render(" Main View "),
143+ ).X(chatRect.Min.X).Y(chatRect.Min.Y),
144+ lipgloss.NewLayer(
145+ lipgloss.NewStyle().Width(sideRect.Dx()).
146+ Height(sideRect.Dy()).
147+ Background(lipgloss.ANSIColor(rand.Intn(256))).
148+ Render(" Side View "),
149+ ).X(sideRect.Min.X).Y(sideRect.Min.Y),
150+ lipgloss.NewLayer(m.editor.View()).
151+ X(editRect.Min.X).Y(editRect.Min.Y),
152+ lipgloss.NewLayer(m.help.View(helpKeyMap)).
153+ X(helpRect.Min.X).Y(helpRect.Min.Y),
154+ )
155+
156+ layers = append(layers, mainLayer)
154157
155158 v.Layer = lipgloss.NewCanvas(layers...)
156159
@@ -209,23 +212,37 @@ func (m *UI) updateEditor(msg tea.KeyPressMsg, cmds *[]tea.Cmd) {
209212// height given in cells.
210213func (m *UI) updateLayout(w, h int) {
211214 // The screen area we're working with
212− area := image.Rect(1, 1, w-1, h-1) // -1 for margins
215+ area := image.Rect(0, 0, w, h)
213216 helpKeyMap := m.focusedKeyMap()
214217 helpHeight := 1
218+ if m.dialog.HasDialogs() && len(m.dialog.FullHelp()) > 0 && len(m.dialog.ShortHelp()) > 0 {
219+ helpKeyMap = m.dialog
220+ }
215221 if m.showFullHelp {
216− helpHeight = max(1, len(helpKeyMap.FullHelp()))
222+ for _, row := range helpKeyMap.FullHelp() {
223+ helpHeight = max(helpHeight, len(row))
224+ }
217225 }
218226
219− chatRect, sideRect := uv.SplitHorizontal(area, uv.Fixed(area.Dx()-40))
220− chatRect, editRect := uv.SplitVertical(chatRect, uv.Fixed(area.Dy()-5-helpHeight))
221− // Add 1 line margin bottom of mainRect
227+ // Add app margins
228+ mainRect := area
229+ mainRect.Min.X += 1
230+ mainRect.Min.Y += 1
231+ mainRect.Max.X -= 1
232+ mainRect.Max.Y -= 1
233+
234+ mainRect, helpRect := uv.SplitVertical(mainRect, uv.Fixed(mainRect.Dy()-helpHeight))
235+ chatRect, sideRect := uv.SplitHorizontal(mainRect, uv.Fixed(mainRect.Dx()-40))
236+ chatRect, editRect := uv.SplitVertical(chatRect, uv.Fixed(mainRect.Dy()-5))
237+
238+ // Add 1 line margin bottom of chatRect
222239 chatRect, _ = uv.SplitVertical(chatRect, uv.Fixed(chatRect.Dy()-1))
223− editRect, helpRect := uv.SplitVertical(editRect, uv.Fixed(5))
224− // Add 1 line margin bottom of footRect
240+ // Add 1 line margin bottom of editRect
225241 editRect, _ = uv.SplitVertical(editRect, uv.Fixed(editRect.Dy()-1))
226242
227243 m.layout = layout{
228244 area: area,
245+ main: mainRect,
229246 chat: chatRect,
230247 editor: editRect,
231248 sidebar: sideRect,
@@ -238,6 +255,9 @@ type layout struct {
238255 // area is the overall available area.
239256 area uv.Rectangle
240257
258+ // main is the main area excluding help.
259+ main uv.Rectangle
260+
241261 // chat is the area for the chat pane.
242262 chat uv.Rectangle
243263