diff --git a/NOTES.md b/NOTES.md index 73b998b..0a221fc 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,8 +1,8 @@ # Notes - Simplify into one struct - - All the maps and stuff could be replaced with a `Tab` struct - - And then just one single map of tab number to `Tab` + - All the maps and stuff could be replaced with a `tab` struct + - And then just one single map of tab number to `tab` - URL for each tab should not be stored as a string - in the current code there's lots of reparsing the URL ## Upstream Bugs diff --git a/display/display.go b/display/display.go index 6f9e6a7..b1dd14b 100644 --- a/display/display.go +++ b/display/display.go @@ -172,7 +172,7 @@ func Init() { // 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, ".") && !strings.HasPrefix(query, "about:")) { - u := viper.GetString("a-general.search") + "?" + pathEscape(query) + u := viper.GetString("a-general.search") + "?" + queryEscape(query) cache.Remove(u) // Don't use the cached version of the search URL(u) } else { diff --git a/display/private.go b/display/private.go index 7a1480e..89c00ab 100644 --- a/display/private.go +++ b/display/private.go @@ -54,8 +54,9 @@ func textWidth() int { return viper.GetInt("a-general.max_width") } -// pathEscape is the same as url.PathEscape, but it also replaces the +. -func pathEscape(path string) string { +// queryEscape is the same as url.PathEscape, but it also replaces the +. +// This is because Gemini requires percent-escaping for queries. +func queryEscape(path string) string { return strings.ReplaceAll(url.PathEscape(path), "+", "%2B") } @@ -220,6 +221,10 @@ func setPage(p *structs.Page) { // If there is some error, it will return "". // The second returned item is a bool indicating if page content was displayed. // It returns false for Errors, other protocols, etc. +// +// TODO: Add tab number param - now the func only saves the values like the content +// and bottom bar. +// TODO: Some other func that constantly updates bottom bar values func handleURL(u string) (string, bool) { defer App.Draw() // Just in case @@ -324,7 +329,7 @@ func handleURL(u string) (string, bool) { if ok { // Make another request with the query string added // + chars are replaced because PathEscape doesn't do that - parsed.RawQuery = pathEscape(userInput) + parsed.RawQuery = queryEscape(userInput) if len(parsed.String()) > 1024 { // 1024 is the max size for URLs in the spec Error("Input Error", "URL for that input would be too long.") diff --git a/display/tab.go b/display/tab.go new file mode 100644 index 0000000..63f19a9 --- /dev/null +++ b/display/tab.go @@ -0,0 +1,121 @@ +package display + +import ( + "strings" + "sync" + + "github.com/makeworld-the-better-one/amfora/structs" + "gitlab.com/tslocum/cview" +) + +type tabMode int + +const ( + modeOff tabMode = iota // Regular mode + modeLinkSelect // When the enter key is pressed, allow for tab-based link navigation +) + +// tabHist holds the history for a tab. +type tabHistory struct { + urls []string + pos int // Position: where in the list of URLs we are +} + +// tab hold the information needed for each browser tab. +type tab struct { + page *structs.Page + view *cview.TextView + mode tabMode + history *tabHistory + reformatMut *sync.Mutex // Mutex for reformatting, so there's only one reformat job at once + selected string // The current text or link selected + barLabel string // The bottomBar label for the tab + barText string // The bottomBar text for the tab +} + +// makeNewTab initializes an tab struct with no content. +func makeNewTab() *tab { + return &tab{ + page: &structs.Page{}, + view: cview.NewTextView(). + SetDynamicColors(true). + SetRegions(true). + SetScrollable(true). + SetWrap(false). + SetChangedFunc(func() { + App.Draw() + }), + mode: modeOff, + reformatMut: &sync.Mutex{}, + } +} + +// addToHistory adds the given URL to history. +// It assumes the URL is currently being loaded and displayed on the page. +func (t *tab) addToHistory(u string) { + if t.history.pos < len(t.history.urls)-1 { + // We're somewhere in the middle of the history instead, with URLs ahead and behind. + // The URLs ahead need to be removed so this new URL is the most recent item in the history + t.history.urls = t.history.urls[:t.history.pos+1] + } + t.history.urls = append(t.history.urls, u) + t.history.pos++ +} + +// 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) +} + +// 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) +} + +// hasContent returns true when the tab has a page that could be displayed. +// The most likely situation where false would be returned is when the default +// new tab content is being displayed. +func (t *tab) hasContent() bool { + if t.page == nil || t.view == nil { + return false + } + if t.page.Url == "" { + return false + } + if strings.HasPrefix(t.page.Url, "about:") { + return false + } + if t.page.Content == "" { + return false + } + return true +} + +// saveScroll saves where in the page the user was. +// It should be used whenever moving from one page to another. +func (t *tab) saveScroll() { + // It will also be saved in the cache because the cache uses the same pointer + row, col := t.view.GetScrollOffset() + t.page.Row = row + t.page.Column = col +} + +// applyScroll applies the saved scroll values to the page and tab. +// It should only be used when going backward and forward. +func (t *tab) applyScroll() { + t.view.ScrollTo(t.page.Row, t.page.Column) +} + +// saveBottomBar saves the current bottomBar values in the tab. +func (t *tab) saveBottomBar() { + t.barLabel = bottomBar.GetLabel() + t.barText = bottomBar.GetText() +} + +// applyBottomBar sets the bottomBar using the stored tab values +func (t *tab) applyBottomBar() { + bottomBar.SetLabel(t.barLabel) + bottomBar.SetText(t.barText) +}