diff --git a/internal/ui/app.go b/internal/ui/app.go index 7dc50fc..af9476a 100644 --- a/internal/ui/app.go +++ b/internal/ui/app.go @@ -21,9 +21,10 @@ import ( // editorFinishedMsg is sent after the external $EDITOR exits. type editorFinishedMsg struct { - ticketID string + ticketID string // empty when create==true path string err error + create bool // true: create ticket from edited file } // Mode is the active UI mode. @@ -374,6 +375,20 @@ func (m Model) updateInput(msg tea.KeyMsg) (tea.Model, tea.Cmd) { } return m, nil + case key.Matches(msg, m.keys.EditInEditor): + if m.mode != ModeCreate || m.creatingProject { + break + } + m.clearStatus() + cmd, err := m.openEditorForNewTicket() + if err != nil { + m.setError(err) + return m, nil + } + m.input.Blur() + m.mode = ModeList + return m, cmd + case key.Matches(msg, m.keys.Enter): value := strings.TrimSpace(m.input.Value()) if m.mode == ModeCreate { @@ -397,7 +412,7 @@ func (m Model) updateInput(msg tea.KeyMsg) (tea.Model, tea.Cmd) { m.setStatus(fmt.Sprintf("created project %s", id)) return m, nil } - if err := m.createTicket(value); err != nil { + if err := m.createTicket(value, ""); err != nil { m.setError(err) return m, nil } @@ -474,6 +489,21 @@ func (m Model) openEditorForSelected() (tea.Cmd, error) { }), nil } +func (m Model) openEditorForNewTicket() (tea.Cmd, error) { + title := strings.TrimSpace(m.input.Value()) + if title == "" { + title = "New ticket" + } + path, err := ticket.WriteEditFile(ticket.Ticket{Title: title}) + if err != nil { + return nil, err + } + c := ticket.EditorCmd(path) + return tea.ExecProcess(c, func(err error) tea.Msg { + return editorFinishedMsg{path: path, err: err, create: true} + }), nil +} + func (m Model) handleEditorFinished(msg editorFinishedMsg) (tea.Model, tea.Cmd) { defer func() { _ = os.Remove(msg.path) }() @@ -493,6 +523,16 @@ func (m Model) handleEditorFinished(msg editorFinishedMsg) (tea.Model, tea.Cmd) return m, nil } + if msg.create { + if err := m.createTicket(title, description); err != nil { + m.setError(err) + return m, nil + } + m.mode = ModeList + m.setStatus("created") + return m, nil + } + before, err := m.store.Get(msg.ticketID) if err != nil { m.setError(err) @@ -521,13 +561,14 @@ func (m Model) handleEditorFinished(msg editorFinishedMsg) (tea.Model, tea.Cmd) return m, nil } -func (m *Model) createTicket(title string) error { +func (m *Model) createTicket(title, description string) error { cmd := &command.CreateTicket{ Store: m.store, Ticket: ticket.Ticket{ - Title: title, - ProjectID: m.cfg.ActiveProject, - Status: m.boardList.DefaultStatus(), + Title: title, + Description: description, + ProjectID: m.cfg.ActiveProject, + Status: m.boardList.DefaultStatus(), }, } if err := m.exec.Do(cmd); err != nil { diff --git a/internal/ui/helpbar.go b/internal/ui/helpbar.go index 631ff32..9174f96 100644 --- a/internal/ui/helpbar.go +++ b/internal/ui/helpbar.go @@ -14,10 +14,14 @@ type helpEntry struct { func (m Model) helpEntries() []helpEntry { switch m.mode { case ModeCreate: - return []helpEntry{ + entries := []helpEntry{ {"enter", "save"}, {"esc", "cancel"}, } + if !m.creatingProject { + entries = append([]helpEntry{{"ctrl+e", "editor"}}, entries...) + } + return entries case ModeSearch: return []helpEntry{ {"enter", "apply"}, diff --git a/internal/ui/keys.go b/internal/ui/keys.go index 1f43be9..6f47eb0 100644 --- a/internal/ui/keys.go +++ b/internal/ui/keys.go @@ -11,15 +11,16 @@ type KeyMap struct { Project key.Binding Board key.Binding Status key.Binding - Edit key.Binding - Delete key.Binding // x — confirm first - DeleteForce key.Binding // X — immediate - ConfirmYes key.Binding - Undo key.Binding - Redo key.Binding - Quit key.Binding - Enter key.Binding - Escape key.Binding + Edit key.Binding + EditInEditor key.Binding // ctrl+e — open $EDITOR while creating + Delete key.Binding // x — confirm first + DeleteForce key.Binding // X — immediate + ConfirmYes key.Binding + Undo key.Binding + Redo key.Binding + Quit key.Binding + Enter key.Binding + Escape key.Binding } // DefaultKeyMap returns the default key bindings. @@ -57,6 +58,10 @@ func DefaultKeyMap() KeyMap { key.WithKeys("e"), key.WithHelp("e", "edit"), ), + EditInEditor: key.NewBinding( + key.WithKeys("ctrl+e"), + key.WithHelp("ctrl+e", "editor"), + ), Delete: key.NewBinding( key.WithKeys("x"), key.WithHelp("x", "delete"),