Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/catppuccin/go v0.3.0 // indirect
github.com/charmbracelet/colorprofile v0.4.1 // indirect
github.com/charmbracelet/huh v1.0.0 // indirect
github.com/charmbracelet/x/ansi v0.11.6 // indirect
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.9.0 // indirect
github.com/clipperhouse/stringish v0.1.1 // indirect
Expand All @@ -43,6 +46,7 @@ require (
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3v
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
Expand All @@ -22,6 +24,8 @@ github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
github.com/charmbracelet/glamour v1.0.0 h1:AWMLOVFHTsysl4WV8T8QgkQ0s/ZNZo7CiE4WKhk8l08=
github.com/charmbracelet/glamour v1.0.0/go.mod h1:DSdohgOBkMr2ZQNhw4LZxSGpx3SvpeujNoXrQyH2hxo=
github.com/charmbracelet/huh v1.0.0 h1:wOnedH8G4qzJbmhftTqrpppyqHakl/zbbNdXIWJyIxw=
github.com/charmbracelet/huh v1.0.0/go.mod h1:5YVc+SlZ1IhQALxRPpkGwwEKftN/+OlJlnJYlDRFqN4=
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
Expand All @@ -32,6 +36,8 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payR
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI=
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4=
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA=
Expand Down Expand Up @@ -89,6 +95,8 @@ github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byF
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
Expand Down
2 changes: 1 addition & 1 deletion pkg/tui/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1574,7 +1574,7 @@ func (m *Model) updateCommit(msg tea.Msg) (tea.Model, tea.Cmd) {
}
case "ctrl+g":
// AI generate commit message from staged diff
if m.commitDlg.State == views.CommitInputSubject || m.commitDlg.State == views.CommitInputBody {
if m.commitDlg.State == views.CommitForm {
go m.generateAICommitMsg()
return m, nil
}
Expand Down
5 changes: 1 addition & 4 deletions pkg/tui/app_commitai.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,5 @@ func (m *Model) populateCommitFromAI(msg string) {
body = strings.TrimSpace(parts[1])
}

m.commitDlg.Subject.SetValue(subject)
if body != "" {
m.commitDlg.Body.SetValue(body)
}
m.commitDlg.SetAIMessage(subject, body)
}
44 changes: 21 additions & 23 deletions pkg/tui/app_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,43 +22,46 @@ type configResultMsg struct {

// updateConfig handles the config dialog lifecycle.
func (m *Model) updateConfig(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
if km, ok := msg.(tea.KeyMsg); ok {
// Detect bracketed paste (Ctrl+Shift+V) — handle BEFORE normal key processing
// to avoid double-inserting characters.
if msg.Paste && m.configDlg.State == views.ConfigInput {
m.configDlg.HandlePaste(string(msg.Runes))
if km.Paste && m.configDlg.State == views.ConfigInput {
m.configDlg.HandlePaste(string(km.Runes))
return m, nil
}
}

key := msg.String()
handled := m.configDlg.HandleKey(key)
if !handled {
return m, nil
}
updated, cmd := m.configDlg.Update(msg)
m.configDlg = *updated

// On enter in loading state, process selection/value
if key == "enter" && m.configDlg.State == views.ConfigLoading {
if key, ok := msg.(tea.KeyMsg); ok {
// A completed step lands in loading state — process it.
if key.String() == "enter" && m.configDlg.State == views.ConfigLoading {
m.processConfigAction()
return m, nil
return m, cmd
}

// On enter in result state, advance or close
if key == "enter" && m.configDlg.State == views.ConfigResult && m.configDlg.Success {
if key.String() == "enter" && m.configDlg.State == views.ConfigResult && m.configDlg.Success {
// If "saved" result during provider config, advance to next step
if m.configDlg.Result == "saved" && m.configDlg.TargetProvider != "" {
m.advanceProviderConfig()
return m, nil
return m, cmd
}
}

// If dialog was closed, exit mode
if !m.configDlg.Active() {
m.mode = modeNormal
}
return m, cmd
}

return m, nil
// Non-key messages (e.g. resize): exit mode if closed.
if !m.configDlg.Active() {
m.mode = modeNormal
}
return m, cmd
}

// processConfigAction handles Enter at the saving step.
Expand All @@ -80,9 +83,9 @@ func (m *Model) processConfigAction() {

case views.FieldAIProviderMenu:
item := m.configDlg.GetSelectedItem()
// Skip separator line
// Skip separator line (rebuild the step so the form stays live)
if item == "───" {
m.configDlg.State = views.ConfigSelect // stay in select
m.configDlg.RefreshForm()
return
}
if item == "Add custom provider..." {
Expand Down Expand Up @@ -125,12 +128,7 @@ func (m *Model) processConfigAction() {
item := m.configDlg.GetSelectedItem()
if views.IsCustomModelOption(item) {
// Open free-form text input for custom model
m.configDlg.State = views.ConfigInput
m.configDlg.Field = views.FieldAIModel
m.configDlg.Input = ""
m.configDlg.Cursor = 0
m.configDlg.Result = ""
m.configDlg.Success = false
m.configDlg.OpenCustomModelInput(m.configDlg.TargetProvider)
return
}
m.saveProviderModel()
Expand Down
Loading
Loading