diff --git a/internal/ui/model/editor.go b/internal/ui/model/editor.go index 1066de5f96a4c3105329d35e6434a768d7001ab0..b4c8644c7d8264216305c99b4f8df066e813b400 100644 --- a/internal/ui/model/editor.go +++ b/internal/ui/model/editor.go @@ -15,6 +15,11 @@ type EditorKeyMap struct { SendMessage key.Binding OpenEditor key.Binding Newline key.Binding + + // Attachments key maps + AttachmentDeleteMode key.Binding + Escape key.Binding + DeleteAllAttachments key.Binding } func DefaultEditorKeyMap() EditorKeyMap { @@ -38,6 +43,18 @@ func DefaultEditorKeyMap() EditorKeyMap { // to reflect that. key.WithHelp("ctrl+j", "newline"), ), + AttachmentDeleteMode: key.NewBinding( + key.WithKeys("ctrl+r"), + key.WithHelp("ctrl+r+{i}", "delete attachment at index i"), + ), + Escape: key.NewBinding( + key.WithKeys("esc", "alt+esc"), + key.WithHelp("esc", "cancel delete mode"), + ), + DeleteAllAttachments: key.NewBinding( + key.WithKeys("r"), + key.WithHelp("ctrl+r+r", "delete all attachments"), + ), } } @@ -49,6 +66,8 @@ type EditorModel struct { keyMap EditorKeyMap textarea *textarea.Model + attachments []any // TODO: Implement attachments + readyPlaceholder string workingPlaceholder string } @@ -110,12 +129,30 @@ func (m *EditorModel) View() string { // ShortHelp returns the short help view for the editor model. func (m *EditorModel) ShortHelp() []key.Binding { - return nil + k := m.keyMap + binds := []key.Binding{ + k.AddFile, + k.SendMessage, + k.OpenEditor, + k.Newline, + } + + if len(m.attachments) > 0 { + binds = append(binds, + k.AttachmentDeleteMode, + k.DeleteAllAttachments, + k.Escape, + ) + } + + return binds } // FullHelp returns the full help view for the editor model. func (m *EditorModel) FullHelp() [][]key.Binding { - return nil + return [][]key.Binding{ + m.ShortHelp(), + } } // Cursor returns the relative cursor position of the editor. diff --git a/internal/ui/model/ui.go b/internal/ui/model/ui.go index 20f1847cdf917eef2c9e1959dde067b3224da8cc..f157efce1df8091a9b647bef6172ba47e4095098 100644 --- a/internal/ui/model/ui.go +++ b/internal/ui/model/ui.go @@ -59,6 +59,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.WindowSizeMsg: m.updateLayout(msg.Width, msg.Height) m.editor.SetSize(m.layout.editor.Dx(), m.layout.editor.Dy()) + m.help.Width = m.layout.help.Dx() case tea.KeyPressMsg: if m.dialog.HasDialogs() { m.updateDialogs(msg, &cmds) @@ -75,6 +76,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case key.Matches(msg, m.keyMap.Help): m.showFullHelp = !m.showFullHelp m.help.ShowAll = m.showFullHelp + m.updateLayout(m.layout.area.Dx(), m.layout.area.Dy()) case key.Matches(msg, m.keyMap.Quit): if !m.dialog.ContainsDialog(dialog.QuitDialogID) { m.dialog.AddDialog(dialog.NewQuit(m.com)) @@ -130,27 +132,28 @@ func (m *UI) View() tea.View { v.Cursor = cur } - layers = append(layers, lipgloss.NewLayer( - lipgloss.NewStyle().Width(chatRect.Dx()). - Height(chatRect.Dy()). - Background(lipgloss.ANSIColor(rand.Intn(256))). - Render(" Main View "), - ).X(chatRect.Min.X).Y(chatRect.Min.Y), - lipgloss.NewLayer( - lipgloss.NewStyle().Width(sideRect.Dx()). - Height(sideRect.Dy()). - Background(lipgloss.ANSIColor(rand.Intn(256))). - Render(" Side View "), - ).X(sideRect.Min.X).Y(sideRect.Min.Y), - lipgloss.NewLayer(m.editor.View()). - X(editRect.Min.X).Y(editRect.Min.Y), - lipgloss.NewLayer( - lipgloss.NewStyle().Width(helpRect.Dx()). - Height(helpRect.Dy()). - Background(lipgloss.ANSIColor(rand.Intn(256))). - Render(m.help.View(helpKeyMap)), - ).X(helpRect.Min.X).Y(helpRect.Min.Y), - ) + mainLayer := lipgloss.NewLayer("").X(area.Min.X).Y(area.Min.Y). + Width(area.Dx()).Height(area.Dy()). + AddLayers( + lipgloss.NewLayer( + lipgloss.NewStyle().Width(chatRect.Dx()). + Height(chatRect.Dy()). + Background(lipgloss.ANSIColor(rand.Intn(256))). + Render(" Main View "), + ).X(chatRect.Min.X).Y(chatRect.Min.Y), + lipgloss.NewLayer( + lipgloss.NewStyle().Width(sideRect.Dx()). + Height(sideRect.Dy()). + Background(lipgloss.ANSIColor(rand.Intn(256))). + Render(" Side View "), + ).X(sideRect.Min.X).Y(sideRect.Min.Y), + lipgloss.NewLayer(m.editor.View()). + X(editRect.Min.X).Y(editRect.Min.Y), + lipgloss.NewLayer(m.help.View(helpKeyMap)). + X(helpRect.Min.X).Y(helpRect.Min.Y), + ) + + layers = append(layers, mainLayer) v.Layer = lipgloss.NewCanvas(layers...) @@ -209,23 +212,37 @@ func (m *UI) updateEditor(msg tea.KeyPressMsg, cmds *[]tea.Cmd) { // height given in cells. func (m *UI) updateLayout(w, h int) { // The screen area we're working with - area := image.Rect(1, 1, w-1, h-1) // -1 for margins + area := image.Rect(0, 0, w, h) helpKeyMap := m.focusedKeyMap() helpHeight := 1 + if m.dialog.HasDialogs() && len(m.dialog.FullHelp()) > 0 && len(m.dialog.ShortHelp()) > 0 { + helpKeyMap = m.dialog + } if m.showFullHelp { - helpHeight = max(1, len(helpKeyMap.FullHelp())) + for _, row := range helpKeyMap.FullHelp() { + helpHeight = max(helpHeight, len(row)) + } } - chatRect, sideRect := uv.SplitHorizontal(area, uv.Fixed(area.Dx()-40)) - chatRect, editRect := uv.SplitVertical(chatRect, uv.Fixed(area.Dy()-5-helpHeight)) - // Add 1 line margin bottom of mainRect + // Add app margins + mainRect := area + mainRect.Min.X += 1 + mainRect.Min.Y += 1 + mainRect.Max.X -= 1 + mainRect.Max.Y -= 1 + + mainRect, helpRect := uv.SplitVertical(mainRect, uv.Fixed(mainRect.Dy()-helpHeight)) + chatRect, sideRect := uv.SplitHorizontal(mainRect, uv.Fixed(mainRect.Dx()-40)) + chatRect, editRect := uv.SplitVertical(chatRect, uv.Fixed(mainRect.Dy()-5)) + + // Add 1 line margin bottom of chatRect chatRect, _ = uv.SplitVertical(chatRect, uv.Fixed(chatRect.Dy()-1)) - editRect, helpRect := uv.SplitVertical(editRect, uv.Fixed(5)) - // Add 1 line margin bottom of footRect + // Add 1 line margin bottom of editRect editRect, _ = uv.SplitVertical(editRect, uv.Fixed(editRect.Dy()-1)) m.layout = layout{ area: area, + main: mainRect, chat: chatRect, editor: editRect, sidebar: sideRect, @@ -238,6 +255,9 @@ type layout struct { // area is the overall available area. area uv.Rectangle + // main is the main area excluding help. + main uv.Rectangle + // chat is the area for the chat pane. chat uv.Rectangle