diff --git a/go.mod b/go.mod index a13c5ad..86744f4 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,17 @@ go 1.19 require ( github.com/charmbracelet/bubbles v0.16.1 + github.com/charmbracelet/bubbletea v0.24.3-0.20230609163353-b80eb8303bba + github.com/charmbracelet/lipgloss v0.7.1 github.com/spf13/cobra v1.7.0 + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 ) require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/bubbletea v0.24.3-0.20230609163353-b80eb8303bba // indirect - github.com/charmbracelet/lipgloss v0.7.1 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.18 // indirect @@ -23,6 +25,7 @@ require ( github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/sahilm/fuzzy v0.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.6.0 // indirect diff --git a/go.sum b/go.sum index ac37cc1..4755189 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,6 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY= github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= -github.com/charmbracelet/bubbletea v0.24.1 h1:LpdYfnu+Qc6XtvMz6d/6rRY71yttHTP5HtrjMgWvixc= -github.com/charmbracelet/bubbletea v0.24.1/go.mod h1:rK3g/2+T8vOSEkNHvtq40umJpeVYDn6bLaqbgzhL/hg= github.com/charmbracelet/bubbletea v0.24.3-0.20230609163353-b80eb8303bba h1:hWjlqzPiG3dWN5KZUAKKnqz/R8BlYqX1Aw+nk7riQss= github.com/charmbracelet/bubbletea v0.24.3-0.20230609163353-b80eb8303bba/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= @@ -13,8 +11,11 @@ github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNW github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= @@ -36,10 +37,14 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= +github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/keymap.go b/keymap.go index 931a86f..8ba4fe7 100644 --- a/keymap.go +++ b/keymap.go @@ -15,11 +15,11 @@ type KeyMap struct { func DefaultKeybinds() KeyMap { return KeyMap{ NextInput: key.NewBinding( - key.WithKeys("tab", "ctrl+n"), + key.WithKeys("tab"), key.WithHelp("tab", "next"), ), PrevInput: key.NewBinding( - key.WithKeys("shift+tab", "ctrl+p"), + key.WithKeys("shift+tab"), ), Send: key.NewBinding( key.WithKeys("ctrl+d", "enter"), diff --git a/model.go b/model.go index f55c14a..c9752d9 100644 --- a/model.go +++ b/model.go @@ -8,9 +8,11 @@ import ( "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" + "github.com/charmbracelet/bubbles/spinner" "github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" + "golang.org/x/exp/constraints" ) type State int @@ -44,10 +46,11 @@ type Model struct { Attachments list.Model // filepicker is used to pick file attachments. - filepicker filepicker.Model - help help.Model - keymap KeyMap - quitting bool + filepicker filepicker.Model + loadingSpinner spinner.Model + help help.Model + keymap KeyMap + quitting bool } func NewModel() Model { @@ -76,7 +79,7 @@ func NewModel() Model { subject.Placeholder = "Hello!" body := textarea.New() - body.Placeholder = "# Hi" + body.Placeholder = "# Email" body.ShowLineNumbers = false body.FocusedStyle.CursorLine = activeTextStyle body.FocusedStyle.Prompt = activeLabelStyle @@ -103,16 +106,20 @@ func NewModel() Model { picker := filepicker.New() picker.CurrentDirectory, _ = os.UserHomeDir() + loadingSpinner := spinner.NewModel() + loadingSpinner.Spinner = spinner.Dot + return Model{ - state: editingFrom, - From: from, - To: to, - Subject: subject, - Body: body, - Attachments: attachments, - filepicker: picker, - help: help.New(), - keymap: DefaultKeybinds(), + state: editingFrom, + From: from, + To: to, + Subject: subject, + Body: body, + Attachments: attachments, + filepicker: picker, + help: help.New(), + keymap: DefaultKeybinds(), + loadingSpinner: loadingSpinner, } } @@ -168,6 +175,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.focusActiveInput() case key.Matches(msg, m.keymap.Send): + m.state = sendingEmail + return m, tea.Batch( + m.loadingSpinner.Tick, + ) case key.Matches(msg, m.keymap.Attach): m.state = pickingFile case key.Matches(msg, m.keymap.Unattach): @@ -194,19 +205,22 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.filepicker, cmd = m.filepicker.Update(msg) cmds = append(cmds, cmd) - if m.state == pickingFile { + switch m.state { + case pickingFile: if didSelect, path := m.filepicker.DidSelectFile(msg); didSelect { m.Attachments.InsertItem(0, attachment(path)) m.Attachments.SetHeight(len(m.Attachments.Items()) + 2) m.state = editingAttachments m.updateKeymap() } - } - - if m.state == editingAttachments { + case editingAttachments: m.Attachments, cmd = m.Attachments.Update(msg) cmds = append(cmds, cmd) + case sendingEmail: + m.loadingSpinner, cmd = m.loadingSpinner.Update(msg) + cmds = append(cmds, cmd) } + m.help, cmd = m.help.Update(msg) cmds = append(cmds, cmd) @@ -259,9 +273,12 @@ func (m Model) View() string { return "" } - if m.state == pickingFile { + switch m.state { + case pickingFile: return "\n" + activeLabelStyle.Render("Attachments") + "\n\n" + m.filepicker.View() + case sendingEmail: + return "\n " + m.loadingSpinner.View() + "Sending email" } var s strings.Builder @@ -278,5 +295,12 @@ func (m Model) View() string { s.WriteString("\n") s.WriteString(m.help.View(m.keymap)) - return s.String() + return paddedStyle.Render(s.String()) +} + +func max[T constraints.Ordered](a, b T) T { + if a > b { + return a + } + return b } diff --git a/style.go b/style.go index c924afc..c0edaff 100644 --- a/style.go +++ b/style.go @@ -17,4 +17,6 @@ var ( placeholderStyle = lipgloss.NewStyle().Foreground(darkGrayColor) cursorStyle = lipgloss.NewStyle().Foreground(whiteColor) + + paddedStyle = lipgloss.NewStyle().Padding(1) )