1
0
mirror of https://github.com/makew0rld/amfora.git synced 2025-02-02 15:07:34 -05:00

Merge branch 'master' into stream

This commit is contained in:
makeworld 2021-05-14 18:47:43 -04:00
commit e3f062e75e
32 changed files with 656 additions and 196 deletions

View File

@ -5,6 +5,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Support for version 1.1 JSON feeds
- Copy current URL or selected URL to clipboard (#220, #225)
- Uses <kbd>C</kbd> and <kbd>c</kbd> by default
- Configurable keybindings for scrolling on pages (#211, #222)
- Ability to save `about:` pages (#210, #236)
- `bind_beginning` and `bind_end` keybindings
### Changed
- Favicon support removed (#199)
- Bookmarks are stored using XML in the XBEL format, old bookmarks are transferred (#68)
@ -17,6 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Possible subscription update race condition on startup
- Plaintext documents are escaped properly (regression from v1.8.0)
- Help page scrollbar color matches what's in the theme config
- Regression where lists would not appear if `bullets = false` (#234, #235)
- Support multiple bookmarks with the same name
## [1.8.0] - 2021-02-17

View File

@ -32,6 +32,11 @@ uninstall:
$(RM) -f $(PREFIX)/share/applications/amfora.desktop
# Development helpers
.PHONY: fmt
fmt:
$(GO) fmt ./...
.PHONY: gen
gen:
$(GO) generate ./...

View File

@ -6,11 +6,11 @@
## Upstream Bugs
- Bookmark keys aren't deleted, just set to `""`
- Waiting on [this viper PR](https://github.com/spf13/viper/pull/519) to be merged
- [cview.Styles not being used](https://gitlab.com/tslocum/cview/-/issues/47) - issue is circumvented in Amfora
- [ANSI conversion is messed up](https://gitlab.com/tslocum/cview/-/issues/48)
- [WordWrap is broken in some cases](https://gitlab.com/tslocum/cview/-/issues/27#note_475438483) - close #156 if this is fixed
- [Prevent panic when reformatting](https://gitlab.com/tslocum/cview/-/issues/50) - can't reliably reproduce or debug
- [Unicode bullet symbol mask causes issues with PasswordInput](https://gitlab.com/tslocum/cview/-/issues/55)
- [cview.Styles not being used](https://code.rocketnine.space/tslocum/cview/issues/47) - issue is circumvented in Amfora
- [ANSI conversion is messed up](https://code.rocketnine.space/tslocum/cview/issues/48)
- [WordWrap is broken in some cases](https://code.rocketnine.space/tslocum/cview/issues/27) - close #156 if this is fixed
- [Prevent panic when reformatting](https://code.rocketnine.space/tslocum/cview/issues/50) - can't reliably reproduce or debug
- [Unicode bullet symbol mask causes issues with PasswordInput](https://code.rocketnine.space/tslocum/cview/issues/55)
## Upstream PRs

View File

@ -36,14 +36,14 @@ update-desktop-database ~/.local/share/applications
Make sure to click "Watch" in the top right, then "Custom" > "Releases" to get notified about new releases!
## Linux
Amfora is packaged in many Linux distros. It's also on [Scoop](https://scoop.sh/) for Windows users.
### Linux
<a href="https://repology.org/project/amfora/versions">
<img src="https://repology.org/badge/vertical-allrepos/amfora.svg" alt="Packaging status" align="right">
</a>
Amfora is packaged in many Linux distros. It's also on [Scoop](https://scoop.sh/) for Windows users.
### Homebrew
If you use [Homebrew](https://brew.sh/), you can install Amfora through the my personal tap.
@ -56,6 +56,10 @@ You can update it with:
brew upgrade amfora
```
### Termux
If you're using [Termux](https://termux.com/) on Android you can't just run Amfora like normal. After installing Amfora, run `pkg install proot`. Then run `termux-chroot` before running the Amfora binary. You can exit out of the chroot after closing Amfora. See [here](https://stackoverflow.com/q/38959067/7361270) for why this is needed.
### From Source
This section is for advanced users who want to install the latest (possibly unstable) version of Amfora.
@ -139,14 +143,14 @@ Please see [the wiki](https://github.com/makeworld-the-better-one/amfora/wiki) f
## Libraries
Amfora ❤️ open source!
- [cview](https://gitlab.com/tslocum/cview/) for the TUI
- [cview](https://code.rocketnine.space/tslocum/cview) for the TUI
- It's a fork of [tview](https://github.com/rivo/tview) with PRs merged and active support
- It uses [tcell](https://github.com/gdamore/tcell) for low level terminal operations
- [Viper](https://github.com/spf13/viper) for configuration and TOFU storing
- [go-gemini](https://github.com/makeworld-the-better-one/go-gemini), my forked and updated Gemini client/server library
- My [progressbar fork](https://github.com/makeworld-the-better-one/progressbar) - pull request [here](https://github.com/schollz/progressbar/pull/69)
- [progressbar](https://github.com/schollz/progressbar)
- [go-humanize](https://github.com/dustin/go-humanize)
- My [gofeed fork](https://github.com/makeworld-the-better-one/gofeed) - pull request [here](https://github.com/mmcdole/gofeed/pull/164)
- [gofeed](https://github.com/mmcdole/gofeed)
## License
This project is licensed under the GPL v3.0. See the [LICENSE](./LICENSE) file for details.

View File

@ -17,3 +17,7 @@ Thank you to the following contributors, who have helped make Amfora great. FOSS
* Stephen Robinson (@sudobash1)
* Peter Steinberg (@objectliteral)
* Thomas Adam (@ThomasAdam)
* @lostleonardo
* Himanshu (@singalhimanshu)
* @regr4
* Anas Mohamed (@amohamed11)

View File

@ -159,27 +159,35 @@ func Remove(url string) {
}
}
// All returns all the bookmarks in a map of URLs to names.
// It also returns a slice of map keys, sorted so that the map *values*
// are in alphabetical order, with case ignored.
func All() (map[string]string, []string) {
bkmksMap := make(map[string]string)
inverted := make(map[string]string) // Holds inverted map, name->URL
names := make([]string, len(data.Bookmarks)) // Holds bookmark names, for sorting
keys := make([]string, len(data.Bookmarks)) // Final sorted keys (URLs), for returning at the end
for i, bkmk := range data.Bookmarks {
bkmksMap[bkmk.URL] = bkmk.Name
inverted[bkmk.Name] = bkmk.URL
names[i] = bkmk.Name
}
// Sort, then turn back into URL keys
sort.Strings(names)
for i, name := range names {
keys[i] = inverted[name]
}
return bkmksMap, keys
// bkmkNameSlice is used for sorting bookmarks alphabetically.
// It implements sort.Interface.
type bkmkNameSlice struct {
names []string
urls []string
}
func (b *bkmkNameSlice) Len() int {
return len(b.names)
}
func (b *bkmkNameSlice) Less(i, j int) bool {
return b.names[i] < b.names[j]
}
func (b *bkmkNameSlice) Swap(i, j int) {
b.names[i], b.names[j] = b.names[j], b.names[i]
b.urls[i], b.urls[j] = b.urls[j], b.urls[i]
}
// All returns all the bookmarks, as two arrays, one for names and one for URLs.
// They are sorted alphabetically.
func All() ([]string, []string) {
b := bkmkNameSlice{
make([]string, len(data.Bookmarks)),
make([]string, len(data.Bookmarks)),
}
for i, bkmk := range data.Bookmarks {
b.names[i] = bkmk.Name
b.urls[i] = bkmk.URL
}
sort.Sort(&b)
return b.names, b.urls
}

View File

@ -2,6 +2,7 @@ package bookmarks
// Structs and code for the XBEL XML bookmark format.
// https://github.com/makeworld-the-better-one/amfora/issues/68
// http://xbel.sourceforge.net/
import (
"encoding/xml"
@ -38,7 +39,7 @@ type xbel struct {
XMLName xml.Name `xml:"xbel"`
Version string `xml:"version,attr"`
Bookmarks []*xbelBookmark `xml:"bookmark"`
// Later: Folders []*xbelFolder
// Folders []*xbelFolder // Use later for #56
}
// Instance of xbel - loaded from bookmarks file

26
cache/page.go vendored
View File

@ -13,7 +13,7 @@ var pages = make(map[string]*structs.Page) // The actual cache
var urls = make([]string, 0) // Duplicate of the keys in the `pages` map, but in order of being added
var maxPages = 0 // Max allowed number of pages in cache
var maxSize = 0 // Max allowed cache size in bytes
var lock = sync.RWMutex{}
var mu = sync.RWMutex{}
var timeout = time.Duration(0)
// SetMaxPages sets the max number of pages the cache can hold.
@ -79,8 +79,8 @@ func AddPage(p *structs.Page) {
RemovePage(urls[0])
}
lock.Lock()
defer lock.Unlock()
mu.Lock()
defer mu.Unlock()
pages[p.URL] = p
// Remove the URL if it was already there, then add it to the end
removeURL(p.URL)
@ -90,24 +90,24 @@ func AddPage(p *structs.Page) {
// RemovePage will remove a page from the cache.
// Even if the page doesn't exist there will be no error.
func RemovePage(url string) {
lock.Lock()
defer lock.Unlock()
mu.Lock()
defer mu.Unlock()
delete(pages, url)
removeURL(url)
}
// ClearPages removes all pages from the cache.
func ClearPages() {
lock.Lock()
defer lock.Unlock()
mu.Lock()
defer mu.Unlock()
pages = make(map[string]*structs.Page)
urls = make([]string, 0)
}
// SizePages returns the approx. current size of the cache in bytes.
func SizePages() int {
lock.RLock()
defer lock.RUnlock()
mu.RLock()
defer mu.RUnlock()
n := 0
for _, page := range pages {
n += page.Size()
@ -116,16 +116,16 @@ func SizePages() int {
}
func NumPages() int {
lock.RLock()
defer lock.RUnlock()
mu.RLock()
defer mu.RUnlock()
return len(pages)
}
// GetPage returns the page struct, and a bool indicating if the page was in the cache or not.
// (nil, false) is returned if the page isn't in the cache.
func GetPage(url string) (*structs.Page, bool) {
lock.RLock()
defer lock.RUnlock()
mu.RLock()
defer mu.RUnlock()
p, ok := pages[url]
if ok && (timeout == 0 || time.Since(p.MadeAt) < timeout) {

View File

@ -11,13 +11,13 @@ import (
"runtime"
"strings"
"code.rocketnine.space/tslocum/cview"
"github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/cache"
homedir "github.com/mitchellh/go-homedir"
"github.com/rkoesters/xdg/basedir"
"github.com/rkoesters/xdg/userdirs"
"github.com/spf13/viper"
"gitlab.com/tslocum/cview"
)
var amforaAppData string // Where amfora files are stored on Windows - cached here
@ -210,6 +210,10 @@ func Init() error {
viper.SetDefault("keybindings.bind_sub", "Ctrl-A")
viper.SetDefault("keybindings.bind_add_sub", "Ctrl-X")
viper.SetDefault("keybindings.bind_save", "Ctrl-S")
viper.SetDefault("keybindings.bind_moveup", "k")
viper.SetDefault("keybindings.bind_movedown", "j")
viper.SetDefault("keybindings.bind_moveleft", "h")
viper.SetDefault("keybindings.bind_moveright", "l")
viper.SetDefault("keybindings.bind_pgup", []string{"PgUp", "u"})
viper.SetDefault("keybindings.bind_pgdn", []string{"PgDn", "d"})
viper.SetDefault("keybindings.bind_bottom", "Space")
@ -242,6 +246,10 @@ func Init() error {
viper.SetDefault("keybindings.bind_tab8", "*")
viper.SetDefault("keybindings.bind_tab9", "(")
viper.SetDefault("keybindings.bind_tab0", ")")
viper.SetDefault("keybindings.bind_copy_page_url", "C")
viper.SetDefault("keybindings.bind_copy_target_url", "c")
viper.SetDefault("keybindings.bind_beginning", []string{"Home", "g"})
viper.SetDefault("keybindings.bind_end", []string{"End", "G"})
viper.SetDefault("keybindings.shift_numbers", "")
viper.SetDefault("url-handlers.other", "off")
viper.SetDefault("cache.max_size", 0)

View File

@ -148,6 +148,10 @@ scrollbar = "auto"
# bind_reload
# bind_back
# bind_forward
# bind_moveup
# bind_movedown
# bind_moveleft
# bind_moveright
# bind_pgup
# bind_pgdn
# bind_new_tab
@ -158,6 +162,10 @@ scrollbar = "auto"
# bind_help
# bind_sub: for viewing the subscriptions page
# bind_add_sub
# bind_copy_page_url
# bind_copy_target_url
# bind_beginning: moving to beginning of page (top left)
# bind_end: same but the for the end (bottom left)
[url-handlers]
# Allows setting the commands to run for various URL schemes.

View File

@ -3,6 +3,7 @@ package config
import (
"strings"
"code.rocketnine.space/tslocum/cview"
"github.com/gdamore/tcell/v2"
"github.com/spf13/viper"
)
@ -42,6 +43,10 @@ const (
CmdReload
CmdBack
CmdForward
CmdMoveUp
CmdMoveDown
CmdMoveLeft
CmdMoveRight
CmdPgup
CmdPgdn
CmdNewTab
@ -52,6 +57,10 @@ const (
CmdHelp
CmdSub
CmdAddSub
CmdCopyPageURL
CmdCopyTargetURL
CmdBeginning
CmdEnd
)
type keyBinding struct {
@ -147,35 +156,43 @@ func parseBinding(cmd Command, binding string) {
// Called by config.Init()
func KeyInit() {
configBindings := map[Command]string{
CmdLink1: "keybindings.bind_link1",
CmdLink2: "keybindings.bind_link2",
CmdLink3: "keybindings.bind_link3",
CmdLink4: "keybindings.bind_link4",
CmdLink5: "keybindings.bind_link5",
CmdLink6: "keybindings.bind_link6",
CmdLink7: "keybindings.bind_link7",
CmdLink8: "keybindings.bind_link8",
CmdLink9: "keybindings.bind_link9",
CmdLink0: "keybindings.bind_link0",
CmdBottom: "keybindings.bind_bottom",
CmdEdit: "keybindings.bind_edit",
CmdHome: "keybindings.bind_home",
CmdBookmarks: "keybindings.bind_bookmarks",
CmdAddBookmark: "keybindings.bind_add_bookmark",
CmdSave: "keybindings.bind_save",
CmdReload: "keybindings.bind_reload",
CmdBack: "keybindings.bind_back",
CmdForward: "keybindings.bind_forward",
CmdPgup: "keybindings.bind_pgup",
CmdPgdn: "keybindings.bind_pgdn",
CmdNewTab: "keybindings.bind_new_tab",
CmdCloseTab: "keybindings.bind_close_tab",
CmdNextTab: "keybindings.bind_next_tab",
CmdPrevTab: "keybindings.bind_prev_tab",
CmdQuit: "keybindings.bind_quit",
CmdHelp: "keybindings.bind_help",
CmdSub: "keybindings.bind_sub",
CmdAddSub: "keybindings.bind_add_sub",
CmdLink1: "keybindings.bind_link1",
CmdLink2: "keybindings.bind_link2",
CmdLink3: "keybindings.bind_link3",
CmdLink4: "keybindings.bind_link4",
CmdLink5: "keybindings.bind_link5",
CmdLink6: "keybindings.bind_link6",
CmdLink7: "keybindings.bind_link7",
CmdLink8: "keybindings.bind_link8",
CmdLink9: "keybindings.bind_link9",
CmdLink0: "keybindings.bind_link0",
CmdBottom: "keybindings.bind_bottom",
CmdEdit: "keybindings.bind_edit",
CmdHome: "keybindings.bind_home",
CmdBookmarks: "keybindings.bind_bookmarks",
CmdAddBookmark: "keybindings.bind_add_bookmark",
CmdSave: "keybindings.bind_save",
CmdReload: "keybindings.bind_reload",
CmdBack: "keybindings.bind_back",
CmdForward: "keybindings.bind_forward",
CmdMoveUp: "keybindings.bind_moveup",
CmdMoveDown: "keybindings.bind_movedown",
CmdMoveLeft: "keybindings.bind_moveleft",
CmdMoveRight: "keybindings.bind_moveright",
CmdPgup: "keybindings.bind_pgup",
CmdPgdn: "keybindings.bind_pgdn",
CmdNewTab: "keybindings.bind_new_tab",
CmdCloseTab: "keybindings.bind_close_tab",
CmdNextTab: "keybindings.bind_next_tab",
CmdPrevTab: "keybindings.bind_prev_tab",
CmdQuit: "keybindings.bind_quit",
CmdHelp: "keybindings.bind_help",
CmdSub: "keybindings.bind_sub",
CmdAddSub: "keybindings.bind_add_sub",
CmdCopyPageURL: "keybindings.bind_copy_page_url",
CmdCopyTargetURL: "keybindings.bind_copy_target_url",
CmdBeginning: "keybindings.bind_beginning",
CmdEnd: "keybindings.bind_end",
}
// This is split off to allow shift_numbers to override bind_tab[1-90]
// (This is needed for older configs so that the default bind_tab values
@ -199,6 +216,16 @@ func KeyInit() {
tcellKeys[kname] = k
}
// Set cview navigation keys to use user-set ones
cview.Keys.MoveUp2 = viper.GetStringSlice(configBindings[CmdMoveUp])
cview.Keys.MoveDown2 = viper.GetStringSlice(configBindings[CmdMoveDown])
cview.Keys.MoveLeft2 = viper.GetStringSlice(configBindings[CmdMoveLeft])
cview.Keys.MoveRight2 = viper.GetStringSlice(configBindings[CmdMoveRight])
cview.Keys.MoveFirst = viper.GetStringSlice(configBindings[CmdBeginning])
cview.Keys.MoveFirst2 = nil
cview.Keys.MoveLast = viper.GetStringSlice(configBindings[CmdEnd])
cview.Keys.MoveLast2 = nil
for c, allb := range configBindings {
for _, b := range viper.GetStringSlice(allb) {
parseBinding(c, b)

View File

@ -1,6 +1,6 @@
# User Contributed Themes
You can use these themes by replacing the `[theme]` section of your config with their contents. Some themes won't display properly on terminals that do not have truecolor support.
You can use these themes by replacing the `[theme]` section of your [config](https://github.com/makeworld-the-better-one/amfora/wiki/Configuration) with their contents. Some themes won't display properly on terminals that do not have truecolor support.
## Nord
@ -69,7 +69,7 @@ Contributed by **[@bnthor](https://github.com/bnthor)**.
</details>
### One Dark
## One Dark
Contributed by **[@sergetymo](https://github.com/sergetymo)**.
@ -82,6 +82,39 @@ Contributed by **[@sergetymo](https://github.com/sergetymo)**.
![screenshot of error modal](https://user-images.githubusercontent.com/65758149/101183206-da73aa00-3657-11eb-8733-5040c8aefb99.png)
</details>
## Atelier Forest
Contributed by **[@joyalicegu](https://github.com/joyalicegu)**.
### Dark
![screenshot of atelier forest dark theme](https://user-images.githubusercontent.com/16532904/114287117-35a81580-9a19-11eb-8515-a4fa5fee00b8.png)
### Light
![screenshot of atelier forest light theme](https://user-images.githubusercontent.com/16532904/114287105-22954580-9a19-11eb-8c09-2bce083286b2.png)
## Slimey
Contributed by **[@lee2sman](https://github.com/lee2sman)**.
![screenshot of Slimey theme](https://user-images.githubusercontent.com/7377908/114319350-212e5080-9adf-11eb-9d41-d4e800c6570f.png)
## Gruvbox Dark
Contributed by **[@thumb](https://github.com/thumbfighter)**.
![screenshot of Gruvbox Dark theme](https://user-images.githubusercontent.com/19327775/114431954-b97f1080-9b85-11eb-9da5-e1ebf06beba9.png)
<details>
<summary>More screenshots</summary>
![screenshot of makeworld.space home page](https://user-images.githubusercontent.com/19327775/114432099-f0552680-9b85-11eb-9b4f-629d62d971b4.png)
![screenshot of error](https://user-images.githubusercontent.com/19327775/114432066-e4696480-9b85-11eb-8ed0-c454f37fe9cf.png)
![screenshot of add bookmark](https://user-images.githubusercontent.com/19327775/114432084-e8958200-9b85-11eb-9813-9982c3c0effa.png)
</details>
## Yours?
Contribute your own theme by opening a PR.

View File

@ -0,0 +1,52 @@
[theme]
# atelier forest light
bg = "#f1efee"
fg = "#68615e"
tab_num = "#68615e"
tab_divider = "#e6e2e0"
bottombar_label = "#3d97b8"
bottombar_text = "#68615e"
bottombar_bg = "#f1efee"
scrollbar = "#68615e"
hdg_1 = "#f22c40"
hdg_2 = "#7b9726"
hdg_3 = "#c33ff3"
amfora_link = "#407ee7"
foreign_link = "#f22c40"
link_number = "#68615e"
regular_text = "#68615e"
quote_text = "#68615e"
preformatted_text = "#68615e"
list_text = "#68615e"
btn_bg = "#407ee7"
btn_text = "#f1efee"
dl_choice_modal_bg = "#e6e2e0"
dl_choice_modal_text = "#68615e"
dl_modal_bg = "#e6e2e0"
dl_modal_text = "#68615e"
info_modal_bg = "#e6e2e0"
info_modal_text = "#68615e"
error_modal_bg = "#e6e2e0"
error_modal_text = "#f22c40"
yesno_modal_bg = "#e6e2e0"
yesno_modal_text = "#68615e"
tofu_modal_bg = "#e6e2e0"
tofu_modal_text = "#68615e"
subscription_modal_bg = "#e6e2e0"
subscription_modal_text = "#68615e"
input_modal_bg = "#e6e2e0"
input_modal_text = "#68615e"
input_modal_field_bg = "#f1efee"
input_modal_field_text = "#68615e"
bkmk_modal_bg = "#e6e2e0"
bkmk_modal_text = "#68615e"
bkmk_modal_label = "#3d97b8"
bkmk_modal_field_bg = "#f1efee"
bkmk_modal_field_text = "#68615e"

View File

@ -0,0 +1,52 @@
[theme]
# atelier forest
bg = "#1b1918"
fg = "#a8a19f"
tab_num = "#a8a19f"
tab_divider = "#2c2421"
bottombar_label = "#3d97b8"
bottombar_text = "#a8a19f"
bottombar_bg = "#1b1918"
scrollbar = "#a8a19f"
hdg_1 = "#f22c40"
hdg_2 = "#7b9726"
hdg_3 = "#c33ff3"
amfora_link = "#407ee7"
foreign_link = "#f22c40"
link_number = "#a8a19f"
regular_text = "#a8a19f"
quote_text = "#a8a19f"
preformatted_text = "#a8a19f"
list_text = "#a8a19f"
btn_bg = "#407ee7"
btn_text = "#1b1918"
dl_choice_modal_bg = "#2c2421"
dl_choice_modal_text = "#a8a19f"
dl_modal_bg = "#2c2421"
dl_modal_text = "#a8a19f"
info_modal_bg = "#2c2421"
info_modal_text = "#a8a19f"
error_modal_bg = "#2c2421"
error_modal_text = "#f22c40"
yesno_modal_bg = "#2c2421"
yesno_modal_text = "#a8a19f"
tofu_modal_bg = "#2c2421"
tofu_modal_text = "#a8a19f"
subscription_modal_bg = "#2c2421"
subscription_modal_text = "#a8a19f"
input_modal_bg = "#2c2421"
input_modal_text = "#a8a19f"
input_modal_field_bg = "#1b1918"
input_modal_field_text = "#a8a19f"
bkmk_modal_bg = "#2c2421"
bkmk_modal_text = "#a8a19f"
bkmk_modal_label = "#3d97b8"
bkmk_modal_field_bg = "#1b1918"
bkmk_modal_field_text = "#a8a19f"

View File

@ -0,0 +1,52 @@
[theme]
# Gruvbox Dark theme
bg = "#282828"
fg = "#32302f"
tab_num = "#7c6f64"
tab_divider = "#d5c4a1"
bottombar_label = "#8f3f71"
bottombar_text = "#bdae93"
bottombar_bg = "#282828"
scrollbar = "#504945"
hdg_1 = "#cc241d"
hdg_2 = "#fabd2f"
hdg_3 = "#d65d0e"
amfora_link = "#8ec073"
foreign_link = "#458588"
link_number = "#504945"
regular_text = "#f9f5d7"
quote_text = "#d3869b"
preformatted_text = "#d3869b"
list_text = "#bdae93"
btn_bg = "#3c3836"
btn_text = "#ebdbb2"
dl_choice_modal_bg = "#3c3836"
dl_choice_modal_text = "#ebdbb2"
dl_modal_bg = "#3c3836"
dl_modal_text = "#ebdbb2"
info_modal_bg = "#3c3836"
info_modal_text = "#ebdbb2"
error_modal_bg = "#3c3836"
error_modal_text = "#fe8019"
yesno_modal_bg = "#3c3836"
yesno_modal_text = "#ebdbb2"
tofu_modal_bg = "#3c3836"
tofu_modal_text = "#ebdbb2"
subscription_modal_bg = "#3c3836"
subscription_modal_text = "#ebdbb2"
input_modal_bg = "#3c3836"
input_modal_text = "#ebdbb2"
input_modal_field_bg = "#1d2021"
input_modal_field_text = "#ebdbb2"
bkmk_modal_bg = "#3c3836"
bkmk_modal_text = "#ebdbb2"
bkmk_modal_label = "#ebdbb2"
bkmk_modal_field_bg = "#1d2021"
bkmk_modal_field_text = "#f9f5d7"

View File

@ -0,0 +1,82 @@
[theme]
# This section is for changing the COLORS used in Amfora.
# These colors only apply if 'color' is enabled above.
# Colors can be set using a W3C color name, or a hex value such as "#ffffff".
# Note that not all colors will work on terminals that do not have truecolor support.
# If you want to stick to the standard 16 or 256 colors, you can get
# a list of those here: https://jonasjacek.github.io/colors/
# DO NOT use the names from that site, just the hex codes.
# Definitions:
# bg = background
# fg = foreground
# dl = download
# btn = button
# hdg = heading
# bkmk = bookmark
# modal = a popup window/box in the middle of the screen
# EXAMPLES:
# hdg_1 = "green"
# hdg_2 = "#5f0000"
# Available keys to set:
# bg: background for pages, tab row, app in general
bg = "#c7fcd6"
# tab_num: The number/highlight of the tabs at the top
tab_num = "#f49ab9"
# tab_divider: The color of the divider character between tab numbers: |
tab_divider = "#117bf4"
# bottombar_label: The color of the prompt that appears when you press space
bottomrbar_label = "#e9f411"
# bottombar_text: The color of the text you type
bottombar_text = "#040405"
# bottombar_bg
bottombar_bg = "#1fbde0"
hdg_1 = "#a369ef"
hdg_2 = "#ef69ba"
hdg_3 = "#f4295f"
# amfora_link: A link that Amfora supports viewing. For now this is only gemini://
amfora_link = "#A020F0"
# foreign_link: HTTP(S), Gopher, etc
foreign_link = "#808080"
# link_number: The silver number that appears to the left of a link
link_number = "#2662e2"
# regular_text: Normal gemini text, and plaintext documents
regular_text = "#04000c"
# quote_text
quote_text = "#666699"
# preformatted_text
preformatted_text = "#ff1493"
# list_text
list_text = "#04000c"
# btn_bg: The bg color for all modal buttons
# btn_text: The text color for all modal buttons
# dl_choice_modal_bg
# dl_choice_modal_text
# dl_modal_bg
# dl_modal_text
# info_modal_bg
# info_modal_text
# error_modal_bg
# error_modal_text
# yesno_modal_bg
# yesno_modal_text
# tofu_modal_bg
# tofu_modal_text
# input_modal_bg
# input_modal_text
# input_modal_field_bg: The bg of the input field, where you type the text
# input_modal_field_text: The color of the text you type
# bkmk_modal_bg
# bkmk_modal_text
# bkmk_modal_label
# bkmk_modal_field_bg
# bkmk_modal_field_text

View File

@ -145,6 +145,10 @@ scrollbar = "auto"
# bind_reload
# bind_back
# bind_forward
# bind_moveup
# bind_movedown
# bind_moveleft
# bind_moveright
# bind_pgup
# bind_pgdn
# bind_new_tab
@ -155,6 +159,10 @@ scrollbar = "auto"
# bind_help
# bind_sub: for viewing the subscriptions page
# bind_add_sub
# bind_copy_page_url
# bind_copy_target_url
# bind_beginning: moving to beginning of page (top left)
# bind_end: same but the for the end (bottom left)
[url-handlers]
# Allows setting the commands to run for various URL schemes.

View File

@ -3,13 +3,13 @@ package display
import (
"fmt"
"code.rocketnine.space/tslocum/cview"
"github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/bookmarks"
"github.com/makeworld-the-better-one/amfora/config"
"github.com/makeworld-the-better-one/amfora/render"
"github.com/makeworld-the-better-one/amfora/structs"
"github.com/spf13/viper"
"gitlab.com/tslocum/cview"
)
// For adding and removing bookmarks, basically a clone of the input modal.
@ -127,9 +127,9 @@ func Bookmarks(t *tab) {
bkmkPageRaw := "# Bookmarks\r\n\r\n"
// Gather bookmarks
m, keys := bookmarks.All()
for i := range keys {
bkmkPageRaw += fmt.Sprintf("=> %s %s\r\n", keys[i], m[keys[i]])
names, urls := bookmarks.All()
for i := range names {
bkmkPageRaw += fmt.Sprintf("=> %s %s\r\n", urls[i], names[i])
}
// Render and display
content, links := render.RenderGemini(bkmkPageRaw, textWidth(), false)
@ -152,7 +152,7 @@ func addBookmark() {
t := tabs[curTab]
p := t.page
if !t.hasContent() {
if !t.hasContent() || t.isAnAboutPage() {
// It's an about: page, or a malformed one
return
}

View File

@ -8,6 +8,7 @@ import (
"strings"
"sync"
"code.rocketnine.space/tslocum/cview"
"github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/cache"
"github.com/makeworld-the-better-one/amfora/config"
@ -15,7 +16,6 @@ import (
"github.com/makeworld-the-better-one/amfora/structs"
"github.com/makeworld-the-better-one/go-gemini"
"github.com/spf13/viper"
"gitlab.com/tslocum/cview"
)
var tabs []*tab // Slice of all the current browser tabs
@ -154,7 +154,7 @@ func Init(version, commit, builtBy string) {
reset()
return
}
if query[0] == '.' && tabs[tab].hasContent() {
if query[0] == '.' && tabs[tab].hasContent() && !tabs[tab].isAnAboutPage() {
// Relative url
current, err := url.Parse(tabs[tab].page.URL)
if err != nil {
@ -301,31 +301,6 @@ func Init(version, commit, builtBy string) {
case config.CmdHome:
URL(viper.GetString("a-general.home"))
return nil
case config.CmdBookmarks:
Bookmarks(tabs[curTab])
tabs[curTab].addToHistory("about:bookmarks")
return nil
case config.CmdAddBookmark:
go addBookmark()
return nil
case config.CmdPgup:
tabs[curTab].pageUp()
return nil
case config.CmdPgdn:
tabs[curTab].pageDown()
return nil
case config.CmdSave:
if tabs[curTab].hasContent() {
savePath, err := downloadPage(tabs[curTab].page)
if err != nil {
Error("Download Error", fmt.Sprintf("Error saving page content: %v", err))
} else {
Info(fmt.Sprintf("Page content saved to %s. ", savePath))
}
} else {
Info("The current page has no content, so it couldn't be downloaded.")
}
return nil
case config.CmdBottom:
// Space starts typing, like Bombadillo
bottomBar.SetLabel("[::b]URL/Num./Search: [::-]")
@ -339,29 +314,10 @@ func Init(version, commit, builtBy string) {
bottomBar.SetText(tabs[curTab].page.URL)
App.SetFocus(bottomBar)
return nil
case config.CmdBack:
histBack(tabs[curTab])
return nil
case config.CmdForward:
histForward(tabs[curTab])
return nil
case config.CmdSub:
Subscriptions(tabs[curTab], "about:subscriptions")
tabs[curTab].addToHistory("about:subscriptions")
return nil
case config.CmdAddSub:
go addSubscription()
return nil
}
// Number key: 1-9, 0, LINK1-LINK10
if cmd >= config.CmdLink1 && cmd <= config.CmdLink0 {
if int(cmd) <= len(tabs[curTab].page.Links) {
// It's a valid link number
followLink(tabs[curTab], tabs[curTab].page.URL, tabs[curTab].page.Links[cmd-1])
return nil
}
}
}
// All the keys and operations that can work while a tab IS loading

View File

@ -14,6 +14,7 @@ import (
"strings"
"time"
"code.rocketnine.space/tslocum/cview"
"github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/config"
"github.com/makeworld-the-better-one/amfora/structs"
@ -21,7 +22,6 @@ import (
"github.com/makeworld-the-better-one/go-gemini"
"github.com/schollz/progressbar/v3"
"github.com/spf13/viper"
"gitlab.com/tslocum/cview"
)
// For choosing between download and the portal - copy of YesNo basically
@ -321,8 +321,14 @@ func downloadPage(p *structs.Page) (string, error) {
func downloadNameFromURL(dir, u, ext string) (string, error) {
var name string
var err error
parsed, _ := url.Parse(u)
if parsed.Path == "" || path.Base(parsed.Path) == "/" {
if strings.HasPrefix(u, "about:") {
name, err = getSafeDownloadName(dir, parsed.Opaque+ext, true, 0)
if err != nil {
return "", err
}
} else if parsed.Path == "" || path.Base(parsed.Path) == "/" {
// No file, just the root domain
name, err = getSafeDownloadName(dir, parsed.Hostname()+ext, true, 0)
if err != nil {
@ -340,6 +346,7 @@ func downloadNameFromURL(dir, u, ext string) (string, error) {
return "", err
}
}
return filepath.Join(dir, name), nil
}

View File

@ -180,7 +180,7 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) {
t.mode = tabModeDone
go func(p *structs.Page) {
if b && t.hasContent() && viper.GetBool("subscriptions.popup") {
if b && t.hasContent() && !t.isAnAboutPage() && viper.GetBool("subscriptions.popup") {
// The current page might be an untracked feed, and the user wants
// to be notified in such cases.

View File

@ -5,19 +5,19 @@ import (
"strings"
"text/tabwriter"
"code.rocketnine.space/tslocum/cview"
"github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/config"
"gitlab.com/tslocum/cview"
)
var helpCells = strings.TrimSpace(
"?\tBring up this help. You can scroll!\n" +
"Esc\tLeave the help\n" +
"Arrow keys, h/j/k/l\tScroll and move a page.\n" +
"Arrow keys, %s(left)/%s(down)/%s(up)/%s(right)\tScroll and move a page.\n" +
"%s\tGo up a page in document\n" +
"%s\tGo down a page in document\n" +
"g\tGo to top of document\n" +
"G\tGo to bottom of document\n" +
"%s\tGo to top of document\n" +
"%s\tGo to bottom of document\n" +
"Tab\tNavigate to the next item in a popup.\n" +
"Shift-Tab\tNavigate to the previous item in a popup.\n" +
"%s\tGo back in the history\n" +
@ -28,6 +28,8 @@ var helpCells = strings.TrimSpace(
"\tinstead of the current one.\n" +
"%s\tGo to links 1-10 respectively.\n" +
"%s\tEdit current URL\n" +
"%s\tCopy current page URL\n" +
"%s\tCopy current selected URL\n" +
"Enter, Tab\tOn a page this will start link highlighting.\n" +
"\tPress Tab and Shift-Tab to pick different links.\n" +
"\tPress Enter again to go to one, or Esc to stop.\n" +
@ -78,13 +80,21 @@ func helpInit() {
strings.Split(config.GetKeyBinding(config.CmdLink0), ",")[0])
helpCells = fmt.Sprintf(helpCells,
config.GetKeyBinding(config.CmdMoveLeft),
config.GetKeyBinding(config.CmdMoveDown),
config.GetKeyBinding(config.CmdMoveUp),
config.GetKeyBinding(config.CmdMoveRight),
config.GetKeyBinding(config.CmdPgup),
config.GetKeyBinding(config.CmdPgdn),
config.GetKeyBinding(config.CmdBeginning),
config.GetKeyBinding(config.CmdEnd),
config.GetKeyBinding(config.CmdBack),
config.GetKeyBinding(config.CmdForward),
config.GetKeyBinding(config.CmdBottom),
linkKeys,
config.GetKeyBinding(config.CmdEdit),
config.GetKeyBinding(config.CmdCopyPageURL),
config.GetKeyBinding(config.CmdCopyTargetURL),
tabKeys,
config.GetKeyBinding(config.CmdTab0),
config.GetKeyBinding(config.CmdPrevTab),

View File

@ -5,11 +5,11 @@ import (
"strings"
"time"
"code.rocketnine.space/tslocum/cview"
humanize "github.com/dustin/go-humanize"
"github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/config"
"github.com/spf13/viper"
"gitlab.com/tslocum/cview"
)
// This file contains code for the popups / modals used in the display.

View File

@ -23,7 +23,7 @@ func followLink(t *tab, prev, next string) {
return
}
if t.hasContent() {
if t.hasContent() && !t.isAnAboutPage() {
nextURL, err := resolveRelLink(t, prev, next)
if err != nil {
Error("URL Error", err.Error())

View File

@ -304,7 +304,7 @@ func addSubscription() {
t := tabs[curTab]
p := t.page
if !t.hasContent() {
if !t.hasContent() || t.isAnAboutPage() {
// It's an about: page, or a malformed one
return
}

View File

@ -1,13 +1,16 @@
package display
import (
"fmt"
"net/url"
"strconv"
"strings"
"code.rocketnine.space/tslocum/cview"
"github.com/atotto/clipboard"
"github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/config"
"github.com/makeworld-the-better-one/amfora/structs"
"gitlab.com/tslocum/cview"
)
type tabMode int
@ -122,15 +125,100 @@ func makeNewTab() *tab {
}
})
t.view.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
// Capture left/right scrolling and change the left margin size accordingly, see #197
// Up/down scrolling is saved in this func to keep them in sync, but the keys
// are passed and no extra behaviour happens.
// Capture scrolling and change the left margin size accordingly, see #197
// This was also touched by #222
// This also captures any tab-specific events now
if t.mode != tabModeDone {
// Any events that should be caught when the tab is loading is handled in display.go
return nil
}
cmd := config.TranslateKeyEvent(event)
// Cmds that aren't single row/column scrolling
//nolint:exhaustive
switch cmd {
case config.CmdBookmarks:
Bookmarks(&t)
t.addToHistory("about:bookmarks")
return nil
case config.CmdAddBookmark:
go addBookmark()
return nil
case config.CmdPgup:
t.pageUp()
return nil
case config.CmdPgdn:
t.pageDown()
return nil
case config.CmdSave:
if t.hasContent() {
savePath, err := downloadPage(t.page)
if err != nil {
Error("Download Error", fmt.Sprintf("Error saving page content: %v", err))
} else {
Info(fmt.Sprintf("Page content saved to %s. ", savePath))
}
} else {
Info("The current page has no content, so it couldn't be downloaded.")
}
return nil
case config.CmdBack:
histBack(&t)
return nil
case config.CmdForward:
histForward(&t)
return nil
case config.CmdSub:
Subscriptions(&t, "about:subscriptions")
tabs[curTab].addToHistory("about:subscriptions")
return nil
case config.CmdCopyPageURL:
currentURL := tabs[curTab].page.URL
err := clipboard.WriteAll(currentURL)
if err != nil {
Error("Copy Error", err.Error())
return nil
}
return nil
case config.CmdCopyTargetURL:
currentURL := t.page.URL
selectedURL := t.HighlightedURL()
if selectedURL == "" {
return nil
}
u, _ := url.Parse(currentURL)
copiedURL, err := u.Parse(selectedURL)
if err != nil {
err := clipboard.WriteAll(selectedURL)
if err != nil {
Error("Copy Error", err.Error())
return nil
}
return nil
}
err = clipboard.WriteAll(copiedURL.String())
if err != nil {
Error("Copy Error", err.Error())
return nil
}
return nil
}
// Number key: 1-9, 0, LINK1-LINK10
if cmd >= config.CmdLink1 && cmd <= config.CmdLink0 {
if int(cmd) <= len(t.page.Links) {
// It's a valid link number
followLink(&t, t.page.URL, t.page.Links[cmd-1])
return nil
}
}
// Scrolling stuff
key := event.Key()
mod := event.Modifiers()
ru := event.Rune()
width, height := t.view.GetBufferSize()
height, width := t.view.GetBufferSize()
_, _, boxW, boxH := t.view.GetInnerRect()
// Make boxW accurate by subtracting one if a scrollbar is covering the last
@ -140,8 +228,7 @@ func makeNewTab() *tab {
boxW--
}
if (key == tcell.KeyRight && mod == tcell.ModNone) ||
(key == tcell.KeyRune && mod == tcell.ModNone && ru == 'l') {
if cmd == config.CmdMoveRight || (key == tcell.KeyRight && mod == tcell.ModNone) {
// Scrolling to the right
if t.page.Column >= leftMargin() {
@ -158,28 +245,37 @@ func makeNewTab() *tab {
}
}
t.page.Column++
} else if (key == tcell.KeyLeft && mod == tcell.ModNone) ||
(key == tcell.KeyRune && mod == tcell.ModNone && ru == 'h') {
} else if cmd == config.CmdMoveLeft || (key == tcell.KeyLeft && mod == tcell.ModNone) {
// Scrolling to the left
if t.page.Column == 0 {
// Can't scroll to the left anymore
return nil
}
t.page.Column--
} else if (key == tcell.KeyUp && mod == tcell.ModNone) ||
(key == tcell.KeyRune && mod == tcell.ModNone && ru == 'k') {
} else if cmd == config.CmdMoveUp || (key == tcell.KeyUp && mod == tcell.ModNone) {
// Scrolling up
if t.page.Row > 0 {
t.page.Row--
}
return event
} else if (key == tcell.KeyDown && mod == tcell.ModNone) ||
(key == tcell.KeyRune && mod == tcell.ModNone && ru == 'j') {
} else if cmd == config.CmdMoveDown || (key == tcell.KeyDown && mod == tcell.ModNone) {
// Scrolling down
if t.page.Row < height {
t.page.Row++
}
return event
} else if cmd == config.CmdBeginning {
t.page.Row = 0
// This is required because cview will also set the column (incorrectly)
// if it handles this event itself
t.applyScroll()
App.Draw()
return nil
} else if cmd == config.CmdEnd {
t.page.Row = height
t.applyScroll()
App.Draw()
return nil
} else {
// Some other key, stop processing it
return event
@ -207,18 +303,27 @@ func (t *tab) addToHistory(u string) {
// pageUp scrolls up 75% of the height of the terminal, like Bombadillo.
func (t *tab) pageUp() {
row, col := t.view.GetScrollOffset()
t.view.ScrollTo(row-(termH/4)*3, col)
t.page.Row -= (termH / 4) * 3
if t.page.Row < 0 {
t.page.Row = 0
}
t.applyScroll()
}
// pageDown scrolls down 75% of the height of the terminal, like Bombadillo.
func (t *tab) pageDown() {
row, col := t.view.GetScrollOffset()
t.view.ScrollTo(row+(termH/4)*3, col)
height, _ := t.view.GetBufferSize()
t.page.Row += (termH / 4) * 3
if t.page.Row > height {
t.page.Row = height
}
t.applyScroll()
}
// hasContent returns false when the tab's page is malformed,
// has no content or URL, or if it's an 'about:' page.
// has no content or URL.
func (t *tab) hasContent() bool {
if t.page == nil || t.view == nil {
return false
@ -226,15 +331,17 @@ func (t *tab) hasContent() bool {
if t.page.URL == "" {
return false
}
if strings.HasPrefix(t.page.URL, "about:") {
return false
}
if t.page.Content == "" {
return false
}
return true
}
// isAnAboutPage returns true when the tab's page is an about page
func (t *tab) isAnAboutPage() bool {
return strings.HasPrefix(t.page.URL, "about:")
}
// applyHorizontalScroll handles horizontal scroll logic including left margin resizing,
// see #197 for details. Use applyScroll instead.
//
@ -323,3 +430,15 @@ func (t *tab) applyAll() {
t.applyBottomBar()
}
}
// HighlightedURL returns the currently selected URL
func (t *tab) HighlightedURL() string {
currentSelection := tabs[curTab].view.GetHighlights()
if len(currentSelection) > 0 {
linkN, _ := strconv.Atoi(currentSelection[0])
selectedURL := tabs[curTab].page.Links[linkN]
return selectedURL
}
return ""
}

View File

@ -21,4 +21,8 @@ Thank you to the following contributors, who have helped make Amfora great. FOSS
* Stephen Robinson (@sudobash1)
* Peter Steinberg (@objectliteral)
* Thomas Adam (@ThomasAdam)
* @lostleonardo
* Himanshu (@singalhimanshu)
* @regr4
* Anas Mohamed (@amohamed11)
`)

View File

@ -5,9 +5,9 @@ import (
"net/url"
"strings"
"code.rocketnine.space/tslocum/cview"
"github.com/makeworld-the-better-one/go-gemini"
"github.com/spf13/viper"
"gitlab.com/tslocum/cview"
"golang.org/x/text/unicode/norm"
)
@ -90,7 +90,7 @@ func textWidth() int {
// It also returns an error if it could not resolve the links, which should be displayed
// to the user.
func resolveRelLink(t *tab, prev, next string) (string, error) {
if !t.hasContent() {
if !t.hasContent() || t.isAnAboutPage() {
return next, nil
}

13
go.mod
View File

@ -3,6 +3,8 @@ module github.com/makeworld-the-better-one/amfora
go 1.14
require (
code.rocketnine.space/tslocum/cview v1.5.4
github.com/atotto/clipboard v0.1.4
github.com/dustin/go-humanize v1.0.0
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gdamore/tcell/v2 v2.2.1-0.20210305060500-f4d402906fa3
@ -10,23 +12,18 @@ require (
github.com/makeworld-the-better-one/go-gemini v0.11.0
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.3.1 // indirect
github.com/mmcdole/gofeed v1.1.0
github.com/mmcdole/gofeed v1.1.2
github.com/pelletier/go-toml v1.8.0 // indirect
github.com/rkoesters/xdg v0.0.0-20181125232953-edd15b846f9b
github.com/schollz/progressbar/v3 v3.7.2
github.com/schollz/progressbar/v3 v3.8.0
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.6.1
gitlab.com/tslocum/cview v1.5.4-0.20210327214212-2d7324e8db4f
golang.org/x/text v0.3.5
golang.org/x/text v0.3.6
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
)
replace github.com/mmcdole/gofeed => github.com/makeworld-the-better-one/gofeed v1.1.1-0.20201123002655-c0c6354134fe
replace github.com/schollz/progressbar/v3 => github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20201220005701-b036c4d38568

44
go.sum
View File

@ -10,6 +10,10 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
code.rocketnine.space/tslocum/cbind v0.1.5 h1:i6NkeLLNPNMS4NWNi3302Ay3zSU6MrqOT+yJskiodxE=
code.rocketnine.space/tslocum/cbind v0.1.5/go.mod h1:LtfqJTzM7qhg88nAvNhx+VnTjZ0SXBJtxBObbfBWo/M=
code.rocketnine.space/tslocum/cview v1.5.4 h1:zBUFAanViudrAw8ZCqNxaufqrYL6a7F1AkkIClXIzYo=
code.rocketnine.space/tslocum/cview v1.5.4/go.mod h1:JjgoZi3b528SaV923oQq14PGUCTE/g/6iggnDUxsChE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@ -24,6 +28,8 @@ github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9Pq
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
@ -49,7 +55,7 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.0.0-dev/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
github.com/gdamore/tcell/v2 v2.2.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
github.com/gdamore/tcell/v2 v2.2.1-0.20210305060500-f4d402906fa3 h1:PqyGpJlv98ynqEPq5MMiT+hcUPPvIomSS0Rnmy5Tl9A=
github.com/gdamore/tcell/v2 v2.2.1-0.20210305060500-f4d402906fa3/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@ -134,17 +140,12 @@ github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzR
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/makeworld-the-better-one/go-gemini v0.11.0 h1:MNGiULJFvcqls9oCy40tE897hDeKvNmEK9i5kRucgQk=
github.com/makeworld-the-better-one/go-gemini v0.11.0/go.mod h1:F+3x+R1xeYK90jMtBq+U+8Sh64r2dHleDZ/en3YgSmg=
github.com/makeworld-the-better-one/gofeed v1.1.1-0.20201123002655-c0c6354134fe h1:i3b9Qy5z23DcXRnrsMYcM5s9Ng5VIidM1xZd+szuTsY=
github.com/makeworld-the-better-one/gofeed v1.1.1-0.20201123002655-c0c6354134fe/go.mod h1:QQO3maftbOu+hiVOGOZDRLymqGQCos4zxbA4j89gMrE=
github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20201220005701-b036c4d38568 h1:fod4pD+rsU73WIUxl8Kpo35LDuOx0uxzlprBKbm84vw=
github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20201220005701-b036c4d38568/go.mod h1:CG/f0JmacksUc6TkZToO7tVq4t03zIQSQUtTd7F9GR4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@ -160,6 +161,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.1 h1:cCBH2gTD2K0OtLlv/Y5H01VQCqmlDxz30kS5Y5bqfLA=
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mmcdole/gofeed v1.1.2 h1:7I5su6dO5/Rg2LEKS5ofPISVbi2vfxO2SNVSA/QN1y4=
github.com/mmcdole/gofeed v1.1.2/go.mod h1:QQO3maftbOu+hiVOGOZDRLymqGQCos4zxbA4j89gMrE=
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf h1:sWGE2v+hO0Nd4yFU/S/mDBM5plIU8v/Qhfz41hkDIAI=
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -197,6 +200,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/schollz/progressbar/v3 v3.8.0 h1:BKyefEMgFBDbo+JaeqHcm/9QdSj8qG8sUY+6UppGpnw=
github.com/schollz/progressbar/v3 v3.8.0/go.mod h1:Y9mmL2knZj3LUaBDyBEzFdPrymIr08hnlFMZmfxwbx4=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@ -231,10 +236,6 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
gitlab.com/tslocum/cbind v0.1.4 h1:cbZXPPcieXspk8cShoT6efz7HAT8yMNQcofYWNizis4=
gitlab.com/tslocum/cbind v0.1.4/go.mod h1:RvwYE3auSjBNlCmWeGspzn+jdLUVQ8C2QGC+0nP9ChI=
gitlab.com/tslocum/cview v1.5.4-0.20210327214212-2d7324e8db4f h1:tq1PwUdixNKqfAqJtaeVL7Ih5CXPs0xfi6iYIQzh5rA=
gitlab.com/tslocum/cview v1.5.4-0.20210327214212-2d7324e8db4f/go.mod h1:vsXBczVRvmjQZN3HZU0xlqXjkvnNldD9m3feKywrHcg=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@ -246,8 +247,8 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -306,24 +307,27 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201113135734-0a15ea8d9b02/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210326220804-49726bf1d181 h1:64ChN/hjER/taL4YJuA+gpLfIMT+/NFherRZixbxOhg=
golang.org/x/sys v0.0.0-20210326220804-49726bf1d181/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210223095934-7937bea0104d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54 h1:rF3Ohx8DRyl8h2zw9qojyLHLhrJpEMgyPOImREEryf0=
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 h1:EC6+IGYTjPpRfv9a2b/6Puw0W+hLtAhkV1tPsXhutqs=
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -11,9 +11,9 @@ import (
"strconv"
"strings"
"code.rocketnine.space/tslocum/cview"
"github.com/makeworld-the-better-one/amfora/config"
"github.com/spf13/viper"
"gitlab.com/tslocum/cview"
)
// Regex for identifying ANSI color codes
@ -234,6 +234,15 @@ func convertRegularGemini(s string, numLinks, width int, proxied bool) (string,
wrappedItem[0] = fmt.Sprintf(" [%s]\u2022", config.GetColorString("list_text")) +
wrappedItem[0] + "[-]"
wrappedLines = append(wrappedLines, wrappedItem...)
} else {
wrappedItem := wrapLine(lines[i][1:], width,
fmt.Sprintf(" [%s]", config.GetColorString("list_text")),
"[-]", false)
// Add "*"
wrappedItem[0] = fmt.Sprintf(" [%s]*", config.GetColorString("list_text")) +
wrappedItem[0] + "[-]"
wrappedLines = append(wrappedLines, wrappedItem...)
}
// Optionally list lines could be colored here too, if color is enabled
} else if strings.HasPrefix(lines[i], ">") {

View File

@ -8,7 +8,7 @@ Other projects are encouraged to copy this code if it's useful to them, and this
## License
If you prefer, you can consider the code in this package, and this package only, to be licensed under the MIT license instead.
If you prefer, you can consider the code in this package, and this package only, to be licensed under the MIT license instead. So the code in this package is dual-licensed. You can use the LICENSE file in the root of this repo, or the license text below.
<details>
<summary>Click to see MIT license terms</summary>