mirror of
https://github.com/makeworld-the-better-one/amfora.git
synced 2024-08-16 18:20:50 +03:00
✨ Emoji favicon support - fixes #62
This commit is contained in:
parent
edd128e7c5
commit
0bc5939600
@ -7,11 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Emoji favicons can now be seen if `emoji_favicons` is enabled in the config (#62)
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
- Two digit (and higher) link texts are now in line with one digit ones (#60)
|
||||
- Race condition when reloading pages, could have caused the cache to still be used
|
||||
|
||||
|
||||
## [1.4.0] - 2020-07-28
|
||||
|
@ -97,7 +97,7 @@ Features in *italics* are in the master branch, but not in the latest release.
|
||||
- [x] Theming
|
||||
- [ ] Subscribe to RSS and Atom feeds and display them
|
||||
- Subscribing to page changes, similar to how Spacewalk works, will also be supported
|
||||
- [ ] Emoji favicons
|
||||
- [x] *Emoji favicons*
|
||||
- See `gemini://mozz.us/files/rfc_gemini_favicon.gmi` for details
|
||||
- [ ] Stream support
|
||||
- [ ] Full client certificate UX within the client
|
||||
|
58
cache/favicons.go
vendored
Normal file
58
cache/favicons.go
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Functions for caching emoji favicons.
|
||||
// See gemini://mozz.us/files/rfc_gemini_favicon.gmi for details.
|
||||
|
||||
var favicons = make(map[string]string) // domain to emoji
|
||||
var favMu = sync.RWMutex{}
|
||||
|
||||
var KnownNoFavicon = "no"
|
||||
|
||||
// AddFavicon will add an emoji to the cache under that host.
|
||||
// It does not verify that the string passed is actually an emoji.
|
||||
// You can pass KnownNoFavicon as the emoji when a host doesn't have a valid favicon.
|
||||
func AddFavicon(host, emoji string) {
|
||||
favMu.Lock()
|
||||
favicons[host] = emoji
|
||||
favMu.Unlock()
|
||||
}
|
||||
|
||||
// ClearFavicons removes all favicons from the cache
|
||||
func ClearFavicons() {
|
||||
favMu.Lock()
|
||||
favicons = make(map[string]string)
|
||||
favMu.Unlock()
|
||||
}
|
||||
|
||||
// GetFavicon returns the favicon string for the host.
|
||||
// It returns an empty string if there is no favicon cached.
|
||||
// It might also return KnownNoFavicon to indicate that that host does not have
|
||||
// a favicon at all.
|
||||
func GetFavicon(host string) string {
|
||||
favMu.RLock()
|
||||
defer favMu.RUnlock()
|
||||
return favicons[host]
|
||||
}
|
||||
|
||||
func NumFavicons() int {
|
||||
favMu.RLock()
|
||||
defer favMu.RUnlock()
|
||||
return len(favicons)
|
||||
}
|
||||
|
||||
func RemoveFavicon(host string) {
|
||||
favMu.Lock()
|
||||
delete(favicons, host)
|
||||
favMu.Unlock()
|
||||
}
|
||||
|
||||
func AllFavicons() string {
|
||||
favMu.RLock()
|
||||
defer favMu.RUnlock()
|
||||
return fmt.Sprintf("%v", favicons)
|
||||
}
|
2
cache/redir.go
vendored
2
cache/redir.go
vendored
@ -31,8 +31,8 @@ func AddRedir(og, redir string) {
|
||||
// ClearRedirs removes all redirects from the cache.
|
||||
func ClearRedirs() {
|
||||
redirMu.Lock()
|
||||
defer redirMu.Unlock()
|
||||
redirUrls = make(map[string]string)
|
||||
redirMu.Unlock()
|
||||
}
|
||||
|
||||
// Redirect takes the provided URL and returns a redirected version, if a redirect
|
||||
|
@ -153,6 +153,7 @@ func Init() error {
|
||||
viper.SetDefault("a-general.downloads", "")
|
||||
viper.SetDefault("a-general.page_max_size", 2097152)
|
||||
viper.SetDefault("a-general.page_max_time", 10)
|
||||
viper.SetDefault("a-general.emoji_favicons", false)
|
||||
viper.SetDefault("cache.max_size", 0)
|
||||
viper.SetDefault("cache.max_pages", 20)
|
||||
|
||||
|
@ -12,6 +12,7 @@ var defaultConf = []byte(`# This is the default config file.
|
||||
# example.com:123
|
||||
|
||||
[a-general]
|
||||
# Press Ctrl-H to access it
|
||||
home = "gemini://gemini.circumlunar.space"
|
||||
|
||||
# What command to run to open a HTTP URL. Set to "default" to try to guess the browser,
|
||||
@ -19,21 +20,36 @@ home = "gemini://gemini.circumlunar.space"
|
||||
# If a command is set, than the URL will be added (in quotes) to the end of the command.
|
||||
# A space will be prepended if necessary.
|
||||
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
|
||||
|
||||
# Any URL that will accept a query string can be put here
|
||||
search = "gemini://gus.guru/search"
|
||||
|
||||
# Whether colors will be used in the terminal
|
||||
color = true
|
||||
|
||||
# Whether to replace list asterisks with unicode bullets
|
||||
bullets = true
|
||||
|
||||
# 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.
|
||||
|
||||
# The max number of columns to wrap a page's text to. Preformatted blocks are not wrapped.
|
||||
max_width = 100
|
||||
|
||||
# 'downloads' is the path to a downloads folder.
|
||||
# An empty value means the code will find the default downloads folder for your system.
|
||||
# If the path does not exist it will be created.
|
||||
downloads = ""
|
||||
|
||||
# Max size for displayable content in bytes - after that size a download window pops up
|
||||
page_max_size = 2097152 # 2 MiB
|
||||
# Max time it takes to load a page in seconds - after that a download window pops up
|
||||
page_max_time = 10
|
||||
|
||||
# Whether to replace tab numbers with emoji favicons, which are cached.
|
||||
emoji_favicons = false
|
||||
|
||||
|
||||
# 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]
|
||||
@ -41,15 +57,16 @@ page_max_time = 10
|
||||
max_size = 0 # Size in bytes
|
||||
max_pages = 30 # The maximum number of pages the cache will store
|
||||
|
||||
|
||||
[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".
|
||||
# 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.
|
||||
# DO NOT use the names from that site, just the hex codes.
|
||||
|
||||
# Definitions:
|
||||
# bg = background
|
||||
|
@ -486,24 +486,7 @@ func CloseTab() {
|
||||
}
|
||||
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab)) // Go to previous page
|
||||
// Rewrite the tab display
|
||||
tabRow.Clear()
|
||||
if viper.GetBool("a-general.color") {
|
||||
for i := 0; i < NumTabs(); i++ {
|
||||
fmt.Fprintf(tabRow, `["%d"][%s] %d [%s][""]|`,
|
||||
i,
|
||||
config.GetColorString("tab_num"),
|
||||
i+1,
|
||||
config.GetColorString("tab_divider"),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < NumTabs(); i++ {
|
||||
fmt.Fprintf(tabRow, `["%d"] %d [""]|`, i, i+1)
|
||||
}
|
||||
}
|
||||
tabRow.Highlight(strconv.Itoa(curTab)).ScrollToHighlight()
|
||||
|
||||
rewriteTabRow()
|
||||
// Restore previous tab's state
|
||||
tabs[curTab].applyAll()
|
||||
|
||||
@ -550,8 +533,10 @@ func Reload() {
|
||||
return
|
||||
}
|
||||
|
||||
go cache.RemovePage(tabs[curTab].page.Url)
|
||||
parsed, _ := url.Parse(tabs[curTab].page.Url)
|
||||
go func(t *tab) {
|
||||
cache.RemovePage(tabs[curTab].page.Url)
|
||||
cache.RemoveFavicon(parsed.Host)
|
||||
handleURL(t, t.page.Url) // goURL is not used bc history shouldn't be added to
|
||||
if t == tabs[curTab] {
|
||||
// Display the bottomBar state that handleURL set
|
||||
|
@ -1,16 +1,22 @@
|
||||
package display
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/makeworld-the-better-one/amfora/cache"
|
||||
"github.com/makeworld-the-better-one/amfora/client"
|
||||
"github.com/makeworld-the-better-one/amfora/config"
|
||||
"github.com/makeworld-the-better-one/amfora/renderer"
|
||||
"github.com/makeworld-the-better-one/amfora/structs"
|
||||
"github.com/makeworld-the-better-one/amfora/webbrowser"
|
||||
"github.com/makeworld-the-better-one/go-gemini"
|
||||
"github.com/makeworld-the-better-one/go-isemoji"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
@ -106,8 +112,16 @@ func setPage(t *tab, p *structs.Page) {
|
||||
// Make sure the page content is fitted to the terminal every time it's displayed
|
||||
reformatPage(p)
|
||||
|
||||
// Change page on screen
|
||||
oldFav := t.page.Favicon
|
||||
|
||||
t.page = p
|
||||
|
||||
go func() {
|
||||
parsed, _ := url.Parse(p.Url)
|
||||
handleFavicon(t, parsed.Host, oldFav)
|
||||
}()
|
||||
|
||||
// Change page on screen
|
||||
t.view.SetText(p.Content)
|
||||
t.view.Highlight("") // Turn off highlights, other funcs may restore if necessary
|
||||
t.view.ScrollToBeginning()
|
||||
@ -144,6 +158,81 @@ func handleHTTP(u string, showInfo bool) {
|
||||
App.Draw()
|
||||
}
|
||||
|
||||
// handleFavicon handles getting and displaying a favicon.
|
||||
// `old` is the previous favicon for the tab.
|
||||
func handleFavicon(t *tab, host, old string) {
|
||||
defer func() {
|
||||
// Update display if needed
|
||||
if t.page.Favicon != old && isValidTab(t) {
|
||||
rewriteTabRow()
|
||||
}
|
||||
}()
|
||||
|
||||
if !viper.GetBool("a-general.emoji_favicons") {
|
||||
// Not enabled
|
||||
return
|
||||
}
|
||||
if t.page.Favicon != "" {
|
||||
return
|
||||
}
|
||||
if host == "" {
|
||||
return
|
||||
}
|
||||
|
||||
fav := cache.GetFavicon(host)
|
||||
if fav == cache.KnownNoFavicon {
|
||||
// It's been cached that this host doesn't have a favicon
|
||||
return
|
||||
}
|
||||
if fav != "" {
|
||||
t.page.Favicon = fav
|
||||
rewriteTabRow()
|
||||
return
|
||||
}
|
||||
|
||||
// No favicon cached
|
||||
res, err := client.Fetch("gemini://" + host + "/favicon.txt")
|
||||
if err != nil {
|
||||
if res != nil {
|
||||
res.Body.Close()
|
||||
}
|
||||
cache.AddFavicon(host, cache.KnownNoFavicon)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.Status != 20 {
|
||||
cache.AddFavicon(host, cache.KnownNoFavicon)
|
||||
return
|
||||
}
|
||||
if !strings.HasPrefix(res.Meta, "text/plain") {
|
||||
cache.AddFavicon(host, cache.KnownNoFavicon)
|
||||
return
|
||||
}
|
||||
// It's a regular plain response
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = io.CopyN(buf, res.Body, 29+2+1) // 29 is the max emoji length, +2 for CRLF, +1 so that the right size will EOF
|
||||
if err == nil {
|
||||
// Content was too large
|
||||
cache.AddFavicon(host, cache.KnownNoFavicon)
|
||||
return
|
||||
} else if err != io.EOF {
|
||||
// Some network reading error
|
||||
// No favicon is NOT known, could be a temporary error
|
||||
return
|
||||
}
|
||||
// EOF, which is what we want.
|
||||
emoji := strings.TrimRight(buf.String(), "\r\n")
|
||||
if !isemoji.IsEmoji(emoji) {
|
||||
cache.AddFavicon(host, cache.KnownNoFavicon)
|
||||
return
|
||||
}
|
||||
// Valid favicon found
|
||||
t.page.Favicon = emoji
|
||||
cache.AddFavicon(host, emoji)
|
||||
}
|
||||
|
||||
// goURL is like handleURL, but takes care of history and the bottomBar.
|
||||
// It should be preferred over handleURL in most cases.
|
||||
// It has no return values to be processed.
|
||||
@ -336,3 +425,32 @@ func handleURL(t *tab, u string) (string, bool) {
|
||||
go dlChoice("That file could not be displayed. What would you like to do?", u, res)
|
||||
return ret("", false)
|
||||
}
|
||||
|
||||
// rewriteTabRow clears the tabRow and writes all the tabs number/favicons into it.
|
||||
func rewriteTabRow() {
|
||||
tabRow.Clear()
|
||||
if viper.GetBool("a-general.color") {
|
||||
for i := 0; i < NumTabs(); i++ {
|
||||
char := strconv.Itoa(i + 1)
|
||||
if tabs[i].page.Favicon != "" {
|
||||
char = tabs[i].page.Favicon
|
||||
}
|
||||
fmt.Fprintf(tabRow, `["%d"][%s] %s [%s][""]|`,
|
||||
i,
|
||||
config.GetColorString("tab_num"),
|
||||
char,
|
||||
config.GetColorString("tab_divider"),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < NumTabs(); i++ {
|
||||
char := strconv.Itoa(i + 1)
|
||||
if tabs[i].page.Favicon != "" {
|
||||
char = tabs[i].page.Favicon
|
||||
}
|
||||
fmt.Fprintf(tabRow, `["%d"] %s [""]|`, i, char)
|
||||
}
|
||||
}
|
||||
tabRow.Highlight(strconv.Itoa(curTab)).ScrollToHighlight()
|
||||
App.Draw()
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@ -7,6 +7,7 @@ require (
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606
|
||||
github.com/makeworld-the-better-one/go-gemini v0.7.0
|
||||
github.com/makeworld-the-better-one/go-isemoji v1.0.0
|
||||
github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20200710151429-125743e22b4f
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mitchellh/mapstructure v1.3.1 // indirect
|
||||
@ -16,10 +17,9 @@ require (
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.7.0
|
||||
github.com/stretchr/testify v1.6.0
|
||||
github.com/stretchr/testify v1.6.1
|
||||
gitlab.com/tslocum/cview v1.4.8-0.20200713214710-cc7796c4ca44
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 // indirect
|
||||
golang.org/x/text v0.3.3
|
||||
gopkg.in/ini.v1 v1.57.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200603094226-e3079894b1e8 // indirect
|
||||
)
|
||||
|
10
go.sum
10
go.sum
@ -126,6 +126,8 @@ 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.7.0 h1:TCerE47eYHLXj6RQDjfd5HdGVbcVqpBC6OoPBlyY7q4=
|
||||
github.com/makeworld-the-better-one/go-gemini v0.7.0/go.mod h1:P7/FbZ+IEIbA/d+A0Y3w2GNgD8SA2AcNv7aDGJbaWG4=
|
||||
github.com/makeworld-the-better-one/go-isemoji v1.0.0 h1:W3O4+qwtXeT8PUDzcQ1UjxiupQWgc/oJHpqwrllx3xM=
|
||||
github.com/makeworld-the-better-one/go-isemoji v1.0.0/go.mod h1:FBjkPl9rr0G4vlZCc+Mr+QcnOfGCTbGWYW8/1sp06I0=
|
||||
github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20200710151429-125743e22b4f h1:YEUlTs5gb35UlBLTgqrub9axWTYB3d7/8TxrkJDZpRI=
|
||||
github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20200710151429-125743e22b4f/go.mod h1:X6sxWNi9PBgQybpR4fpXPVD5fm7svLqZTQ5DJuERIoM=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
@ -203,8 +205,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
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=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho=
|
||||
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
@ -345,8 +347,8 @@ 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/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=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -67,7 +67,6 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int) (*structs
|
||||
|
||||
_, err := io.CopyN(buf, res.Body, viper.GetInt64("a-general.page_max_size")+1)
|
||||
res.Body.Close()
|
||||
rawText := buf.Bytes()
|
||||
if err == nil {
|
||||
// Content was larger than max size
|
||||
return nil, ErrTooLarge
|
||||
@ -86,14 +85,14 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int) (*structs
|
||||
// Convert content first
|
||||
var utfText string
|
||||
if isUTF8(params["charset"]) {
|
||||
utfText = string(rawText)
|
||||
utfText = buf.String()
|
||||
} else {
|
||||
encoding, err := ianaindex.MIME.Encoding(params["charset"])
|
||||
if encoding == nil || err != nil {
|
||||
// Some encoding doesn't exist and wasn't caught in CanDisplay()
|
||||
return nil, errors.New("unsupported encoding")
|
||||
}
|
||||
utfText, err = encoding.NewDecoder().String(string(rawText))
|
||||
utfText, err = encoding.NewDecoder().String(buf.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ type Page struct {
|
||||
Selected string // The current text or link selected
|
||||
SelectedID string // The cview region ID for the selected text/link
|
||||
Mode PageMode
|
||||
Favicon string
|
||||
}
|
||||
|
||||
// Size returns an approx. size of a Page in bytes.
|
||||
|
Loading…
Reference in New Issue
Block a user