mirror of
https://github.com/makeworld-the-better-one/amfora.git
synced 2024-08-17 02:30:39 +03:00
parent
199d122990
commit
cfe58cb5f3
14
CHANGELOG.md
14
CHANGELOG.md
@ -6,12 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Support over 55 charsets (#3)
|
||||
- Add titles to error modals
|
||||
- **Bookmarks** (#10)
|
||||
- **Support over 55 charsets** (#3)
|
||||
- **Search using the bottom bar**
|
||||
- Add titles to all modals
|
||||
- Store ports in TOFU database (#7)
|
||||
- Search from bottom bar
|
||||
- Wrapping based on terminal width (#1)
|
||||
- `left_margin` config option
|
||||
- `left_margin` config option (#1)
|
||||
- Right margin for text (#1)
|
||||
- Desktop entry file
|
||||
- Option to continue anyway when cert doesn't match TOFU database
|
||||
@ -21,7 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Changed
|
||||
- Connection timeout is 15 seconds (was 5s)
|
||||
- Hash `SubjectPublicKeyInfo` for TOFU instead (#7)
|
||||
- `wrap_width` config option became `max_width`
|
||||
- `wrap_width` config option became `max_width` (#1)
|
||||
- Make the help table look better
|
||||
|
||||
### Removed
|
||||
- Opening multiple URLs from the command line (threading issues)
|
||||
@ -30,7 +33,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Reset bottom bar on error / invalid URL
|
||||
- Side scrolling doesn't cut off text on the left side (#1)
|
||||
- Mark status code 21 as invalid
|
||||
- You can't type on the bottom bar as it's loading
|
||||
- Bottom bar is not in focus after clicking Enter
|
||||
- Badly formed links on pages can no longer crash the browser
|
||||
|
||||
|
||||
## [1.0.0] - 2020-06-18
|
||||
|
@ -49,8 +49,8 @@ Features in *italics* are in the master branch, but not in the latest release.
|
||||
- [x] Basic forward/backward history, for each tab
|
||||
- [x] Input (Status Code 10 & 11)
|
||||
- [x] *Multiple charset support (over 55)*
|
||||
- [x] *Built-in search using GUS*
|
||||
- [ ] Bookmarks
|
||||
- [x] *Built-in search (uses GUS by default)*
|
||||
- [x] *Bookmarks*
|
||||
- [ ] Search in pages with <kbd>Ctrl-F</kbd>
|
||||
- [ ] Download pages and arbitrary data
|
||||
- [ ] Emoji favicons
|
||||
|
60
bookmarks/bookmarks.go
Normal file
60
bookmarks/bookmarks.go
Normal file
@ -0,0 +1,60 @@
|
||||
package bookmarks
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"strings"
|
||||
|
||||
"github.com/makeworld-the-better-one/amfora/config"
|
||||
)
|
||||
|
||||
var bkmkStore = config.BkmkStore
|
||||
|
||||
// bkmkKey returns the viper key for the given bookmark URL.
|
||||
// Note that URLs are the keys, NOT the bookmark name.
|
||||
func bkmkKey(url string) string {
|
||||
// Keys are base32 encoded URLs to prevent any bad chars like periods from being used
|
||||
return "bookmarks." + base32.StdEncoding.EncodeToString([]byte(url))
|
||||
}
|
||||
|
||||
func Set(url, name string) {
|
||||
bkmkStore.Set(bkmkKey(url), name)
|
||||
bkmkStore.WriteConfig()
|
||||
}
|
||||
|
||||
// Get returns the NAME of the bookmark, given the URL.
|
||||
// It also returns a bool indicating whether it exists.
|
||||
func Get(url string) (string, bool) {
|
||||
name := bkmkStore.GetString(bkmkKey(url))
|
||||
return name, name != ""
|
||||
}
|
||||
|
||||
func Remove(url string) {
|
||||
// XXX: Viper can't actually delete keys, which means the bookmarks file might get clouded
|
||||
// with non-entries over time.
|
||||
bkmkStore.Set(bkmkKey(url), "")
|
||||
bkmkStore.WriteConfig()
|
||||
}
|
||||
|
||||
// All returns all the bookmarks in a map of URLs to names.
|
||||
func All() map[string]string {
|
||||
ret := make(map[string]string)
|
||||
|
||||
bkmksMap, ok := bkmkStore.AllSettings()["bookmarks"].(map[string]interface{})
|
||||
if !ok {
|
||||
// No bookmarks stored yet, return empty map
|
||||
return ret
|
||||
}
|
||||
for b32Url, name := range bkmksMap {
|
||||
if n, ok := name.(string); n == "" || !ok {
|
||||
// name is not a string, or it's empty - ignore
|
||||
continue
|
||||
}
|
||||
url, err := base32.StdEncoding.DecodeString(strings.ToUpper(b32Url))
|
||||
if err != nil {
|
||||
// This would only happen if a user messed around with the bookmarks file
|
||||
continue
|
||||
}
|
||||
ret[string(url)] = name.(string)
|
||||
}
|
||||
return ret
|
||||
}
|
5
cache/cache.go
vendored
5
cache/cache.go
vendored
@ -4,6 +4,7 @@ package cache
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/makeworld-the-better-one/amfora/structs"
|
||||
@ -47,8 +48,8 @@ func removeUrl(url string) {
|
||||
// If your page is larger than the max cache size, the provided page
|
||||
// will silently not be added to the cache.
|
||||
func Add(p *structs.Page) {
|
||||
if p.Url == "" {
|
||||
// Just in case, don't waste cache on new tab page
|
||||
if p.Url == "" || strings.HasPrefix(p.Url, "about:") {
|
||||
// Just in case, these pages shouldn't be cached
|
||||
return
|
||||
}
|
||||
// Never cache pages with query strings, to reduce unexpected behaviour
|
||||
|
@ -19,12 +19,17 @@ var TofuStore = viper.New()
|
||||
var tofuDBDir string
|
||||
var tofuDBPath string
|
||||
|
||||
// Bookmarks
|
||||
var BkmkStore = viper.New()
|
||||
var bkmkDir string
|
||||
var bkmkPath string
|
||||
|
||||
func Init() error {
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Cache AppData path
|
||||
// Store AppData path
|
||||
if runtime.GOOS == "windows" {
|
||||
appdata, ok := os.LookupEnv("APPDATA")
|
||||
if ok {
|
||||
@ -33,7 +38,8 @@ func Init() error {
|
||||
amforaAppData = filepath.Join(home, filepath.FromSlash("AppData/Roaming/amfora/"))
|
||||
}
|
||||
}
|
||||
// Cache config directory and file paths
|
||||
|
||||
// Store config directory and file paths
|
||||
if runtime.GOOS == "windows" {
|
||||
configDir = amforaAppData
|
||||
} else {
|
||||
@ -48,7 +54,7 @@ func Init() error {
|
||||
}
|
||||
configPath = filepath.Join(configDir, "config.toml")
|
||||
|
||||
// Cache TOFU db directory and file paths
|
||||
// Store TOFU db directory and file paths
|
||||
if runtime.GOOS == "windows" {
|
||||
// Windows just stores it in APPDATA along with other stuff
|
||||
tofuDBDir = amforaAppData
|
||||
@ -64,6 +70,25 @@ func Init() error {
|
||||
}
|
||||
tofuDBPath = filepath.Join(tofuDBDir, "tofu.toml")
|
||||
|
||||
// Store bookmarks dir and path
|
||||
if runtime.GOOS == "windows" {
|
||||
// Windows just keeps it in APPDATA along with other Amfora files
|
||||
bkmkDir = amforaAppData
|
||||
} else {
|
||||
// XDG data dir on POSIX systems
|
||||
xdg_data, ok := os.LookupEnv("XDG_DATA_HOME")
|
||||
if ok && strings.TrimSpace(xdg_data) != "" {
|
||||
bkmkDir = filepath.Join(xdg_data, "amfora")
|
||||
} else {
|
||||
// Default to ~/.local/share/amfora
|
||||
bkmkDir = filepath.Join(home, ".local", "share", "amfora")
|
||||
}
|
||||
}
|
||||
bkmkPath = filepath.Join(bkmkDir, "bookmarks.toml")
|
||||
|
||||
// Create necessary files and folders
|
||||
|
||||
// Config
|
||||
err = os.MkdirAll(configDir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -78,12 +103,20 @@ func Init() error {
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
|
||||
// TOFU
|
||||
err = os.MkdirAll(tofuDBDir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.OpenFile(tofuDBPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
|
||||
// Bookmarks
|
||||
err = os.MkdirAll(bkmkDir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.OpenFile(bkmkPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
|
||||
|
||||
// Setup vipers
|
||||
|
||||
TofuStore.SetConfigFile(tofuDBPath)
|
||||
TofuStore.SetConfigType("toml")
|
||||
@ -92,6 +125,18 @@ func Init() error {
|
||||
return err
|
||||
}
|
||||
|
||||
BkmkStore.SetConfigFile(bkmkPath)
|
||||
BkmkStore.SetConfigType("toml")
|
||||
err = BkmkStore.ReadInConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
BkmkStore.Set("DO NOT TOUCH", true)
|
||||
err = BkmkStore.WriteConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
viper.SetDefault("a-general.home", "gemini.circumlunar.space")
|
||||
viper.SetDefault("a-general.http", "default")
|
||||
viper.SetDefault("a-general.search", "gus.guru/search")
|
||||
|
@ -9,7 +9,7 @@ var defaultConf = []byte(`# This is the default config file.
|
||||
# gemini://example.com
|
||||
# //example.com
|
||||
# example.com
|
||||
# example.com:1901
|
||||
# example.com:123
|
||||
|
||||
[a-general]
|
||||
home = "gemini://gemini.circumlunar.space"
|
||||
@ -23,16 +23,10 @@ http = "default"
|
||||
search = "gemini://gus.guru/search" # Any URL that will accept a query string can be put here
|
||||
color = true # Whether colors will be used in the terminal
|
||||
bullets = true # Whether to replace list asterisks with unicode bullets
|
||||
|
||||
# A number from 0 to 1, indicating what percentage of the terminal width the left margin should take up.
|
||||
left_margin = 0.15
|
||||
max_width = 100 # The max number of columns to wrap a page's text to. Preformatted blocks are not wrapped.
|
||||
|
||||
[bookmarks]
|
||||
# Make sure to quote the key names if you edit this part yourself
|
||||
# Example:
|
||||
# "CAPCOM" = "gemini://gemini.circumlunar.space/capcom/"
|
||||
|
||||
# Options for page cache - which is only for text/gemini pages
|
||||
# Increase the cache size to speed up browsing at the expense of memory
|
||||
[cache]
|
||||
|
@ -6,7 +6,7 @@
|
||||
# gemini://example.com
|
||||
# //example.com
|
||||
# example.com
|
||||
# example.com:1901
|
||||
# example.com:123
|
||||
|
||||
[a-general]
|
||||
home = "gemini://gemini.circumlunar.space"
|
||||
@ -20,16 +20,10 @@ http = "default"
|
||||
search = "gemini://gus.guru/search" # Any URL that will accept a query string can be put here
|
||||
color = true # Whether colors will be used in the terminal
|
||||
bullets = true # Whether to replace list asterisks with unicode bullets
|
||||
|
||||
# A number from 0 to 1, indicating what percentage of the terminal width the left margin should take up.
|
||||
left_margin = 0.15
|
||||
max_width = 100 # The max number of columns to wrap a page's text to. Preformatted blocks are not wrapped.
|
||||
|
||||
[bookmarks]
|
||||
# Make sure to quote the key names if you edit this part yourself
|
||||
# Example:
|
||||
# "CAPCOM" = "gemini://gemini.circumlunar.space/capcom/"
|
||||
|
||||
# Options for page cache - which is only for text/gemini pages
|
||||
# Increase the cache size to speed up browsing at the expense of memory
|
||||
[cache]
|
||||
|
120
display/bookmarks.go
Normal file
120
display/bookmarks.go
Normal file
@ -0,0 +1,120 @@
|
||||
package display
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/makeworld-the-better-one/amfora/renderer"
|
||||
"github.com/makeworld-the-better-one/amfora/structs"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/makeworld-the-better-one/amfora/bookmarks"
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
|
||||
// For adding and removing bookmarks, basically a clone of the input modal.
|
||||
var bkmkModal = cview.NewModal().
|
||||
SetBackgroundColor(tcell.ColorTeal).
|
||||
SetButtonBackgroundColor(tcell.ColorNavy).
|
||||
SetButtonTextColor(tcell.ColorWhite).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
|
||||
// bkmkCh is for the user action
|
||||
var bkmkCh = make(chan int) // 1, 0, -1 for add/update, cancel, and remove
|
||||
var bkmkModalText string // The current text of the input field in the modal
|
||||
|
||||
func bkmkInit() {
|
||||
bkmkModal.SetBorder(true)
|
||||
bkmkModal.SetBorderColor(tcell.ColorWhite)
|
||||
bkmkModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
switch buttonLabel {
|
||||
case "Add":
|
||||
bkmkCh <- 1
|
||||
case "Change":
|
||||
bkmkCh <- 1
|
||||
case "Remove":
|
||||
bkmkCh <- -1
|
||||
case "Cancel":
|
||||
bkmkCh <- 0
|
||||
}
|
||||
|
||||
//tabPages.SwitchToPage(strconv.Itoa(curTab)) - handled in bkmk()
|
||||
})
|
||||
bkmkModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
bkmkModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
bkmkModal.GetFrame().SetTitle(" Add Bookmark ")
|
||||
}
|
||||
|
||||
// Bkmk displays the "Add a bookmark" modal.
|
||||
// It accepts the default value for the bookmark name that will be displayed, but can be changed by the user.
|
||||
// It also accepts a bool indicating whether this page already has a bookmark.
|
||||
// It returns the bookmark name and the bookmark action:
|
||||
// 1, 0, -1 for add/update, cancel, and remove
|
||||
func openBkmkModal(name string, exists bool) (string, int) {
|
||||
// Basically a copy of Input()
|
||||
|
||||
// Remove and re-add input field - to clear the old text
|
||||
if bkmkModal.GetForm().GetFormItemCount() > 0 {
|
||||
bkmkModal.GetForm().RemoveFormItem(0)
|
||||
}
|
||||
bkmkModalText = ""
|
||||
bkmkModal.GetForm().AddInputField("Name: ", name, 0, nil,
|
||||
func(text string) {
|
||||
// Store for use later
|
||||
bkmkModalText = text
|
||||
})
|
||||
|
||||
bkmkModal.ClearButtons()
|
||||
if exists {
|
||||
bkmkModal.SetText("Change or remove the bookmark for the current page?")
|
||||
bkmkModal.AddButtons([]string{"Change", "Remove", "Cancel"})
|
||||
} else {
|
||||
bkmkModal.SetText("Create a bookmark for the current page?")
|
||||
bkmkModal.AddButtons([]string{"Add", "Cancel"})
|
||||
}
|
||||
tabPages.ShowPage("bkmk")
|
||||
tabPages.SendToFront("bkmk")
|
||||
App.SetFocus(bkmkModal)
|
||||
App.Draw()
|
||||
|
||||
action := <-bkmkCh
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
|
||||
return bkmkModalText, action
|
||||
}
|
||||
|
||||
// Bookmarks displays the bookmarks page on the current tab.
|
||||
func Bookmarks() {
|
||||
// Gather bookmarks
|
||||
rawContent := "# Bookmarks\r\n\r\n"
|
||||
for url, name := range bookmarks.All() {
|
||||
rawContent += fmt.Sprintf("=> %s %s\r\n", url, name)
|
||||
}
|
||||
// Render and display
|
||||
content, links := renderer.RenderGemini(rawContent, textWidth())
|
||||
page := structs.Page{Content: content, Links: links, Url: "about:bookmarks"}
|
||||
setPage(&page)
|
||||
}
|
||||
|
||||
// addBookmark goes through the process of adding a bookmark for the current page.
|
||||
// It is the high-level way of doing it. It should be called in a goroutine.
|
||||
// It can also be called to edit an existing bookmark.
|
||||
func addBookmark() {
|
||||
if !strings.HasPrefix(tabMap[curTab].Url, "gemini://") {
|
||||
// Can't make bookmarks for other kinds of URLs
|
||||
return
|
||||
}
|
||||
|
||||
name, exists := bookmarks.Get(tabMap[curTab].Url)
|
||||
// Open a bookmark modal with the current name of the bookmark, if it exists
|
||||
newName, action := openBkmkModal(name, exists)
|
||||
switch action {
|
||||
case 1:
|
||||
// Add/change the bookmark
|
||||
bookmarks.Set(tabMap[curTab].Url, newName)
|
||||
case -1:
|
||||
bookmarks.Remove(tabMap[curTab].Url)
|
||||
}
|
||||
// Other case is action = 0, meaning "Cancel", so nothing needs to happen
|
||||
}
|
@ -5,13 +5,11 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/makeworld-the-better-one/amfora/renderer"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/makeworld-the-better-one/amfora/cache"
|
||||
"github.com/makeworld-the-better-one/amfora/renderer"
|
||||
"github.com/makeworld-the-better-one/amfora/structs"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
|
||||
@ -28,24 +26,13 @@ var bottomBar = cview.NewInputField().
|
||||
SetFieldTextColor(tcell.ColorBlack).
|
||||
SetLabelColor(tcell.ColorGreen)
|
||||
|
||||
var helpTable = cview.NewTable().
|
||||
SetSelectable(false, false).
|
||||
SetFixed(1, 2).
|
||||
SetBorders(true).
|
||||
SetBordersColor(tcell.ColorGray)
|
||||
|
||||
// Viewer for the tab primitives
|
||||
// Pages are named as strings of tab numbers - so the textview for the first tab
|
||||
// is held in the page named "0".
|
||||
// The only pages that don't confine to this scheme named after the modals above,
|
||||
// which is used to draw modals on top the current tab.
|
||||
// The only pages that don't confine to this scheme are those named after modals,
|
||||
// which are used to draw modals on top the current tab.
|
||||
// Ex: "info", "error", "input", "yesno"
|
||||
var tabPages = cview.NewPages().
|
||||
AddPage("help", helpTable, true, false).
|
||||
AddPage("info", infoModal, false, false).
|
||||
AddPage("error", errorModal, false, false).
|
||||
AddPage("input", inputModal, false, false).
|
||||
AddPage("yesno", yesNoModal, false, false)
|
||||
var tabPages = cview.NewPages()
|
||||
|
||||
// The tabs at the top with titles
|
||||
var tabRow = cview.NewTextView().
|
||||
@ -116,15 +103,17 @@ func Init() {
|
||||
if c == 0 {
|
||||
tableCell = cview.NewTableCell(cells[cell]).
|
||||
SetAttributes(tcell.AttrBold).
|
||||
SetExpansion(1)
|
||||
SetExpansion(1).
|
||||
SetAlign(cview.AlignCenter)
|
||||
} else {
|
||||
tableCell = cview.NewTableCell(cells[cell]).
|
||||
tableCell = cview.NewTableCell(" " + cells[cell]).
|
||||
SetExpansion(2)
|
||||
}
|
||||
helpTable.SetCell(r, c, tableCell)
|
||||
cell++
|
||||
}
|
||||
}
|
||||
tabPages.AddPage("help", helpTable, true, false)
|
||||
|
||||
bottomBar.SetBackgroundColor(tcell.ColorWhite)
|
||||
bottomBar.SetDoneFunc(func(key tcell.Key) {
|
||||
@ -147,7 +136,7 @@ func Init() {
|
||||
if err != nil {
|
||||
// It's a full URL or search term
|
||||
// Detect if it's a search or URL
|
||||
if strings.Contains(query, " ") || (!strings.Contains(query, "//") && !strings.Contains(query, ".")) {
|
||||
if strings.Contains(query, " ") || (!strings.Contains(query, "//") && !strings.Contains(query, ".") && !strings.HasPrefix(query, "about:")) {
|
||||
URL(viper.GetString("a-general.search") + "?" + pathEscape(query))
|
||||
} else {
|
||||
// Full URL
|
||||
@ -178,7 +167,7 @@ func Init() {
|
||||
|
||||
// Render the default new tab content ONCE and store it for later
|
||||
renderedNewTabContent, newTabLinks = renderer.RenderGemini(newTabContent, textWidth())
|
||||
newTabPage = structs.Page{Content: renderedNewTabContent, Links: newTabLinks}
|
||||
newTabPage = structs.Page{Content: renderedNewTabContent, Links: newTabLinks, Url: "about:newtab"}
|
||||
|
||||
modalInit()
|
||||
|
||||
@ -212,6 +201,13 @@ func Init() {
|
||||
case tcell.KeyCtrlQ:
|
||||
Stop()
|
||||
return nil
|
||||
case tcell.KeyCtrlB:
|
||||
Bookmarks()
|
||||
addToHist("about:bookmarks")
|
||||
return nil
|
||||
case tcell.KeyCtrlD:
|
||||
go addBookmark()
|
||||
return nil
|
||||
case tcell.KeyRune:
|
||||
// Regular key was sent
|
||||
switch string(event.Rune()) {
|
||||
@ -422,6 +418,22 @@ func Reload() {
|
||||
// URL loads and handles the provided URL for the current tab.
|
||||
// It should be an absolute URL.
|
||||
func URL(u string) {
|
||||
// Some code is copied in followLink()
|
||||
|
||||
if u == "about:bookmarks" {
|
||||
Bookmarks()
|
||||
addToHist("about:bookmarks")
|
||||
return
|
||||
}
|
||||
if u == "about:newtab" {
|
||||
setPage(&newTabPage)
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(u, "about:") {
|
||||
Error("Error", "Not a valid 'about:' URL.")
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
final, displayed := handleURL(u)
|
||||
if displayed {
|
||||
@ -433,10 +445,3 @@ func URL(u string) {
|
||||
func NumTabs() int {
|
||||
return len(tabViews)
|
||||
}
|
||||
|
||||
// Help displays the help and keybindings.
|
||||
func Help() {
|
||||
helpTable.ScrollToBeginning()
|
||||
tabPages.SwitchToPage("help")
|
||||
App.Draw()
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
package display
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
|
||||
var helpCells = strings.TrimSpace(`
|
||||
?|Bring up this help.
|
||||
@ -8,17 +13,32 @@ Esc|Leave the help
|
||||
Arrow keys, h/j/k/l|Scroll and move a page.
|
||||
Tab|Navigate to the next item in a popup.
|
||||
Shift-Tab|Navigate to the previous item in a popup.
|
||||
Ctrl-H|Go home
|
||||
Ctrl-T|New tab
|
||||
Ctrl-W|Close tab. For now, only the right-most tab can be closed.
|
||||
b|Go back a page
|
||||
f|Go forward a page
|
||||
g|Go to top of document
|
||||
G|Go to bottom of document
|
||||
spacebar|Open bar at the bottom - type a URL or link number
|
||||
Enter|On a page this will start link highlighting. Press Tab and Shift-Tab to pick different links. Press enter again to go to one.
|
||||
Ctrl-R, R|Reload a page. This also clears the cache.
|
||||
q, Ctrl-Q|Quit
|
||||
Shift-NUMBER|Go to a specific tab.
|
||||
Shift-0, )|Go to the last tab.
|
||||
Ctrl-H|Go home
|
||||
Ctrl-T|New tab
|
||||
Ctrl-W|Close tab. For now, only the right-most tab can be closed.
|
||||
Ctrl-R, R|Reload a page, discarding the cached version.
|
||||
Ctrl-B|View bookmarks
|
||||
Ctrl-D|Add, change, or remove a bookmark for the current page.
|
||||
q, Ctrl-Q|Quit
|
||||
`)
|
||||
|
||||
var helpTable = cview.NewTable().
|
||||
SetSelectable(false, false).
|
||||
SetFixed(1, 2).
|
||||
SetBorders(true).
|
||||
SetBordersColor(tcell.ColorGray)
|
||||
|
||||
// Help displays the help and keybindings.
|
||||
func Help() {
|
||||
helpTable.ScrollToBeginning()
|
||||
tabPages.SwitchToPage("help")
|
||||
App.Draw()
|
||||
}
|
||||
|
@ -9,7 +9,8 @@ import (
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
|
||||
// This file contains code for all the popups / modals used in the display
|
||||
// This file contains code for the popups / modals used in the display.
|
||||
// The bookmark modal is in bookmarks.go
|
||||
|
||||
var infoModal = cview.NewModal().
|
||||
SetBackgroundColor(tcell.ColorGray).
|
||||
@ -45,20 +46,30 @@ var yesNoModal = cview.NewModal().
|
||||
var yesNoCh = make(chan bool)
|
||||
|
||||
func modalInit() {
|
||||
tabPages.AddPage("info", infoModal, false, false).
|
||||
AddPage("error", errorModal, false, false).
|
||||
AddPage("input", inputModal, false, false).
|
||||
AddPage("yesno", yesNoModal, false, false).
|
||||
AddPage("bkmk", bkmkModal, false, false)
|
||||
|
||||
// Modal functions that can't be added up above, because they return the wrong type
|
||||
|
||||
infoModal.SetBorder(true)
|
||||
infoModal.SetBorderColor(tcell.ColorWhite)
|
||||
infoModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
})
|
||||
infoModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
infoModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
infoModal.GetFrame().SetTitle(" Info ")
|
||||
|
||||
errorModal.SetBorder(true)
|
||||
errorModal.SetBorderColor(tcell.ColorWhite)
|
||||
errorModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
errorModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
errorModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
})
|
||||
errorModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
errorModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
|
||||
inputModal.SetBorder(true)
|
||||
inputModal.SetBorderColor(tcell.ColorWhite)
|
||||
@ -72,6 +83,9 @@ func modalInit() {
|
||||
|
||||
//tabPages.SwitchToPage(strconv.Itoa(curTab)) - handled in Input()
|
||||
})
|
||||
inputModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
inputModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
inputModal.GetFrame().SetTitle(" Input ")
|
||||
|
||||
yesNoModal.SetBorder(true)
|
||||
yesNoModal.SetBorderColor(tcell.ColorWhite)
|
||||
@ -84,6 +98,10 @@ func modalInit() {
|
||||
|
||||
//tabPages.SwitchToPage(strconv.Itoa(curTab)) - Handled in YesNo()
|
||||
})
|
||||
yesNoModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
yesNoModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
|
||||
bkmkInit()
|
||||
}
|
||||
|
||||
// Error displays an error on the screen in a modal.
|
||||
@ -157,7 +175,7 @@ func YesNo(prompt string) bool {
|
||||
// Tofu displays the TOFU warning modal.
|
||||
// It returns a bool indicating whether the user wants to continue.
|
||||
func Tofu(host string) bool {
|
||||
// Reuses yesno modal, with error colour
|
||||
// Reuses yesNoModal, with error colour
|
||||
|
||||
yesNoModal.SetBackgroundColor(tcell.ColorMaroon)
|
||||
yesNoModal.SetText(
|
||||
|
@ -8,6 +8,8 @@ Press the ? key at any time to bring up the help, and see other keybindings. Mos
|
||||
|
||||
Happy browsing!
|
||||
|
||||
=> //gemini.circumlunar.space Gemini homepage
|
||||
=> about:bookmarks Bookmarks
|
||||
|
||||
=> //gemini.circumlunar.space Project Gemini
|
||||
=> https://github.com/makeworld-the-better-one/amfora Amfora homepage [HTTPS]
|
||||
`
|
||||
|
@ -61,6 +61,10 @@ func tabHasContent() bool {
|
||||
// Likely the default content page
|
||||
return false
|
||||
}
|
||||
if strings.HasPrefix(tabMap[curTab].Url, "about:") {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := tabMap[curTab]
|
||||
return ok // If there's a page, return true
|
||||
}
|
||||
@ -84,16 +88,44 @@ func applyScroll() {
|
||||
// followLink should be used when the user "clicks" a link on a page.
|
||||
// Not when a URL is opened on a new tab for the first time.
|
||||
func followLink(prev, next string) {
|
||||
saveScroll() // Likely called later on anyway, here just in case
|
||||
prevParsed, _ := url.Parse(prev)
|
||||
nextParsed, err := url.Parse(next)
|
||||
|
||||
// Copied from URL()
|
||||
if next == "about:bookmarks" {
|
||||
Bookmarks()
|
||||
addToHist("about:bookmarks")
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(next, "about:") {
|
||||
Error("Error", "Not a valid 'about:' URL for linking")
|
||||
return
|
||||
}
|
||||
|
||||
if tabHasContent() {
|
||||
saveScroll() // Likely called later on anyway, here just in case
|
||||
prevParsed, _ := url.Parse(prev)
|
||||
nextParsed, err := url.Parse(next)
|
||||
if err != nil {
|
||||
Error("URL Error", "Link URL could not be parsed")
|
||||
return
|
||||
}
|
||||
nextURL := prevParsed.ResolveReference(nextParsed).String()
|
||||
go func() {
|
||||
final, displayed := handleURL(nextURL)
|
||||
if displayed {
|
||||
addToHist(final)
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
// No content on current tab, so the "prev" URL is not valid.
|
||||
// An example is the about:newtab page
|
||||
_, err := url.Parse(next)
|
||||
if err != nil {
|
||||
Error("URL Error", "Link URL could not be parsed")
|
||||
return
|
||||
}
|
||||
nextURL := prevParsed.ResolveReference(nextParsed).String()
|
||||
go func() {
|
||||
final, displayed := handleURL(nextURL)
|
||||
final, displayed := handleURL(next)
|
||||
if displayed {
|
||||
addToHist(final)
|
||||
}
|
||||
@ -112,15 +144,9 @@ func addLeftMargin(text string) string {
|
||||
func setPage(p *structs.Page) {
|
||||
saveScroll() // Save the scroll of the previous page
|
||||
|
||||
if !p.Displayable {
|
||||
// Add margin to page based on terminal width
|
||||
p.Content = addLeftMargin(p.Content)
|
||||
p.Displayable = true
|
||||
}
|
||||
|
||||
// Change page on screen
|
||||
tabMap[curTab] = p
|
||||
tabViews[curTab].SetText(p.Content)
|
||||
tabViews[curTab].SetText(addLeftMargin(p.Content))
|
||||
tabViews[curTab].Highlight("") // Turn off highlights
|
||||
tabViews[curTab].ScrollToBeginning()
|
||||
|
||||
@ -144,12 +170,14 @@ func handleURL(u string) (string, bool) {
|
||||
|
||||
App.SetFocus(tabViews[curTab])
|
||||
|
||||
//logger.Log.Printf("Sent: %s", u)
|
||||
// To allow linking to the bookmarks page, and history browsing
|
||||
if u == "about:bookmarks" {
|
||||
Bookmarks()
|
||||
return "about:bookmarks", true
|
||||
}
|
||||
|
||||
u = normalizeURL(u)
|
||||
|
||||
//logger.Log.Printf("Normalized: %s", u)
|
||||
|
||||
parsed, err := url.Parse(u)
|
||||
if err != nil {
|
||||
Error("URL Error", err.Error())
|
||||
|
16
go.sum
16
go.sum
@ -32,20 +32,17 @@ github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
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 v1.3.0 h1:r35w0JBADPZCVQijYebl6YMWWtHRqVEGt7kL2eBADRM=
|
||||
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
|
||||
github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606 h1:Y00kKKKYVyn7InlCMRcnZbwcjHFIsgkjU0Bn1F5re4o=
|
||||
github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=
|
||||
@ -143,7 +140,6 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
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=
|
||||
@ -152,7 +148,6 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
|
||||
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
|
||||
@ -183,25 +178,20 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
@ -279,7 +269,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
|
||||
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=
|
||||
@ -288,7 +277,6 @@ golang.org/x/sys v0.0.0-20200610111108-226ff32320da h1:bGb80FudwxpeucJUjPYJXuJ8H
|
||||
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@ -334,23 +322,19 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
|
||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200603094226-e3079894b1e8 h1:jL/vaozO53FMfZLySWM+4nulF3gQEC6q5jH90LPomDo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200603094226-e3079894b1e8/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -71,7 +71,8 @@ func convertRegularGemini(s string, numLinks int, width int) (string, []string)
|
||||
links = append(links, url)
|
||||
|
||||
if viper.GetBool("a-general.color") {
|
||||
if pU, _ := urlPkg.Parse(url); pU.Scheme == "" || pU.Scheme == "gemini" {
|
||||
pU, err := urlPkg.Parse(url)
|
||||
if err == nil && (pU.Scheme == "" || pU.Scheme == "gemini" || pU.Scheme == "about") {
|
||||
// A gemini link
|
||||
// Add the link text in blue (in a region), and a gray link number to the left of it
|
||||
lines[i] = `[silver::b][` + strconv.Itoa(numLinks+len(links)) + "[]" + "[-::-] " +
|
||||
|
@ -2,12 +2,12 @@ package structs
|
||||
|
||||
// Page is for storing UTF-8 text/gemini pages, as well as text/plain pages.
|
||||
type Page struct {
|
||||
Url string
|
||||
Content string // The processed content, NOT raw. Uses cview colour tags. All link/link texts must have region tags.
|
||||
Links []string // URLs, for each region in the content.
|
||||
Row int // Scroll position
|
||||
Column int // ditto
|
||||
Displayable bool // Set to true once the content has been modified to display nicely on the screen - margins added
|
||||
Url string
|
||||
Content string // The processed content, NOT raw. Uses cview colour tags. All link/link texts must have region tags.
|
||||
Links []string // URLs, for each region in the content.
|
||||
Row int // Scroll position
|
||||
Column int // ditto
|
||||
//Displayable bool // Set to true once the content has been modified to display nicely on the screen - margins added
|
||||
}
|
||||
|
||||
// Size returns an approx. size of a Page in bytes.
|
||||
|
Loading…
Reference in New Issue
Block a user