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:
commit
e3f062e75e
10
CHANGELOG.md
10
CHANGELOG.md
@ -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
|
||||
|
5
Makefile
5
Makefile
@ -32,6 +32,11 @@ uninstall:
|
||||
$(RM) -f $(PREFIX)/share/applications/amfora.desktop
|
||||
|
||||
# Development helpers
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
$(GO) fmt ./...
|
||||
|
||||
.PHONY: gen
|
||||
gen:
|
||||
$(GO) generate ./...
|
||||
|
10
NOTES.md
10
NOTES.md
@ -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
|
||||
|
16
README.md
16
README.md
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
26
cache/page.go
vendored
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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)**.
|
||||

|
||||
</details>
|
||||
|
||||
## Atelier Forest
|
||||
|
||||
Contributed by **[@joyalicegu](https://github.com/joyalicegu)**.
|
||||
|
||||
### Dark
|
||||
|
||||

|
||||
|
||||
### Light
|
||||
|
||||

|
||||
|
||||
|
||||
## Slimey
|
||||
|
||||
Contributed by **[@lee2sman](https://github.com/lee2sman)**.
|
||||
|
||||

|
||||
|
||||
## Gruvbox Dark
|
||||
|
||||
Contributed by **[@thumb](https://github.com/thumbfighter)**.
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>More screenshots</summary>
|
||||
|
||||

|
||||

|
||||

|
||||
</details>
|
||||
|
||||
## Yours?
|
||||
|
||||
Contribute your own theme by opening a PR.
|
||||
|
52
contrib/themes/atelier-forest-light.toml
Normal file
52
contrib/themes/atelier-forest-light.toml
Normal 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"
|
52
contrib/themes/atelier-forest.toml
Normal file
52
contrib/themes/atelier-forest.toml
Normal 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"
|
52
contrib/themes/gruvbox_dark.toml
Normal file
52
contrib/themes/gruvbox_dark.toml
Normal 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"
|
82
contrib/themes/slimey.toml
Normal file
82
contrib/themes/slimey.toml
Normal 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
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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.
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
}
|
||||
|
165
display/tab.go
165
display/tab.go
@ -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 ""
|
||||
}
|
||||
|
@ -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)
|
||||
`)
|
||||
|
@ -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
13
go.mod
@ -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
44
go.sum
@ -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=
|
||||
|
@ -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], ">") {
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user