delete.go

  1package sessions
  2
  3import (
  4	"github.com/charmbracelet/bubbles/v2/key"
  5	tea "github.com/charmbracelet/bubbletea/v2"
  6	"github.com/charmbracelet/crush/internal/session"
  7	"github.com/charmbracelet/crush/internal/tui/components/chat"
  8	"github.com/charmbracelet/crush/internal/tui/components/dialogs"
  9	"github.com/charmbracelet/crush/internal/tui/styles"
 10	"github.com/charmbracelet/crush/internal/tui/util"
 11	"github.com/charmbracelet/lipgloss/v2"
 12)
 13
 14const DeleteSessionDialogID dialogs.DialogID = "delete-session"
 15
 16type DeleteSessionDialog interface {
 17	dialogs.DialogModel
 18}
 19
 20type deleteSessionDialogCmp struct {
 21	wWidth     int
 22	wHeight    int
 23	session    session.Session
 24	selectedNo bool
 25	keymap     DeleteKeyMap
 26}
 27
 28type DeleteKeyMap struct {
 29	LeftRight,
 30	EnterSpace,
 31	Yes,
 32	No,
 33	Tab,
 34	Close key.Binding
 35}
 36
 37func DefaultDeleteKeymap() DeleteKeyMap {
 38	return DeleteKeyMap{
 39		LeftRight: key.NewBinding(
 40			key.WithKeys("left", "right"),
 41			key.WithHelp("←/→", "switch options"),
 42		),
 43		EnterSpace: key.NewBinding(
 44			key.WithKeys("enter", " "),
 45			key.WithHelp("enter/space", "confirm"),
 46		),
 47		Yes: key.NewBinding(
 48			key.WithKeys("y", "Y"),
 49			key.WithHelp("y/Y", "yes"),
 50		),
 51		No: key.NewBinding(
 52			key.WithKeys("n", "N"),
 53			key.WithHelp("n/N", "no"),
 54		),
 55		Tab: key.NewBinding(
 56			key.WithKeys("tab"),
 57			key.WithHelp("tab", "switch options"),
 58		),
 59		Close: key.NewBinding(
 60			key.WithKeys("esc"),
 61			key.WithHelp("esc", "cancel"),
 62		),
 63	}
 64}
 65
 66func NewDeleteSessionDialog(session session.Session) DeleteSessionDialog {
 67	return &deleteSessionDialogCmp{
 68		session:    session,
 69		selectedNo: true,
 70		keymap:     DefaultDeleteKeymap(),
 71	}
 72}
 73
 74func (d *deleteSessionDialogCmp) Init() tea.Cmd {
 75	return nil
 76}
 77
 78func (d *deleteSessionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 79	switch msg := msg.(type) {
 80	case tea.WindowSizeMsg:
 81		d.wWidth = msg.Width
 82		d.wHeight = msg.Height
 83	case tea.KeyPressMsg:
 84		switch {
 85		case key.Matches(msg, d.keymap.LeftRight, d.keymap.Tab):
 86			d.selectedNo = !d.selectedNo
 87			return d, nil
 88		case key.Matches(msg, d.keymap.EnterSpace):
 89			if !d.selectedNo {
 90				return d, tea.Sequence(
 91					util.CmdHandler(dialogs.CloseDialogMsg{}),
 92					util.CmdHandler(chat.SessionDeletedMsg{Session: d.session}),
 93				)
 94			}
 95			return d, util.CmdHandler(dialogs.CloseDialogMsg{})
 96		case key.Matches(msg, d.keymap.Yes):
 97			return d, tea.Sequence(
 98				util.CmdHandler(dialogs.CloseDialogMsg{}),
 99				util.CmdHandler(chat.SessionDeletedMsg{Session: d.session}),
100			)
101		case key.Matches(msg, d.keymap.No, d.keymap.Close):
102			return d, util.CmdHandler(dialogs.CloseDialogMsg{})
103		}
104	}
105	return d, nil
106}
107
108func (d *deleteSessionDialogCmp) View() string {
109	t := styles.CurrentTheme()
110	baseStyle := t.S().Base
111	yesStyle := t.S().Text
112	noStyle := yesStyle
113
114	if d.selectedNo {
115		noStyle = noStyle.Foreground(t.White).Background(t.Secondary)
116		yesStyle = yesStyle.Background(t.BgSubtle)
117	} else {
118		yesStyle = yesStyle.Foreground(t.White).Background(t.Secondary)
119		noStyle = noStyle.Background(t.BgSubtle)
120	}
121
122	question := "Delete session \"" + d.session.Title + "\"?"
123	const horizontalPadding = 3
124	yesButton := yesStyle.Padding(0, horizontalPadding).Render("Delete")
125	noButton := noStyle.Padding(0, horizontalPadding).Render("Cancel")
126
127	buttons := baseStyle.Width(lipgloss.Width(question)).Align(lipgloss.Right).Render(
128		lipgloss.JoinHorizontal(lipgloss.Center, yesButton, "  ", noButton),
129	)
130
131	content := baseStyle.Render(
132		lipgloss.JoinVertical(
133			lipgloss.Center,
134			question,
135			"",
136			buttons,
137		),
138	)
139
140	deleteDialogStyle := baseStyle.
141		Padding(1, 2).
142		Border(lipgloss.RoundedBorder()).
143		BorderForeground(t.BorderFocus)
144
145	return deleteDialogStyle.Render(content)
146}
147
148func (d *deleteSessionDialogCmp) Position() (int, int) {
149	question := "Delete session \"" + d.session.Title + "\"?"
150	row := d.wHeight / 2
151	row -= 7 / 2
152	col := d.wWidth / 2
153	col -= (lipgloss.Width(question) + 4) / 2
154
155	return row, col
156}
157
158func (d *deleteSessionDialogCmp) ID() dialogs.DialogID {
159	return DeleteSessionDialogID
160}