rss email digests over ssh because you're a cool kid herald.dunkirk.sh
go rss rss-reader ssh charm
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add spinner

+49 -22
+4 -4
go.mod
··· 9 9 github.com/charmbracelet/log v0.4.0 10 10 github.com/charmbracelet/ssh v0.0.0-20240725163421-eb71b85b27aa 11 11 github.com/charmbracelet/wish v1.4.3 12 + github.com/joho/godotenv v1.5.1 12 13 github.com/mattn/go-sqlite3 v1.14.24 13 14 github.com/mmcdole/gofeed v1.3.0 15 + github.com/pkg/sftp v1.13.10 14 16 github.com/spf13/cobra v1.9.1 15 17 golang.org/x/crypto v0.41.0 16 18 golang.org/x/sync v0.17.0 ··· 23 25 github.com/andybalholm/cascadia v1.3.1 // indirect 24 26 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect 25 27 github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 26 - github.com/charmbracelet/bubbletea v1.0.0 // indirect 28 + github.com/charmbracelet/bubbletea v1.1.2 // indirect 27 29 github.com/charmbracelet/colorprofile v0.3.3 // indirect 28 30 github.com/charmbracelet/keygen v0.5.1 // indirect 29 31 github.com/charmbracelet/ultraviolet v0.0.0-20251106190538-99ea45596692 // indirect ··· 41 43 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect 42 44 github.com/go-logfmt/logfmt v0.6.0 // indirect 43 45 github.com/inconshreveable/mousetrap v1.1.0 // indirect 44 - github.com/joho/godotenv v1.5.1 // indirect 45 46 github.com/json-iterator/go v1.1.12 // indirect 46 47 github.com/kr/fs v0.1.0 // indirect 47 48 github.com/lucasb-eyer/go-colorful v1.3.0 // indirect ··· 57 58 github.com/muesli/mango-cobra v1.2.0 // indirect 58 59 github.com/muesli/mango-pflag v0.1.0 // indirect 59 60 github.com/muesli/roff v0.1.0 // indirect 60 - github.com/muesli/termenv v0.15.3-0.20240509142007-81b8f94111d5 // indirect 61 - github.com/pkg/sftp v1.13.10 // indirect 61 + github.com/muesli/termenv v0.16.0 // indirect 62 62 github.com/rivo/uniseg v0.4.7 // indirect 63 63 github.com/spf13/pflag v1.0.6 // indirect 64 64 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
+5 -12
go.sum
··· 12 12 github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 13 13 github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY= 14 14 github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= 15 - github.com/charmbracelet/bubbletea v1.0.0 h1:BlNvkVed3DADQlV+W79eioNUOrnMUY25EEVdFUoDoGA= 16 - github.com/charmbracelet/bubbletea v1.0.0/go.mod h1:xc4gm5yv+7tbniEvQ0naiG9P3fzYhk16cTgDZQQW6YE= 15 + github.com/charmbracelet/bubbletea v1.1.2 h1:naQXF2laRxyLyil/i7fxdpiz1/k06IKquhm4vBfHsIc= 16 + github.com/charmbracelet/bubbletea v1.1.2/go.mod h1:9HIU/hBV24qKjlehyj8z1r/tR9TYTQEag+cWZnuXo8E= 17 17 github.com/charmbracelet/colorprofile v0.3.3 h1:DjJzJtLP6/NZ8p7Cgjno0CKGr7wwRJGxWUwh2IyhfAI= 18 18 github.com/charmbracelet/colorprofile v0.3.3/go.mod h1:nB1FugsAbzq284eJcjfah2nhdSLppN2NqvfotkfRYP4= 19 19 github.com/charmbracelet/fang v0.4.4 h1:G4qKxF6or/eTPgmAolwPuRNyuci3hTUGGX1rj1YkHJY= ··· 106 106 github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0= 107 107 github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8= 108 108 github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig= 109 - github.com/muesli/termenv v0.15.3-0.20240509142007-81b8f94111d5 h1:NiONcKK0EV5gUZcnCiPMORaZA0eBDc+Fgepl9xl4lZ8= 110 - github.com/muesli/termenv v0.15.3-0.20240509142007-81b8f94111d5/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= 109 + github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= 110 + github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= 111 111 github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU= 112 112 github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA= 113 113 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= ··· 125 125 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 126 126 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= 127 127 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= 128 - golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 129 - golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 130 128 golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= 131 129 golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= 132 130 golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= 133 131 golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= 134 132 golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 135 - golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= 136 - golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 137 133 golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= 138 134 golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= 139 135 golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= ··· 145 141 golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= 146 142 golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 147 143 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 148 - golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= 149 - golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= 150 144 golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= 145 + golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= 151 146 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 152 - golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= 153 - golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= 154 147 golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= 155 148 golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= 156 149 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+40 -6
ssh/commands.go
··· 127 127 return 128 128 } 129 129 130 - fmt.Fprintln(sess, "Running "+filename+"...") 130 + // Simple spinner animation 131 + spinChars := []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} 132 + done := make(chan struct{}) 133 + result := make(chan struct { 134 + items int 135 + err error 136 + }) 131 137 132 - newItems, err := sched.RunNow(ctx, cfg.ID) 133 - if err != nil { 134 - fmt.Fprintln(sess, errorStyle.Render("Error: "+err.Error())) 138 + // Spinner goroutine 139 + go func() { 140 + i := 0 141 + for { 142 + select { 143 + case <-done: 144 + return 145 + default: 146 + fmt.Fprintf(sess, "\r%s Fetching feeds...", spinChars[i%len(spinChars)]) 147 + i++ 148 + time.Sleep(80 * time.Millisecond) 149 + } 150 + } 151 + }() 152 + 153 + // Work goroutine 154 + go func() { 155 + newItems, err := sched.RunNow(ctx, cfg.ID) 156 + result <- struct { 157 + items int 158 + err error 159 + }{items: newItems, err: err} 160 + }() 161 + 162 + // Wait for result 163 + res := <-result 164 + close(done) 165 + fmt.Fprint(sess, "\r\033[K") // Clear the spinner line 166 + 167 + if res.err != nil { 168 + fmt.Fprintln(sess, errorStyle.Render("Error: "+res.err.Error())) 135 169 return 136 170 } 137 171 138 - if newItems == 0 { 172 + if res.items == 0 { 139 173 fmt.Fprintln(sess, dimStyle.Render("No new items found.")) 140 174 } else { 141 - fmt.Fprintln(sess, successStyle.Render(fmt.Sprintf("Sent %d new item(s) to %s", newItems, cfg.Email))) 175 + fmt.Fprintln(sess, successStyle.Render(fmt.Sprintf("Sent %d new item(s) to %s", res.items, cfg.Email))) 142 176 } 143 177 } 144 178