From 210bfedd8b0e660c82c8783fbd27971234088f85 Mon Sep 17 00:00:00 2001 From: Mark Mandriota Date: Wed, 12 Jul 2023 12:42:20 +0200 Subject: [PATCH] modify keymap --- README.md | 2 +- go.sum | 12 ++ internal/models/anideck/#model.go# | 170 +++++++++++++++++++++++++++++ internal/models/anideck/keymap.go | 25 +++-- internal/models/anideck/model.go | 26 ++--- 5 files changed, 209 insertions(+), 26 deletions(-) create mode 100644 internal/models/anideck/#model.go# diff --git a/README.md b/README.md index 8961104..35e3823 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # What Anime TUI -An TUI alternative to https://github.com/irevenko/what-anime-cli. +A TUI alternative to https://github.com/irevenko/what-anime-cli. ## Showcase https://github.com/mandriota/what-anime-tui/assets/62650188/ffd9a1b9-bc64-4d8f-90d9-c519c84b32eb diff --git a/go.sum b/go.sum index 95291fa..bc65508 100644 --- a/go.sum +++ b/go.sum @@ -6,10 +6,16 @@ github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5 github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= +github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= +github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= 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/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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 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.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= @@ -31,6 +37,10 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= +github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -41,3 +51,5 @@ golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= diff --git a/internal/models/anideck/#model.go# b/internal/models/anideck/#model.go# new file mode 100644 index 0000000..d9b7558 --- /dev/null +++ b/internal/models/anideck/#model.go# @@ -0,0 +1,170 @@ +qpackage anideck + +import ( + "strings" + + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" + "github.com/charmbracelet/bubbles/paginator" + "github.com/charmbracelet/bubbles/textinput" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/mandriota/what-anime-tui/internal/fetcher" + "github.com/muesli/termenv" +) + +const ( + APIBaseURL = `https://api.trace.moe/search?anilistInfo` + APIByURLParameter = `&url=` +) + +var ( + styleBase = lipgloss.NewStyle(). + Background(lipgloss.ANSIColor(termenv.ANSICyan)). + Foreground(lipgloss.ANSIColor(termenv.ANSIBrightWhite)) + styleCard = lipgloss.NewStyle(). + PaddingLeft(4) +) + +type searchFinishedMsg struct{} + +type Model struct { + fetcher fetcher.Fetcher + response *fetcher.Response + + KeyMap KeyMap + + textInput textinput.Model + paginator paginator.Model + help help.Model + + altScreen bool + searching bool + + styleWidget lipgloss.Style + styleStatePanel lipgloss.Style +} + +func New(path string) Model { + am := Model{ + fetcher: fetcher.New(), + response: new(fetcher.Response), + KeyMap: DefaultKeyMap, + textInput: textinput.New(), + paginator: paginator.New(), + help: help.New(), + styleWidget: styleBase.Copy(). + Border(lipgloss.ThickBorder(), false, true, true, true). + AlignHorizontal(lipgloss.Left). + Height(10). + Bold(true), + styleStatePanel: styleBase.Copy(). + Align(lipgloss.Center), + } + + am.textInput.Placeholder = "Enter File or URL to Search" + am.textInput.SetValue(path) + am.textInput.CharLimit = 1024 + am.textInput.Prompt = "┃" + am.textInput.Focus() + + am.paginator.KeyMap = am.KeyMap.Paginator + am.paginator.Type = paginator.Dots + am.paginator.ActiveDot = styleBase.Copy(). + Bold(true). + Render("•") + am.paginator.InactiveDot = styleBase.Copy(). + Foreground(lipgloss.ANSIColor(termenv.ANSIBlack)). + Render("•") + + return am +} + +func (m Model) Init() tea.Cmd { + return func() tea.Msg { + if path := m.textInput.Value(); path != "" { + return tea.KeyMsg(tea.Key{ + Type: tea.KeyEnter, + }) + } + return nil + } +} + +func (m Model) Update(msg tea.Msg) (_ tea.Model, cmd tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + switch { + case key.Matches(msg, m.KeyMap.Quit): + return m, tea.Quit + case key.Matches(msg, m.KeyMap.Search): + m.textInput.Blur() + path := m.textInput.Value() + if path == "" { + return m, nil + } + m.searching = true + return m, func() tea.Msg { + switch { + case strings.HasPrefix(path, "http"): + m.fetcher.FetchByURL(m.response, APIBaseURL+APIByURLParameter+path) + default: + m.fetcher.FetchByFile(m.response, APIBaseURL, path) + } + + return searchFinishedMsg{} + } + case key.Matches(msg, m.KeyMap.Blur): + m.textInput.Blur() + return m, nil + case key.Matches(msg, m.KeyMap.Help): + m.help.ShowAll = !m.help.ShowAll + case key.Matches(msg, m.KeyMap.AltScreen): + m.altScreen = !m.altScreen + if m.altScreen { + return m, tea.EnterAltScreen + } + return m, tea.ExitAltScreen + } + + if m.textInput.Focused() { + m.textInput, cmd = m.textInput.Update(msg) + return m, cmd + } + + switch { + case key.Matches(msg, m.KeyMap.Focus): + m.textInput.Focus() + case key.Matches(msg, m.KeyMap.Paginator.NextPage, m.KeyMap.Paginator.PrevPage): + m.paginator, cmd = m.paginator.Update(msg) + return m, cmd + } + case tea.WindowSizeMsg: + m.styleWidget.Width(msg.Width - 2) + m.styleStatePanel.Width(msg.Width - 2) + m.help.Width = msg.Width + m.textInput.Width = msg.Width - 2 + return m, nil + case searchFinishedMsg: + m.paginator.SetTotalPages(len(m.response.Result)) + m.searching = false + } + + return m, nil +} + +func (m Model) View() string { + s := m.textInput.View() + "\n" + switch { + case m.searching: + s += m.styleWidget.Render(m.styleStatePanel.Render("⧖ SEARCHING ...")) + case len(m.response.Result) == 0: + s += m.styleWidget.Render(m.styleStatePanel.Render("✘ NO RESULTS ✘")) + default: + s += m.styleWidget.Render(m.styleStatePanel.Render(m.paginator.View()) + "\n" + + styleCard.Render(m.response.Result[m.paginator.Page].View()) + "\n") + } + + return s + "\n" + + m.help.View(m.KeyMap) +} diff --git a/internal/models/anideck/keymap.go b/internal/models/anideck/keymap.go index 2857992..d70a627 100644 --- a/internal/models/anideck/keymap.go +++ b/internal/models/anideck/keymap.go @@ -10,6 +10,7 @@ type KeyMap struct { AltScreen key.Binding Search key.Binding Focus key.Binding + Blur key.Binding Help key.Binding Quit key.Binding } @@ -20,8 +21,8 @@ func (k KeyMap) ShortHelp() []key.Binding { func (k KeyMap) FullHelp() [][]key.Binding { return [][]key.Binding{ - {k.Help, k.Quit, k.Focus, k.Search}, - {k.AltScreen, k.Paginator.PrevPage, k.Paginator.NextPage}, + {k.Help, k.Quit, k.Search, k.AltScreen}, + {k.Focus, k.Blur, k.Paginator.PrevPage, k.Paginator.NextPage}, } } @@ -37,23 +38,27 @@ var DefaultKeyMap = KeyMap{ ), }, AltScreen: key.NewBinding( - key.WithKeys(" "), - key.WithHelp("space", "Toggle AltScreen"), + key.WithKeys("ctrl+s"), + key.WithHelp("ctrl+s", "Toggle AltScreen"), ), Search: key.NewBinding( key.WithKeys("enter"), key.WithHelp("enter", "Search"), ), Focus: key.NewBinding( - key.WithKeys("tab"), - key.WithHelp("tab", "Switch Focus"), + key.WithKeys("j"), + key.WithHelp("j", "Form Focus"), + ), + Blur: key.NewBinding( + key.WithKeys("esc"), + key.WithHelp("esc", "Form Blur"), ), Help: key.NewBinding( - key.WithKeys("?"), - key.WithHelp("?", "Toggle Help"), + key.WithKeys("ctrl+g"), + key.WithHelp("ctrl+g", "Toggle Help"), ), Quit: key.NewBinding( - key.WithKeys("ctrl+c", "ctrl+q", "esc"), - key.WithHelp("ctrl+c/ctrl+q/esc", "Quit"), + key.WithKeys("ctrl+c", "ctrl+q"), + key.WithHelp("ctrl+c/ctrl+q", "Quit"), ), } diff --git a/internal/models/anideck/model.go b/internal/models/anideck/model.go index e967818..252c7de 100644 --- a/internal/models/anideck/model.go +++ b/internal/models/anideck/model.go @@ -114,21 +114,9 @@ func (m Model) Update(msg tea.Msg) (_ tea.Model, cmd tea.Cmd) { return searchFinishedMsg{} } - } - - if m.textInput.Focused() { - switch { - case key.Matches(msg, m.KeyMap.Focus): - m.textInput.Blur() - return m, nil - } - if !key.Matches(msg, m.KeyMap.Search) { - m.textInput, cmd = m.textInput.Update(msg) - return m, cmd - } - } - - switch { + case key.Matches(msg, m.KeyMap.Blur): + m.textInput.Blur() + return m, nil case key.Matches(msg, m.KeyMap.Help): m.help.ShowAll = !m.help.ShowAll case key.Matches(msg, m.KeyMap.AltScreen): @@ -137,6 +125,14 @@ func (m Model) Update(msg tea.Msg) (_ tea.Model, cmd tea.Cmd) { return m, tea.EnterAltScreen } return m, tea.ExitAltScreen + } + + if m.textInput.Focused() { + m.textInput, cmd = m.textInput.Update(msg) + return m, cmd + } + + switch { case key.Matches(msg, m.KeyMap.Focus): m.textInput.Focus() case key.Matches(msg, m.KeyMap.Paginator.NextPage, m.KeyMap.Paginator.PrevPage):