From 69dcba50e6ed4ed353faa2d3fbcf296a234e7b31 Mon Sep 17 00:00:00 2001 From: azimut Date: Wed, 26 Apr 2023 05:55:19 -0300 Subject: [PATCH] tui: add crude "links" altwindow --- go.mod | 4 ++ go.sum | 7 ++++ internal/fourchan/tui.go | 4 +- internal/tui/tui.go | 87 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 96 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 478b6b1..859f340 100644 --- a/go.mod +++ b/go.mod @@ -16,11 +16,13 @@ require ( github.com/moshee/go-4chan-api v0.0.0-20180705201006-117c90c93e9c github.com/pkg/errors v0.8.1 github.com/tidwall/gjson v1.14.4 + mvdan.cc/xurls v1.1.0 ) require ( github.com/alecthomas/chroma v0.10.0 // indirect github.com/andybalholm/cascadia v1.3.1 // indirect + github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52 v1.2.1 // indirect github.com/charmbracelet/lipgloss v0.6.0 // indirect github.com/containerd/console v1.0.3 // indirect @@ -37,8 +39,10 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.14.0 // indirect + github.com/mvdan/xurls v1.1.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/sahilm/fuzzy v0.1.0 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect diff --git a/go.sum b/go.sum index 4a9a7a5..6deb92a 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,7 @@ github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUS github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/aymanbagabas/go-osc52 v1.2.1 h1:q2sWUyDcozPLcLabEMd+a+7Ea2DitxZVN9hTxab9L4E= @@ -50,6 +51,7 @@ github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBD github.com/gomarkdown/markdown v0.0.0-20191123064959-2c17d62f5098/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU= github.com/gomarkdown/markdown v0.0.0-20230322041520-c84983bdbf2a h1:AWZzzFrqyjYlRloN6edwTLTUbKxf5flLXNuTBDm3Ews= github.com/gomarkdown/markdown v0.0.0-20230322041520-c84983bdbf2a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= +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/kyokomi/emoji/v2 v2.2.8 h1:jcofPxjHWEkJtkIbcLHvZhxKgCPl6C7MyjTrD4KDqUE= github.com/kyokomi/emoji/v2 v2.2.8/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE= @@ -89,6 +91,8 @@ github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= github.com/muesli/termenv v0.14.0 h1:8x9NFfOe8lmIWK4pgy3IfVEy47f+ppe3tUqdPZG2Uy0= github.com/muesli/termenv v0.14.0/go.mod h1:kG/pF1E7fh949Xhe156crRUrHNyK221IuGO7Ez60Uc8= +github.com/mvdan/xurls v1.1.0 h1:OpuDelGQ1R1ueQ6sSryzi6P+1RtBpfQHM8fJwlE45ww= +github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= @@ -98,6 +102,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= @@ -165,3 +170,5 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +mvdan.cc/xurls v1.1.0 h1:kj0j2lonKseISJCiq1Tfk+iTv65dDGCl0rTbanXJGGc= +mvdan.cc/xurls v1.1.0/go.mod h1:TNWuhvo+IqbUCmtUIb/3LJSQdrzel8loVpgFm0HikbI= diff --git a/internal/fourchan/tui.go b/internal/fourchan/tui.go index 976671a..f2f3939 100644 --- a/internal/fourchan/tui.go +++ b/internal/fourchan/tui.go @@ -24,7 +24,7 @@ func (m Model) Init() tea.Cmd { } func (m Model) View() string { - return m.render.Viewport.View() + return m.render.View() } func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { @@ -32,6 +32,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.render, cmd = m.render.Update(msg) switch msg := msg.(type) { case tea.WindowSizeMsg: + m.op.thread.width = 200 + m.render.RawContent = fmt.Sprint(m) m.op.thread.width = uint(msg.Width) - rightPadding m.render.Viewport.SetContent(fmt.Sprint(m)) } diff --git a/internal/tui/tui.go b/internal/tui/tui.go index 577ba06..550109d 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -2,18 +2,25 @@ package tui import ( "fmt" + "io" "os" + "sort" "github.com/charmbracelet/bubbles/key" + "github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "mvdan.cc/xurls" ) type Model struct { keymap KeyMap - Viewport viewport.Model - onLinkScreen bool IsReady bool + onLinkScreen bool + Viewport viewport.Model + list list.Model + RawContent string // used to scrape the links } type KeyMap struct { @@ -22,6 +29,7 @@ type KeyMap struct { Next key.Binding Prev key.Binding Quit key.Binding + Links key.Binding } var DefaultViewportKeyMap = viewport.KeyMap{ @@ -61,9 +69,13 @@ var DefaultKeyMap = KeyMap{ key.WithHelp("p", "next comment"), ), Quit: key.NewBinding( - key.WithKeys("q", "esc", "ctrl-c"), + key.WithKeys("q", "ctrl-c"), key.WithHelp("q", "quit"), ), + Links: key.NewBinding( + key.WithKeys("o"), + key.WithHelp("o", "links view"), + ), } func (m Model) Init() tea.Cmd { @@ -71,7 +83,11 @@ func (m Model) Init() tea.Cmd { } func (m Model) View() string { - return m.Viewport.View() + if m.onLinkScreen { + return m.list.View() + } else { + return m.Viewport.View() + } } func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { @@ -79,6 +95,15 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { // TODO: not using useHighPerformanceRenderer case tea.WindowSizeMsg: if !m.IsReady { + m.list = list.New( + getItems(m.RawContent), + itemDelegate{}, + msg.Height, + msg.Height, + // 0, 0, + ) + m.list.SetShowTitle(false) + m.list.Select(1) m.Viewport = viewport.Model{ Width: msg.Width, Height: msg.Height, @@ -86,6 +111,10 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { } m.IsReady = true } else { + m.list.SetItems(getItems(m.RawContent)) + m.list.SetSize(msg.Width, 10) + m.list.SetWidth(msg.Width) + m.list.SetHeight(msg.Height) m.Viewport.Height = msg.Height m.Viewport.Width = msg.Width } @@ -97,11 +126,59 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { m.Viewport.GotoBottom() case key.Matches(msg, DefaultKeyMap.Quit): return m, tea.Quit + case key.Matches(msg, DefaultKeyMap.Links): + items := getItems(m.RawContent) + m.list.SetItems(items) + m.list.ResetSelected() + m.onLinkScreen = !m.onLinkScreen } } var cmd tea.Cmd + var cmds []tea.Cmd m.Viewport, cmd = m.Viewport.Update(msg) - return m, cmd + cmds = append(cmds, cmd) + m.list, cmd = m.list.Update(msg) + cmds = append(cmds, cmd) + return m, tea.Batch(cmds...) +} + +type item string + +type itemDelegate struct{} + +var selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170")) +var itemStyle = lipgloss.NewStyle().PaddingLeft(4) + +func (d itemDelegate) Height() int { return 1 } +func (d itemDelegate) Spacing() int { return 0 } +func (d itemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil } +func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { + i, ok := listItem.(item) + if !ok { + return + } + + str := fmt.Sprintf("%2d. %s", index+1, i) + + if index == m.Index() { + fmt.Fprint(w, selectedItemStyle.Render("> "+str)) + } else { + fmt.Fprint(w, itemStyle.Render(str)) + } +} + +func (i item) FilterValue() string { return "" } + +func getItems(text string) []list.Item { + links := xurls.Strict.FindAllString(text, -1) + sort.Slice(links, func(i, j int) bool { + return links[i] > links[j] + }) + items := make([]list.Item, len(links)) + for i := range links { + items[i] = item(links[i]) + } + return items } func RenderLoop(p *tea.Program) {