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}