1
0
mirror of https://github.com/makew0rld/amfora.git synced 2024-06-21 19:35:23 +00:00

cview update (#107)

Co-authored-by: makeworld <25111343+makeworld-the-better-one@users.noreply.github.com>
Co-authored-by: Stephen Robinson <stephen@drsudo.net>
Co-authored-by: Trevor Slocum <trevor@rocketnine.space>
Co-authored-by: Stephen Robinson <sudobash1@users.noreply.github.com>
This commit is contained in:
makeworld 2021-02-17 14:17:13 -05:00 committed by GitHub
parent 332aa6af9f
commit 9198572f34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 625 additions and 549 deletions

View File

@ -24,6 +24,6 @@ jobs:
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
with: with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.31 version: v1.35
# Optional: show only new issues if it's a pull request. The default value is `false`. # Optional: show only new issues if it's a pull request. The default value is `false`.
only-new-issues: true only-new-issues: true

View File

@ -8,8 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- **Media type handlers** - open non-text files in another application (#121, #134) - **Media type handlers** - open non-text files in another application (#121, #134)
- Ability to set custom keybindings in config (#135) - Ability to set custom keybindings in config (#135)
- Added scrollbar, by default only appears on pages that go off-screen (#89, #107)
- More internal about pages, see `about:about` (#160, 187) - More internal about pages, see `about:about` (#160, 187)
### Changed
- Update cview to `d776e728ef6d2a9990a5cd86a70b31f0678613e2` for large performance and feature updates (#107)
- Update to tcell v2 (dependency of cview)
### Fixed ### Fixed
- Don't use cache when URL is typed in bottom bar (#159) - Don't use cache when URL is typed in bottom bar (#159)
- Fix downloading of pages that are too large or timed out - Fix downloading of pages that are too large or timed out
@ -18,6 +23,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Handle empty META string (#176) - Handle empty META string (#176)
- Whitespace around the URL entered in the bottom bar is stripped (#184) - Whitespace around the URL entered in the bottom bar is stripped (#184)
- Don't break visiting IPv6 hosts when port 1965 is specified (#195) - Don't break visiting IPv6 hosts when port 1965 is specified (#195)
- More reliable start, no more flash of unindented text, or text that stays unindented (#107)
- Pages with ANSI resets don't use the terminal's default text and background colors (#107)
- ANSI documents don't leak color into the left margin (#107)
- Rendering very long documents is now ~96% faster, excluding gemtext parsing (#26, #107)
- Due to that same change, less memory is used per-page (#26, #107)
## [1.7.2] - 2020-12-21 ## [1.7.2] - 2020-12-21

View File

@ -4,17 +4,9 @@
- URL for each tab should not be stored as a string - in the current code there's lots of reparsing the URL - URL for each tab should not be stored as a string - in the current code there's lots of reparsing the URL
## Upstream Bugs ## Upstream Bugs
- Wrapping messes up on brackets
- Filed [issue 23](https://gitlab.com/tslocum/cview/-/issues/23)
- Wrapping panics on strings with brackets and Asian characters
- Filed cview [issue 27](https://gitlab.com/tslocum/cview/-/issues/27)
- The panicking was reported and fixed in Amfora [issue 20](https://github.com/makeworld-the-better-one/amfora/issues/20), but the lines are now just not wrapped
- Text background not reset on ANSI pages
- Filed [issue 25](https://gitlab.com/tslocum/cview/-/issues/25)
- Modal styling messed up when wrapped - example occurence is the error modal for a long unsupported scheme URL
- Filed [issue 26](https://gitlab.com/tslocum/cview/-/issues/26)
- Add some bold back into modal text after this is fixed
- Bookmark keys aren't deleted, just set to `""` - Bookmark keys aren't deleted, just set to `""`
- Waiting on [this viper PR](https://github.com/spf13/viper/pull/519) to be merged - Waiting on [this viper PR](https://github.com/spf13/viper/pull/519) to be merged
- Help table cells aren't dynamically wrapped - [cview.Styles not being used](https://gitlab.com/tslocum/cview/-/issues/47) - issue is circumvented in Amfora
- Filed [issue 29](https://gitlab.com/tslocum/cview/-/issues/29) - [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

View File

@ -178,7 +178,7 @@ You can also check out [all the issues with the bug label](https://github.com/ma
## Libraries ## Libraries
Amfora ❤️ open source! Amfora ❤️ open source!
- [cview](https://gitlab.com/tslocum/cview/) for the TUI - My [cview fork](https://gitlab.com/makeworld-the-better-one/cview/) for the TUI - pull request [here](https://gitlab.com/tslocum/cview/-/merge_requests/12)
- It's a fork of [tview](https://github.com/rivo/tview) with PRs merged and active support - 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 - It uses [tcell](https://github.com/gdamore/tcell) for low level terminal operations
- [Viper](https://github.com/spf13/viper) for configuration and TOFU storing - [Viper](https://github.com/spf13/viper) for configuration and TOFU storing

View File

@ -52,14 +52,19 @@ func main() {
client.Init() client.Init()
// Initialize lower-level cview app
if err = display.App.Init(); err != nil {
panic(err)
}
// Initialize Amfora's settings
display.Init(version, commit, builtBy) display.Init(version, commit, builtBy)
display.NewTab() display.NewTab()
display.NewTab() // Open extra tab and close it to fully initialize the app and wrapping
display.CloseTab()
if len(os.Args[1:]) > 0 { if len(os.Args[1:]) > 0 {
display.URL(os.Args[1]) display.URL(os.Args[1])
} }
// Start
if err = display.App.Run(); err != nil { if err = display.App.Run(); err != nil {
panic(err) panic(err)
} }

View File

@ -11,7 +11,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/gdamore/tcell" "github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/cache" "github.com/makeworld-the-better-one/amfora/cache"
homedir "github.com/mitchellh/go-homedir" homedir "github.com/mitchellh/go-homedir"
"github.com/rkoesters/xdg/basedir" "github.com/rkoesters/xdg/basedir"
@ -55,6 +55,10 @@ type MediaHandler struct {
var MediaHandlers = make(map[string]MediaHandler) var MediaHandlers = make(map[string]MediaHandler)
// Controlled by "a-general.scrollbar" in config
// Defaults to ScrollBarAuto on an invalid value
var ScrollBar cview.ScrollBarVisibility
func Init() error { func Init() error {
// *** Set paths *** // *** Set paths ***
@ -204,6 +208,7 @@ func Init() error {
viper.SetDefault("a-general.page_max_size", 2097152) viper.SetDefault("a-general.page_max_size", 2097152)
viper.SetDefault("a-general.page_max_time", 10) viper.SetDefault("a-general.page_max_time", 10)
viper.SetDefault("a-general.emoji_favicons", false) viper.SetDefault("a-general.emoji_favicons", false)
viper.SetDefault("a-general.scrollbar", "auto")
viper.SetDefault("keybindings.bind_reload", []string{"R", "Ctrl-R"}) viper.SetDefault("keybindings.bind_reload", []string{"R", "Ctrl-R"})
viper.SetDefault("keybindings.bind_home", "Backspace") viper.SetDefault("keybindings.bind_home", "Backspace")
viper.SetDefault("keybindings.bind_bookmarks", "Ctrl-B") viper.SetDefault("keybindings.bind_bookmarks", "Ctrl-B")
@ -392,5 +397,15 @@ func Init() error {
} }
} }
// Parse scrollbar options
switch viper.GetString("a-general.scrollbar") {
case "never":
ScrollBar = cview.ScrollBarNever
case "always":
ScrollBar = cview.ScrollBarAlways
default:
ScrollBar = cview.ScrollBarAuto
}
return nil return nil
} }

View File

@ -73,6 +73,10 @@ page_max_time = 10
# Whether to replace tab numbers with emoji favicons, which are cached. # Whether to replace tab numbers with emoji favicons, which are cached.
emoji_favicons = false emoji_favicons = false
# When a scrollbar appears. "never", "auto", and "always" are the only valid values.
# "auto" means the scrollbar only appears when the page is longer than the window.
scrollbar = "auto"
[auth] [auth]
# Authentication settings # Authentication settings
@ -301,6 +305,7 @@ entries_per_page = 20
# bottombar_label: The color of the prompt that appears when you press space # bottombar_label: The color of the prompt that appears when you press space
# bottombar_text: The color of the text you type # bottombar_text: The color of the text you type
# bottombar_bg # bottombar_bg
# scrollbar: The scrollbar that appears on the right for long pages
# hdg_1 # hdg_1
# hdg_2 # hdg_2

View File

@ -3,7 +3,7 @@ package config
import ( import (
"strings" "strings"
"github.com/gdamore/tcell" "github.com/gdamore/tcell/v2"
"github.com/spf13/viper" "github.com/spf13/viper"
) )

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"sync" "sync"
"github.com/gdamore/tcell" "github.com/gdamore/tcell/v2"
) )
// Functions to allow themeing configuration. // Functions to allow themeing configuration.
@ -21,6 +21,7 @@ var theme = map[string]tcell.Color{
"bottombar_label": tcell.Color30, "bottombar_label": tcell.Color30,
"bottombar_text": tcell.ColorBlack, "bottombar_text": tcell.ColorBlack,
"bottombar_bg": tcell.ColorWhite, "bottombar_bg": tcell.ColorWhite,
"scrollbar": tcell.ColorWhite,
// Modals // Modals
"btn_bg": tcell.ColorNavy, // All modal buttons "btn_bg": tcell.ColorNavy, // All modal buttons
@ -74,7 +75,7 @@ func SetColor(key string, color tcell.Color) {
func GetColor(key string) tcell.Color { func GetColor(key string) tcell.Color {
themeMu.RLock() themeMu.RLock()
defer themeMu.RUnlock() defer themeMu.RUnlock()
return theme[key] return theme[key].TrueColor()
} }
// GetColorString returns a string that can be used in a cview color tag, // GetColorString returns a string that can be used in a cview color tag,
@ -83,5 +84,5 @@ func GetColor(key string) tcell.Color {
func GetColorString(key string) string { func GetColorString(key string) string {
themeMu.RLock() themeMu.RLock()
defer themeMu.RUnlock() defer themeMu.RUnlock()
return fmt.Sprintf("#%06x", theme[key].Hex()) return fmt.Sprintf("#%06x", theme[key].TrueColor().Hex())
} }

View File

@ -70,6 +70,10 @@ page_max_time = 10
# Whether to replace tab numbers with emoji favicons, which are cached. # Whether to replace tab numbers with emoji favicons, which are cached.
emoji_favicons = false emoji_favicons = false
# When a scrollbar appears. "never", "auto", and "always" are the only valid values.
# "auto" means the scrollbar only appears when the page is longer than the window.
scrollbar = "auto"
[auth] [auth]
# Authentication settings # Authentication settings
@ -298,6 +302,7 @@ entries_per_page = 20
# bottombar_label: The color of the prompt that appears when you press space # bottombar_label: The color of the prompt that appears when you press space
# bottombar_text: The color of the text you type # bottombar_text: The color of the text you type
# bottombar_bg # bottombar_bg
# scrollbar: The scrollbar that appears on the right for long pages
# hdg_1 # hdg_1
# hdg_2 # hdg_2

View File

@ -34,7 +34,7 @@ func aboutInit(version, commit, builtBy string) {
} }
func createAboutPage(url string, content string) structs.Page { func createAboutPage(url string, content string) structs.Page {
renderContent, links := renderer.RenderGemini(content, textWidth(), leftMargin(), false) renderContent, links := renderer.RenderGemini(content, textWidth(), false)
return structs.Page{ return structs.Page{
Raw: content, Raw: content,
Content: renderContent, Content: renderContent,

View File

@ -2,9 +2,8 @@ package display
import ( import (
"fmt" "fmt"
"strconv"
"github.com/gdamore/tcell" "github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/bookmarks" "github.com/makeworld-the-better-one/amfora/bookmarks"
"github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/config"
"github.com/makeworld-the-better-one/amfora/renderer" "github.com/makeworld-the-better-one/amfora/renderer"
@ -21,37 +20,44 @@ 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 var bkmkModalText string // The current text of the input field in the modal
func bkmkInit() { func bkmkInit() {
panels.AddPanel("bkmk", bkmkModal, false, false)
m := bkmkModal
if viper.GetBool("a-general.color") { if viper.GetBool("a-general.color") {
bkmkModal.SetBackgroundColor(config.GetColor("bkmk_modal_bg")). m.SetBackgroundColor(config.GetColor("bkmk_modal_bg"))
SetButtonBackgroundColor(config.GetColor("btn_bg")). m.SetButtonBackgroundColor(config.GetColor("btn_bg"))
SetButtonTextColor(config.GetColor("btn_text")). m.SetButtonTextColor(config.GetColor("btn_text"))
SetTextColor(config.GetColor("bkmk_modal_text")) m.SetTextColor(config.GetColor("bkmk_modal_text"))
bkmkModal.GetForm(). form := m.GetForm()
SetLabelColor(config.GetColor("bkmk_modal_label")). form.SetLabelColor(config.GetColor("bkmk_modal_label"))
SetFieldBackgroundColor(config.GetColor("bkmk_modal_field_bg")). form.SetFieldBackgroundColor(config.GetColor("bkmk_modal_field_bg"))
SetFieldTextColor(config.GetColor("bkmk_modal_field_text")) form.SetFieldTextColor(config.GetColor("bkmk_modal_field_text"))
bkmkModal.GetFrame(). form.SetButtonBackgroundColorFocused(config.GetColor("btn_text"))
SetBorderColor(config.GetColor("bkmk_modal_text")). form.SetButtonTextColorFocused(config.GetColor("btn_bg"))
SetTitleColor(config.GetColor("bkmk_modal_text")) frame := m.GetFrame()
frame.SetBorderColor(config.GetColor("bkmk_modal_text"))
frame.SetTitleColor(config.GetColor("bkmk_modal_text"))
} else { } else {
bkmkModal.SetBackgroundColor(tcell.ColorBlack). m.SetBackgroundColor(tcell.ColorBlack)
SetButtonBackgroundColor(tcell.ColorWhite). m.SetButtonBackgroundColor(tcell.ColorWhite)
SetButtonTextColor(tcell.ColorBlack). m.SetButtonTextColor(tcell.ColorBlack)
SetTextColor(tcell.ColorWhite) m.SetTextColor(tcell.ColorWhite)
bkmkModal.GetForm(). form := m.GetForm()
SetLabelColor(tcell.ColorWhite). form.SetLabelColor(tcell.ColorWhite)
SetFieldBackgroundColor(tcell.ColorWhite). form.SetFieldBackgroundColor(tcell.ColorWhite)
SetFieldTextColor(tcell.ColorBlack) form.SetFieldTextColor(tcell.ColorBlack)
bkmkModal.GetFrame(). form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
SetBorderColor(tcell.ColorWhite). form.SetButtonTextColorFocused(tcell.ColorWhite)
SetTitleColor(tcell.ColorWhite) frame := m.GetFrame()
frame.SetBorderColor(tcell.ColorWhite)
frame.SetTitleColor(tcell.ColorWhite)
} }
bkmkModal.SetBorder(true) m.SetBorder(true)
bkmkModal.GetFrame(). frame := m.GetFrame()
SetTitleAlign(cview.AlignCenter). frame.SetTitleAlign(cview.AlignCenter)
SetTitle(" Add Bookmark ") frame.SetTitle(" Add Bookmark ")
bkmkModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { m.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
switch buttonLabel { switch buttonLabel {
case "Add": case "Add":
bkmkCh <- 1 bkmkCh <- 1
@ -97,13 +103,13 @@ func openBkmkModal(name string, exists bool, favicon string) (string, int) {
bkmkModalText = text bkmkModalText = text
}) })
tabPages.ShowPage("bkmk") panels.ShowPanel("bkmk")
tabPages.SendToFront("bkmk") panels.SendToFront("bkmk")
App.SetFocus(bkmkModal) App.SetFocus(bkmkModal)
App.Draw() App.Draw()
action := <-bkmkCh action := <-bkmkCh
tabPages.SwitchToPage(strconv.Itoa(curTab)) panels.HidePanel("bkmk")
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
App.Draw() App.Draw()
@ -120,7 +126,7 @@ func Bookmarks(t *tab) {
bkmkPageRaw += fmt.Sprintf("=> %s %s\r\n", keys[i], m[keys[i]]) bkmkPageRaw += fmt.Sprintf("=> %s %s\r\n", keys[i], m[keys[i]])
} }
// Render and display // Render and display
content, links := renderer.RenderGemini(bkmkPageRaw, textWidth(), leftMargin(), false) content, links := renderer.RenderGemini(bkmkPageRaw, textWidth(), false)
page := structs.Page{ page := structs.Page{
Raw: bkmkPageRaw, Raw: bkmkPageRaw,
Content: content, Content: content,

View File

@ -6,8 +6,9 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/gdamore/tcell" "github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/cache" "github.com/makeworld-the-better-one/amfora/cache"
"github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/config"
"github.com/makeworld-the-better-one/amfora/renderer" "github.com/makeworld-the-better-one/amfora/renderer"
@ -39,76 +40,92 @@ var hasSpaceisURL = regexp.MustCompile(`[^ ]+\.[^ ].*/.`)
// The only pages that don't confine to this scheme are those named after modals, // 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. // which are used to draw modals on top the current tab.
// Ex: "info", "error", "input", "yesno" // Ex: "info", "error", "input", "yesno"
var tabPages = cview.NewPages() var panels = cview.NewPanels()
// The tabs at the top with titles // Tabbed viewer for primitives
var tabRow = cview.NewTextView(). // Panels are named as strings of tab numbers - so the textview for the first tab
SetDynamicColors(true). // is held in the page named "0".
SetRegions(true). var browser = cview.NewTabbedPanels()
SetScrollable(true).
SetWrap(false).
SetHighlightedFunc(func(added, removed, remaining []string) {
// There will always only be one string in added - never multiple highlights
// Remaining should always be empty
i, _ := strconv.Atoi(added[0])
tabPages.SwitchToPage(strconv.Itoa(i)) // Tab names are just numbers, zero-indexed
})
// Root layout // Root layout
var layout = cview.NewFlex(). var layout = cview.NewFlex()
SetDirection(cview.FlexRow)
var newTabPage structs.Page var newTabPage structs.Page
var App = cview.NewApplication(). // Global mutex for changing the size of the left margin on all tabs.
EnableMouse(false). var reformatMu = sync.Mutex{}
SetRoot(layout, true).
SetAfterResizeFunc(func(width int, height int) { var App = cview.NewApplication()
func Init(version, commit, builtBy string) {
aboutInit(version, commit, builtBy)
App.EnableMouse(false)
App.SetRoot(layout, true)
App.SetAfterResizeFunc(func(width int, height int) {
// Store for calculations // Store for calculations
termW = width termW = width
termH = height termH = height
// Make sure the current tab content is reformatted when the terminal size changes // Make sure the current tab content is reformatted when the terminal size changes
go func(t *tab) { go func(t *tab) {
t.reformatMu.Lock() // Only one reformat job per tab reformatMu.Lock() // Only allow one reformat job at a time
defer t.reformatMu.Unlock() for i := range tabs {
// Use the current tab, but don't affect other tabs if the user switches tabs // Overwrite all tabs with a new, differently sized, left margin
reformatPageAndSetView(t, t.page) browser.AddTab(strconv.Itoa(i), makeTabLabel(strconv.Itoa(i+1)), makeContentLayout(tabs[i].view))
if tabs[i] == t {
// Reformat page ASAP, in the middle of loop
reformatPageAndSetView(t, t.page)
}
}
App.Draw()
reformatMu.Unlock()
}(tabs[curTab]) }(tabs[curTab])
}) })
func Init(version, commit, builtBy string) { panels.AddPanel("browser", browser, true, true)
aboutInit(version, commit, builtBy)
tabRow.SetChangedFunc(func() {
App.Draw()
})
helpInit() helpInit()
layout. layout.SetDirection(cview.FlexRow)
AddItem(tabRow, 1, 1, false). layout.AddItem(panels, 0, 1, true)
AddItem(nil, 1, 1, false). // One line of empty space above the page layout.AddItem(bottomBar, 1, 1, false)
AddItem(tabPages, 0, 1, true).
AddItem(nil, 1, 1, false). // One line of empty space before bottomBar
AddItem(bottomBar, 1, 1, false)
if viper.GetBool("a-general.color") { if viper.GetBool("a-general.color") {
layout.SetBackgroundColor(config.GetColor("bg")) layout.SetBackgroundColor(config.GetColor("bg"))
tabRow.SetBackgroundColor(config.GetColor("bg"))
bottomBar.SetBackgroundColor(config.GetColor("bottombar_bg")) bottomBar.SetBackgroundColor(config.GetColor("bottombar_bg"))
bottomBar. bottomBar.SetLabelColor(config.GetColor("bottombar_label"))
SetLabelColor(config.GetColor("bottombar_label")). bottomBar.SetFieldBackgroundColor(config.GetColor("bottombar_bg"))
SetFieldBackgroundColor(config.GetColor("bottombar_bg")). bottomBar.SetFieldTextColor(config.GetColor("bottombar_text"))
SetFieldTextColor(config.GetColor("bottombar_text"))
browser.SetTabBackgroundColor(config.GetColor("bg"))
browser.SetTabBackgroundColorFocused(config.GetColor("tab_num"))
browser.SetTabTextColor(config.GetColor("tab_num"))
browser.SetTabTextColorFocused(config.GetColor("bg"))
browser.SetTabSwitcherDivider(
"",
fmt.Sprintf("[%s:%s]|[-]", config.GetColorString("tab_divider"), config.GetColorString("bg")),
fmt.Sprintf("[%s:%s]|[-]", config.GetColorString("tab_divider"), config.GetColorString("bg")),
)
browser.Switcher.SetBackgroundColor(config.GetColor("bg"))
} else { } else {
bottomBar.SetBackgroundColor(tcell.ColorWhite) bottomBar.SetBackgroundColor(tcell.ColorWhite)
bottomBar. bottomBar.SetLabelColor(tcell.ColorBlack)
SetLabelColor(tcell.ColorBlack). bottomBar.SetFieldBackgroundColor(tcell.ColorWhite)
SetFieldBackgroundColor(tcell.ColorWhite). bottomBar.SetFieldTextColor(tcell.ColorBlack)
SetFieldTextColor(tcell.ColorBlack)
browser.SetTabBackgroundColor(tcell.ColorBlack)
browser.SetTabBackgroundColorFocused(tcell.ColorWhite)
browser.SetTabTextColor(tcell.ColorWhite)
browser.SetTabTextColorFocused(tcell.ColorBlack)
browser.SetTabSwitcherDivider(
"",
"[#ffffff:#000000]|[-]",
"[#ffffff:#000000]|[-]",
)
} }
bottomBar.SetDoneFunc(func(key tcell.Key) { bottomBar.SetDoneFunc(func(key tcell.Key) {
tab := curTab tab := curTab
@ -230,7 +247,7 @@ func Init(version, commit, builtBy string) {
// Render the default new tab content ONCE and store it for later // Render the default new tab content ONCE and store it for later
// This code is repeated in Reload() // This code is repeated in Reload()
newTabContent := getNewTabContent() newTabContent := getNewTabContent()
renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), leftMargin(), false) renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), false)
newTabPage = structs.Page{ newTabPage = structs.Page{
Raw: newTabContent, Raw: newTabContent,
Content: renderedNewTabContent, Content: renderedNewTabContent,
@ -426,23 +443,10 @@ func NewTab() {
tabs[curTab].addToHistory("about:newtab") tabs[curTab].addToHistory("about:newtab")
tabs[curTab].history.pos = 0 // Manually set as first page tabs[curTab].history.pos = 0 // Manually set as first page
tabPages.AddAndSwitchToPage(strconv.Itoa(curTab), tabs[curTab].view, true) browser.AddTab(strconv.Itoa(curTab), makeTabLabel(strconv.Itoa(curTab+1)), makeContentLayout(tabs[curTab].view))
browser.SetCurrentTab(strconv.Itoa(curTab))
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
// Add tab number to the actual place where tabs are show on the screen
// Tab regions are 0-indexed but text displayed on the screen starts at 1
if viper.GetBool("a-general.color") {
fmt.Fprintf(tabRow, `["%d"][%s] %d [%s][""]|`,
curTab,
config.GetColorString("tab_num"),
curTab+1,
config.GetColorString("tab_divider"),
)
} else {
fmt.Fprintf(tabRow, `["%d"] %d [""]|`, curTab, curTab+1)
}
tabRow.Highlight(strconv.Itoa(curTab)).ScrollToHighlight()
bottomBar.SetLabel("") bottomBar.SetLabel("")
bottomBar.SetText("") bottomBar.SetText("")
tabs[curTab].saveBottomBar() tabs[curTab].saveBottomBar()
@ -469,7 +473,7 @@ func CloseTab() {
} }
tabs = tabs[:len(tabs)-1] tabs = tabs[:len(tabs)-1]
tabPages.RemovePage(strconv.Itoa(curTab)) browser.RemoveTab(strconv.Itoa(curTab))
if curTab <= 0 { if curTab <= 0 {
curTab = NumTabs() - 1 curTab = NumTabs() - 1
@ -477,8 +481,7 @@ func CloseTab() {
curTab-- curTab--
} }
tabPages.SwitchToPage(strconv.Itoa(curTab)) // Go to previous page browser.SetCurrentTab(strconv.Itoa(curTab)) // Go to previous page
rewriteTabRow()
// Restore previous tab's state // Restore previous tab's state
tabs[curTab].applyAll() tabs[curTab].applyAll()
@ -510,8 +513,7 @@ func SwitchTab(tab int) {
// Display tab // Display tab
reformatPageAndSetView(tabs[curTab], tabs[curTab].page) reformatPageAndSetView(tabs[curTab], tabs[curTab].page)
tabPages.SwitchToPage(strconv.Itoa(curTab)) browser.SetCurrentTab(strconv.Itoa(curTab))
tabRow.Highlight(strconv.Itoa(curTab)).ScrollToHighlight()
tabs[curTab].applyAll() tabs[curTab].applyAll()
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
@ -525,7 +527,7 @@ func Reload() {
// Re-render new tab, similar to Init() // Re-render new tab, similar to Init()
newTabContent := getNewTabContent() newTabContent := getNewTabContent()
tmpTermW := termW tmpTermW := termW
renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), leftMargin(), false) renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), false)
newTabPage = structs.Page{ newTabPage = structs.Page{
Raw: newTabContent, Raw: newTabContent,
Content: renderedNewTabContent, Content: renderedNewTabContent,

View File

@ -14,7 +14,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/gdamore/tcell" "github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/config"
"github.com/makeworld-the-better-one/amfora/structs" "github.com/makeworld-the-better-one/amfora/structs"
"github.com/makeworld-the-better-one/amfora/sysopen" "github.com/makeworld-the-better-one/amfora/sysopen"
@ -24,9 +24,8 @@ import (
"gitlab.com/tslocum/cview" "gitlab.com/tslocum/cview"
) )
// For choosing between download and opening - copy of YesNo basically // For choosing between download and the portal - copy of YesNo basically
var dlChoiceModal = cview.NewModal(). var dlChoiceModal = cview.NewModal()
AddButtons([]string{"Open", "Download", "Cancel"})
// Channel to indicate what choice they made using the button text // Channel to indicate what choice they made using the button text
var dlChoiceCh = make(chan string) var dlChoiceCh = make(chan string)
@ -34,52 +33,70 @@ var dlChoiceCh = make(chan string)
var dlModal = cview.NewModal() var dlModal = cview.NewModal()
func dlInit() { func dlInit() {
panels.AddPanel("dl", dlModal, false, false)
panels.AddPanel("dlChoice", dlChoiceModal, false, false)
dlm := dlModal
chm := dlChoiceModal
if viper.GetBool("a-general.color") { if viper.GetBool("a-general.color") {
dlChoiceModal.SetButtonBackgroundColor(config.GetColor("btn_bg")). chm.SetButtonBackgroundColor(config.GetColor("btn_bg"))
SetButtonTextColor(config.GetColor("btn_text")). chm.SetButtonTextColor(config.GetColor("btn_text"))
SetBackgroundColor(config.GetColor("dl_choice_modal_bg")). chm.SetBackgroundColor(config.GetColor("dl_choice_modal_bg"))
SetTextColor(config.GetColor("dl_choice_modal_text")) chm.SetTextColor(config.GetColor("dl_choice_modal_text"))
dlChoiceModal.GetFrame(). form := chm.GetForm()
SetBorderColor(config.GetColor("dl_choice_modal_text")). form.SetButtonBackgroundColorFocused(config.GetColor("btn_text"))
SetTitleColor(config.GetColor("dl_choice_modal_text")) form.SetButtonTextColorFocused(config.GetColor("btn_bg"))
frame := chm.GetFrame()
frame.SetBorderColor(config.GetColor("dl_choice_modal_text"))
frame.SetTitleColor(config.GetColor("dl_choice_modal_text"))
dlModal.SetButtonBackgroundColor(config.GetColor("btn_bg")). dlm.SetButtonBackgroundColor(config.GetColor("btn_bg"))
SetButtonTextColor(config.GetColor("btn_text")). dlm.SetButtonTextColor(config.GetColor("btn_text"))
SetBackgroundColor(config.GetColor("dl_modal_bg")). dlm.SetBackgroundColor(config.GetColor("dl_modal_bg"))
SetTextColor(config.GetColor("dl_modal_text")) dlm.SetTextColor(config.GetColor("dl_modal_text"))
dlModal.GetFrame(). form = dlm.GetForm()
SetBorderColor(config.GetColor("dl_modal_text")). form.SetButtonBackgroundColorFocused(config.GetColor("btn_text"))
SetTitleColor(config.GetColor("dl_modal_text")) form.SetButtonTextColorFocused(config.GetColor("btn_bg"))
frame = dlm.GetFrame()
frame.SetBorderColor(config.GetColor("dl_modal_text"))
frame.SetTitleColor(config.GetColor("dl_modal_text"))
} else { } else {
dlChoiceModal.SetButtonBackgroundColor(tcell.ColorWhite). chm.SetButtonBackgroundColor(tcell.ColorWhite)
SetButtonTextColor(tcell.ColorBlack). chm.SetButtonTextColor(tcell.ColorBlack)
SetBackgroundColor(tcell.ColorBlack). chm.SetBackgroundColor(tcell.ColorBlack)
SetTextColor(tcell.ColorWhite) chm.SetTextColor(tcell.ColorWhite)
dlChoiceModal.SetBorderColor(tcell.ColorWhite) chm.SetBorderColor(tcell.ColorWhite)
dlChoiceModal.GetFrame().SetTitleColor(tcell.ColorWhite) chm.GetFrame().SetTitleColor(tcell.ColorWhite)
form := chm.GetForm()
form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
form.SetButtonTextColorFocused(tcell.ColorWhite)
dlModal.SetButtonBackgroundColor(tcell.ColorWhite). dlm.SetButtonBackgroundColor(tcell.ColorWhite)
SetButtonTextColor(tcell.ColorBlack). dlm.SetButtonTextColor(tcell.ColorBlack)
SetBackgroundColor(tcell.ColorBlack). dlm.SetBackgroundColor(tcell.ColorBlack)
SetTextColor(tcell.ColorWhite) dlm.SetTextColor(tcell.ColorWhite)
dlModal.GetFrame(). form = dlm.GetForm()
SetBorderColor(tcell.ColorWhite). form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
SetTitleColor(tcell.ColorWhite) form.SetButtonTextColorFocused(tcell.ColorWhite)
frame := dlm.GetFrame()
frame.SetBorderColor(tcell.ColorWhite)
frame.SetTitleColor(tcell.ColorWhite)
} }
dlChoiceModal.SetBorder(true) chm.AddButtons([]string{"Open", "Download", "Cancel"})
dlChoiceModal.GetFrame().SetTitleAlign(cview.AlignCenter) chm.SetBorder(true)
dlChoiceModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { chm.GetFrame().SetTitleAlign(cview.AlignCenter)
chm.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
dlChoiceCh <- buttonLabel dlChoiceCh <- buttonLabel
}) })
dlModal.SetBorder(true) dlm.SetBorder(true)
dlModal.GetFrame(). frame := dlm.GetFrame()
SetTitleAlign(cview.AlignCenter). frame.SetTitleAlign(cview.AlignCenter)
SetTitle(" Download ") frame.SetTitle(" Download ")
dlModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { dlm.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
if buttonLabel == "Ok" { if buttonLabel == "Ok" {
tabPages.SwitchToPage(strconv.Itoa(curTab)) panels.HidePanel("dl")
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
App.Draw() App.Draw()
} }
@ -124,27 +141,29 @@ func dlChoice(text, u string, resp *gemini.Response) {
choice = "Open" choice = "Open"
} else { } else {
dlChoiceModal.SetText(text) dlChoiceModal.SetText(text)
tabPages.ShowPage("dlChoice") panels.ShowPanel("dlChoice")
tabPages.SendToFront("dlChoice") panels.SendToFront("dlChoice")
App.SetFocus(dlChoiceModal) App.SetFocus(dlChoiceModal)
App.Draw() App.Draw()
choice = <-dlChoiceCh choice = <-dlChoiceCh
} }
if choice == "Download" { if choice == "Download" {
tabPages.HidePage("dlChoice") panels.HidePanel("dlChoice")
App.Draw() App.Draw()
downloadURL(config.DownloadsDir, u, resp) downloadURL(config.DownloadsDir, u, resp)
resp.Body.Close() // Only close when the file is downloaded resp.Body.Close() // Only close when the file is downloaded
return return
} }
if choice == "Open" { if choice == "Open" {
tabPages.HidePage("dlChoice") panels.HidePanel("dlChoice")
App.Draw() App.Draw()
open(u, resp) open(u, resp)
return return
} }
tabPages.SwitchToPage(strconv.Itoa(curTab))
// They chose the "Cancel" button
panels.HidePanel("dlChoice")
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
App.Draw() App.Draw()
} }
@ -180,9 +199,11 @@ func open(u string, resp *gemini.Response) {
if path == "" { if path == "" {
return return
} }
tabPages.SwitchToPage(strconv.Itoa(curTab))
panels.HidePanel("dl")
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
App.Draw() App.Draw()
if mediaHandler.Cmd == nil { if mediaHandler.Cmd == nil {
// Open with system default viewer // Open with system default viewer
_, err := sysopen.Open(path) _, err := sysopen.Open(path)
@ -246,15 +267,15 @@ func downloadURL(dir, u string, resp *gemini.Response) string {
// Display // Display
dlModal.ClearButtons() dlModal.ClearButtons()
dlModal.AddButtons([]string{"Downloading..."}) dlModal.AddButtons([]string{"Downloading..."})
tabPages.ShowPage("dl") panels.ShowPanel("dl")
tabPages.SendToFront("dl") panels.SendToFront("dl")
App.SetFocus(dlModal) App.SetFocus(dlModal)
App.Draw() App.Draw()
_, err = io.Copy(io.MultiWriter(f, bar), resp.Body) _, err = io.Copy(io.MultiWriter(f, bar), resp.Body)
done = true done = true
if err != nil { if err != nil {
tabPages.HidePage("dl") panels.HidePanel("dl")
Error("Download Error", err.Error()) Error("Download Error", err.Error())
f.Close() f.Close()
os.Remove(savePath) // Remove partial file os.Remove(savePath) // Remove partial file

View File

@ -59,7 +59,7 @@ func handleFile(u string) (*structs.Page, bool) {
} }
if mimetype == "text/gemini" { if mimetype == "text/gemini" {
rendered, links := renderer.RenderGemini(string(content), textWidth(), leftMargin(), false) rendered, links := renderer.RenderGemini(string(content), textWidth(), false)
page = &structs.Page{ page = &structs.Page{
Mediatype: structs.TextGemini, Mediatype: structs.TextGemini,
URL: u, URL: u,
@ -73,7 +73,7 @@ func handleFile(u string) (*structs.Page, bool) {
Mediatype: structs.TextPlain, Mediatype: structs.TextPlain,
URL: u, URL: u,
Raw: string(content), Raw: string(content),
Content: renderer.RenderPlainText(string(content), leftMargin()), Content: renderer.RenderPlainText(string(content)),
Links: []string{}, Links: []string{},
Width: termW, Width: termW,
} }
@ -107,7 +107,7 @@ func createDirectoryListing(u string) (*structs.Page, bool) {
content += fmt.Sprintf("=> %s%s %s%s\n", f.Name(), separator, f.Name(), separator) content += fmt.Sprintf("=> %s%s %s%s\n", f.Name(), separator, f.Name(), separator)
} }
rendered, links := renderer.RenderGemini(content, textWidth(), leftMargin(), false) rendered, links := renderer.RenderGemini(content, textWidth(), false)
page = &structs.Page{ page = &structs.Page{
Mediatype: structs.TextGemini, Mediatype: structs.TextGemini,
URL: u, URL: u,

View File

@ -9,6 +9,7 @@ import (
"net/url" "net/url"
"os/exec" "os/exec"
"path" "path"
"strconv"
"strings" "strings"
"github.com/makeworld-the-better-one/amfora/cache" "github.com/makeworld-the-better-one/amfora/cache"
@ -90,12 +91,12 @@ func handleOther(u string) {
} }
// handleFavicon handles getting and displaying a favicon. // handleFavicon handles getting and displaying a favicon.
// `old` is the previous favicon for the tab. func handleFavicon(t *tab, host string) {
func handleFavicon(t *tab, host, old string) {
defer func() { defer func() {
// Update display if needed // Update display if needed
if t.page.Favicon != old && isValidTab(t) { if t.page.Favicon != "" && isValidTab(t) {
rewriteTabRow() browser.SetTabLabel(strconv.Itoa(tabNumber(t)), makeTabLabel(t.page.Favicon))
App.Draw()
} }
}() }()
@ -117,7 +118,6 @@ func handleFavicon(t *tab, host, old string) {
} }
if fav != "" { if fav != "" {
t.page.Favicon = fav t.page.Favicon = fav
rewriteTabRow()
return return
} }
@ -389,7 +389,7 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) {
res.Body = rr.NewRestartReader(res.Body) res.Body = rr.NewRestartReader(res.Body)
if renderer.CanDisplay(res) { if renderer.CanDisplay(res) {
page, err := renderer.MakePage(u, res, textWidth(), leftMargin(), usingProxy) page, err := renderer.MakePage(u, res, textWidth(), usingProxy)
// Rendering may have taken a while, make sure tab is still valid // Rendering may have taken a while, make sure tab is still valid
if !isValidTab(t) { if !isValidTab(t) {
return ret("", false) return ret("", false)

View File

@ -2,72 +2,69 @@ package display
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
"text/tabwriter"
"github.com/gdamore/tcell" "github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/config"
"gitlab.com/tslocum/cview" "gitlab.com/tslocum/cview"
) )
var helpCells = strings.TrimSpace(` var helpCells = strings.TrimSpace(
?|Bring up this help. You can scroll! "?\tBring up this help. You can scroll!\n" +
Enter|Close this help page "Esc\tLeave the help\n" +
Esc|Close this help page or any active modal popups "Arrow keys, h/j/k/l\tScroll and move a page.\n" +
Arrow keys, h/j/k/l|Scroll and move a page. "%s\tGo up a page in document\n" +
%s|Go up a page in document "%s\tGo down a page in document\n" +
%s|Go down a page in document "g\tGo to top of document\n" +
g|Go to top of document "G\tGo to bottom of document\n" +
G|Go to bottom of document "Tab\tNavigate to the next item in a popup.\n" +
Tab|Navigate to the next item in a popup. "Shift-Tab\tNavigate to the previous item in a popup.\n" +
Shift-Tab|Navigate to the previous item in a popup. "%s\tGo back in the history\n" +
%s|Go back in the history "%s\tGo forward in the history\n" +
%s|Go forward in the history "%s\tOpen bar at the bottom - type a URL, link number, search term.\n" +
%s|Open bar at the bottom - type a URL, link number, search term. "\tYou can also type two dots (..) to go up a directory in the URL.\n" +
|You can also type two dots (..) to go up a directory in the URL. "\tTyping new:N will open link number N in a new tab\n" +
|Typing new:N will open link number N in a new tab "\tinstead of the current one.\n" +
|instead of the current one. "%s\tGo to links 1-10 respectively.\n" +
%s|Go to links 1-10 respectively. "%s\tEdit current URL\n" +
%s|Edit current URL "Enter, Tab\tOn a page this will start link highlighting.\n" +
Enter, Tab|On a page this will start link highlighting. "\tPress Tab and Shift-Tab to pick different links.\n" +
|Press Tab and Shift-Tab to pick different links. "\tPress Enter again to go to one, or Esc to stop.\n" +
|Press Enter again to go to one, or Esc to stop. "%s\tGo to a specific tab. (Default: Shift-NUMBER)\n" +
%s|Go to a specific tab. (Default: Shift-NUMBER) "%s\tGo to the last tab.\n" +
%s|Go to the last tab. "%s\tPrevious tab\n" +
%s|Previous tab "%s\tNext tab\n" +
%s|Next tab "%s\tGo home\n" +
%s|Go home "%s\tNew tab, or if a link is selected,\n" +
%s|New tab, or if a link is selected, "\tthis will open the link in a new tab.\n" +
|this will open the link in a new tab. "%s\tClose tab. For now, only the right-most tab can be closed.\n" +
%s|Close tab. For now, only the right-most tab can be closed. "%s\tReload a page, discarding the cached version.\n" +
%s|Reload a page, discarding the cached version. "\tThis can also be used if you resize your terminal.\n" +
|This can also be used if you resize your terminal. "%s\tView bookmarks\n" +
%s|View bookmarks "%s\tAdd, change, or remove a bookmark for the current page.\n" +
%s|Add, change, or remove a bookmark for the current page. "%s\tSave the current page to your downloads.\n" +
%s|Save the current page to your downloads. "%s\tView subscriptions\n" +
%s|View subscriptions "%s\tAdd or update a subscription\n" +
%s|Add or update a subscription "%s\tQuit\n")
%s|Quit
`)
var helpTable = cview.NewTable(). var helpTable = cview.NewTextView()
SetSelectable(false, false).
SetBorders(false).
SetScrollBarVisibility(cview.ScrollBarNever)
// Help displays the help and keybindings. // Help displays the help and keybindings.
func Help() { func Help() {
helpTable.ScrollToBeginning() helpTable.ScrollToBeginning()
tabPages.SwitchToPage("help") panels.ShowPanel("help")
panels.SendToFront("help")
App.SetFocus(helpTable) App.SetFocus(helpTable)
App.Draw()
} }
func helpInit() { func helpInit() {
// Populate help table // Populate help table
helpTable.SetBackgroundColor(config.GetColor("bg"))
helpTable.SetPadding(0, 0, 1, 1)
helpTable.SetDoneFunc(func(key tcell.Key) { helpTable.SetDoneFunc(func(key tcell.Key) {
if key == tcell.KeyEsc || key == tcell.KeyEnter { if key == tcell.KeyEsc || key == tcell.KeyEnter {
tabPages.SwitchToPage(strconv.Itoa(curTab)) panels.HidePanel("help")
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
App.Draw() App.Draw()
} }
@ -102,34 +99,16 @@ func helpInit() {
config.GetKeyBinding(config.CmdQuit), config.GetKeyBinding(config.CmdQuit),
) )
rows := strings.Count(helpCells, "\n") + 1 lines := strings.Split(helpCells, "\n")
cells := strings.Split( w := tabwriter.NewWriter(helpTable, 0, 8, 2, ' ', 0)
strings.ReplaceAll(helpCells, "\n", "|"), for i, line := range lines {
"|") if i > 0 && line[0] != '\t' {
cell := 0 fmt.Fprintln(w, "\t")
extraRows := 0 // Rows continued from the previous, without spacing
for r := 0; r < rows; r++ {
for c := 0; c < 2; c++ {
var tableCell *cview.TableCell
if c == 0 {
// First column, the keybinding
tableCell = cview.NewTableCell(" " + cells[cell]).
SetAttributes(tcell.AttrBold).
SetAlign(cview.AlignLeft)
} else {
tableCell = cview.NewTableCell(" " + cells[cell])
}
if c == 0 && cells[cell] == "" || (cell > 0 && cells[cell-1] == "" && c == 1) {
// The keybinding column for this row was blank, meaning the explanation
// column is continued from the previous row.
// The row should be added without any spacing rows
helpTable.SetCell(((2*r)-extraRows/2)-1, c, tableCell)
extraRows++
} else {
helpTable.SetCell((2*r)-extraRows/2, c, tableCell) // Every other row, for readability
}
cell++
} }
fmt.Fprintln(w, line)
} }
tabPages.AddPage("help", helpTable, true, false)
w.Flush()
panels.AddPanel("help", helpTable, true, false)
} }

View File

@ -2,12 +2,11 @@ package display
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
"time" "time"
humanize "github.com/dustin/go-humanize" humanize "github.com/dustin/go-humanize"
"github.com/gdamore/tcell" "github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/config"
"github.com/spf13/viper" "github.com/spf13/viper"
"gitlab.com/tslocum/cview" "gitlab.com/tslocum/cview"
@ -16,103 +15,133 @@ import (
// This file contains code for 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 // The bookmark modal is in bookmarks.go
var infoModal = cview.NewModal(). var infoModal = cview.NewModal()
AddButtons([]string{"Ok"})
var errorModal = cview.NewModal(). var errorModal = cview.NewModal()
AddButtons([]string{"Ok"})
var inputModal = cview.NewModal() var inputModal = cview.NewModal()
var inputCh = make(chan string) var inputCh = make(chan string)
var inputModalText string // The current text of the input field in the modal var inputModalText string // The current text of the input field in the modal
var yesNoModal = cview.NewModal(). var yesNoModal = cview.NewModal()
AddButtons([]string{"Yes", "No"})
// Channel to receive yesNo answer on // Channel to receive yesNo answer on
var yesNoCh = make(chan bool) var yesNoCh = make(chan bool)
func modalInit() { func modalInit() {
tabPages.AddPage("info", infoModal, false, false). infoModal.AddButtons([]string{"Ok"})
AddPage("error", errorModal, false, false).
AddPage("input", inputModal, false, false). errorModal.AddButtons([]string{"Ok"})
AddPage("yesno", yesNoModal, false, false).
AddPage("bkmk", bkmkModal, false, false). yesNoModal.AddButtons([]string{"Yes", "No"})
AddPage("dlChoice", dlChoiceModal, false, false).
AddPage("dl", dlModal, false, false) panels.AddPanel("info", infoModal, false, false)
panels.AddPanel("error", errorModal, false, false)
panels.AddPanel("input", inputModal, false, false)
panels.AddPanel("yesno", yesNoModal, false, false)
// Color setup // Color setup
if viper.GetBool("a-general.color") { if viper.GetBool("a-general.color") {
infoModal.SetBackgroundColor(config.GetColor("info_modal_bg")). m := infoModal
SetButtonBackgroundColor(config.GetColor("btn_bg")). m.SetBackgroundColor(config.GetColor("info_modal_bg"))
SetButtonTextColor(config.GetColor("btn_text")). m.SetButtonBackgroundColor(config.GetColor("btn_bg"))
SetTextColor(config.GetColor("info_modal_text")) m.SetButtonTextColor(config.GetColor("btn_text"))
infoModal.GetFrame(). m.SetTextColor(config.GetColor("info_modal_text"))
SetBorderColor(config.GetColor("info_modal_text")). form := m.GetForm()
SetTitleColor(config.GetColor("info_modal_text")) form.SetButtonBackgroundColorFocused(config.GetColor("btn_text"))
form.SetButtonTextColorFocused(config.GetColor("btn_bg"))
frame := m.GetFrame()
frame.SetBorderColor(config.GetColor("info_modal_text"))
frame.SetTitleColor(config.GetColor("info_modal_text"))
errorModal.SetBackgroundColor(config.GetColor("error_modal_bg")). m = errorModal
SetButtonBackgroundColor(config.GetColor("btn_bg")). m.SetBackgroundColor(config.GetColor("error_modal_bg"))
SetButtonTextColor(config.GetColor("btn_text")). m.SetButtonBackgroundColor(config.GetColor("btn_bg"))
SetTextColor(config.GetColor("error_modal_text")) m.SetButtonTextColor(config.GetColor("btn_text"))
errorModal.GetFrame(). m.SetTextColor(config.GetColor("error_modal_text"))
SetBorderColor(config.GetColor("error_modal_text")). form = m.GetForm()
SetTitleColor(config.GetColor("error_modal_text")) form.SetButtonBackgroundColorFocused(config.GetColor("btn_text"))
form.SetButtonTextColorFocused(config.GetColor("btn_bg"))
frame = errorModal.GetFrame()
frame.SetBorderColor(config.GetColor("error_modal_text"))
frame.SetTitleColor(config.GetColor("error_modal_text"))
inputModal.SetBackgroundColor(config.GetColor("input_modal_bg")). m = inputModal
SetButtonBackgroundColor(config.GetColor("btn_bg")). m.SetBackgroundColor(config.GetColor("input_modal_bg"))
SetButtonTextColor(config.GetColor("btn_text")). m.SetButtonBackgroundColor(config.GetColor("btn_bg"))
SetTextColor(config.GetColor("input_modal_text")) m.SetButtonTextColor(config.GetColor("btn_text"))
inputModal.GetFrame(). m.SetTextColor(config.GetColor("input_modal_text"))
SetBorderColor(config.GetColor("input_modal_text")). frame = inputModal.GetFrame()
SetTitleColor(config.GetColor("input_modal_text")) frame.SetBorderColor(config.GetColor("input_modal_text"))
inputModal.GetForm(). frame.SetTitleColor(config.GetColor("input_modal_text"))
SetFieldBackgroundColor(config.GetColor("input_modal_field_bg")). form = inputModal.GetForm()
SetFieldTextColor(config.GetColor("input_modal_field_text")) form.SetFieldBackgroundColor(config.GetColor("input_modal_field_bg"))
form.SetFieldTextColor(config.GetColor("input_modal_field_text"))
form.SetButtonBackgroundColorFocused(config.GetColor("btn_text"))
form.SetButtonTextColorFocused(config.GetColor("btn_bg"))
yesNoModal.SetButtonBackgroundColor(config.GetColor("btn_bg")). m = yesNoModal
SetButtonTextColor(config.GetColor("btn_text")) m.SetButtonBackgroundColor(config.GetColor("btn_bg"))
m.SetButtonTextColor(config.GetColor("btn_text"))
form = m.GetForm()
form.SetButtonBackgroundColorFocused(config.GetColor("btn_text"))
form.SetButtonTextColorFocused(config.GetColor("btn_bg"))
} else { } else {
infoModal.SetBackgroundColor(tcell.ColorBlack). m := infoModal
SetButtonBackgroundColor(tcell.ColorWhite). m.SetBackgroundColor(tcell.ColorBlack)
SetButtonTextColor(tcell.ColorBlack). m.SetButtonBackgroundColor(tcell.ColorWhite)
SetTextColor(tcell.ColorWhite) m.SetButtonTextColor(tcell.ColorBlack)
infoModal.GetFrame(). m.SetTextColor(tcell.ColorWhite)
SetBorderColor(tcell.ColorWhite). form := m.GetForm()
SetTitleColor(tcell.ColorWhite) form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
form.SetButtonTextColorFocused(tcell.ColorWhite)
frame := infoModal.GetFrame()
frame.SetBorderColor(tcell.ColorWhite)
frame.SetTitleColor(tcell.ColorWhite)
errorModal.SetBackgroundColor(tcell.ColorBlack). m = errorModal
SetButtonBackgroundColor(tcell.ColorWhite). m.SetBackgroundColor(tcell.ColorBlack)
SetButtonTextColor(tcell.ColorBlack). m.SetButtonBackgroundColor(tcell.ColorWhite)
SetTextColor(tcell.ColorWhite) m.SetButtonTextColor(tcell.ColorBlack)
errorModal.GetFrame(). m.SetTextColor(tcell.ColorWhite)
SetBorderColor(tcell.ColorWhite). form = m.GetForm()
SetTitleColor(tcell.ColorWhite) form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
form.SetButtonTextColorFocused(tcell.ColorWhite)
frame = errorModal.GetFrame()
frame.SetBorderColor(tcell.ColorWhite)
frame.SetTitleColor(tcell.ColorWhite)
inputModal.SetBackgroundColor(tcell.ColorBlack). m = inputModal
SetButtonBackgroundColor(tcell.ColorWhite). m.SetBackgroundColor(tcell.ColorBlack)
SetButtonTextColor(tcell.ColorBlack). m.SetButtonBackgroundColor(tcell.ColorWhite)
SetTextColor(tcell.ColorWhite) m.SetButtonTextColor(tcell.ColorBlack)
inputModal.GetFrame(). m.SetTextColor(tcell.ColorWhite)
SetBorderColor(tcell.ColorWhite). frame = inputModal.GetFrame()
SetTitleColor(tcell.ColorWhite) frame.SetBorderColor(tcell.ColorWhite)
inputModal.GetForm(). frame.SetTitleColor(tcell.ColorWhite)
SetFieldBackgroundColor(tcell.ColorWhite). form = inputModal.GetForm()
SetFieldTextColor(tcell.ColorBlack) form.SetFieldBackgroundColor(tcell.ColorWhite)
form.SetFieldTextColor(tcell.ColorBlack)
form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
form.SetButtonTextColorFocused(tcell.ColorWhite)
// YesNo background color is changed in funcs // YesNo background color is changed in funcs
yesNoModal.SetButtonBackgroundColor(tcell.ColorWhite). m = yesNoModal
SetButtonTextColor(tcell.ColorBlack) m.SetButtonBackgroundColor(tcell.ColorWhite)
m.SetButtonTextColor(tcell.ColorBlack)
form = m.GetForm()
form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
form.SetButtonTextColorFocused(tcell.ColorWhite)
} }
// Modal functions that can't be added up above, because they return the wrong type // Modal functions that can't be added up above, because they return the wrong type
infoModal.SetBorder(true) infoModal.SetBorder(true)
infoModal.GetFrame(). frame := infoModal.GetFrame()
SetTitleAlign(cview.AlignCenter). frame.SetTitleAlign(cview.AlignCenter)
SetTitle(" Info ") frame.SetTitle(" Info ")
infoModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { infoModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
tabPages.SwitchToPage(strconv.Itoa(curTab)) panels.HidePanel("info")
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
App.Draw() App.Draw()
}) })
@ -120,15 +149,15 @@ func modalInit() {
errorModal.SetBorder(true) errorModal.SetBorder(true)
errorModal.GetFrame().SetTitleAlign(cview.AlignCenter) errorModal.GetFrame().SetTitleAlign(cview.AlignCenter)
errorModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { errorModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
tabPages.SwitchToPage(strconv.Itoa(curTab)) panels.HidePanel("error")
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
App.Draw() App.Draw()
}) })
inputModal.SetBorder(true) inputModal.SetBorder(true)
inputModal.GetFrame(). frame = inputModal.GetFrame()
SetTitleAlign(cview.AlignCenter). frame.SetTitleAlign(cview.AlignCenter)
SetTitle(" Input ") frame.SetTitle(" Input ")
inputModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { inputModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
if buttonLabel == "Send" { if buttonLabel == "Send" {
inputCh <- inputModalText inputCh <- inputModalText
@ -167,8 +196,8 @@ func Error(title, text string) {
errorModal.GetFrame().SetTitle(title) errorModal.GetFrame().SetTitle(title)
errorModal.SetText(text) errorModal.SetText(text)
tabPages.ShowPage("error") panels.ShowPanel("error")
tabPages.SendToFront("error") panels.SendToFront("error")
App.SetFocus(errorModal) App.SetFocus(errorModal)
App.Draw() App.Draw()
} }
@ -176,8 +205,8 @@ func Error(title, text string) {
// Info displays some info on the screen in a modal. // Info displays some info on the screen in a modal.
func Info(s string) { func Info(s string) {
infoModal.SetText(s) infoModal.SetText(s)
tabPages.ShowPage("info") panels.ShowPanel("info")
tabPages.SendToFront("info") panels.SendToFront("info")
App.SetFocus(infoModal) App.SetFocus(infoModal)
App.Draw() App.Draw()
} }
@ -198,14 +227,14 @@ func Input(prompt string) (string, bool) {
}) })
inputModal.SetText(prompt + " ") inputModal.SetText(prompt + " ")
tabPages.ShowPage("input") panels.ShowPanel("input")
tabPages.SendToFront("input") panels.SendToFront("input")
App.SetFocus(inputModal) App.SetFocus(inputModal)
App.Draw() App.Draw()
resp := <-inputCh resp := <-inputCh
tabPages.SwitchToPage(strconv.Itoa(curTab)) panels.HidePanel("input")
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
App.Draw() App.Draw()
@ -218,29 +247,29 @@ func Input(prompt string) (string, bool) {
// YesNo displays a modal asking a yes-or-no question. // YesNo displays a modal asking a yes-or-no question.
func YesNo(prompt string) bool { func YesNo(prompt string) bool {
if viper.GetBool("a-general.color") { if viper.GetBool("a-general.color") {
yesNoModal. m := yesNoModal
SetBackgroundColor(config.GetColor("yesno_modal_bg")). m.SetBackgroundColor(config.GetColor("yesno_modal_bg"))
SetTextColor(config.GetColor("yesno_modal_text")) m.SetTextColor(config.GetColor("yesno_modal_text"))
yesNoModal.GetFrame(). frame := yesNoModal.GetFrame()
SetBorderColor(config.GetColor("yesno_modal_text")). frame.SetBorderColor(config.GetColor("yesno_modal_text"))
SetTitleColor(config.GetColor("yesno_modal_text")) frame.SetTitleColor(config.GetColor("yesno_modal_text"))
} else { } else {
yesNoModal. m := yesNoModal
SetBackgroundColor(tcell.ColorBlack). m.SetBackgroundColor(tcell.ColorBlack)
SetTextColor(tcell.ColorWhite) m.SetTextColor(tcell.ColorWhite)
yesNoModal.GetFrame(). frame := yesNoModal.GetFrame()
SetBorderColor(tcell.ColorWhite). frame.SetBorderColor(tcell.ColorWhite)
SetTitleColor(tcell.ColorWhite) frame.SetTitleColor(tcell.ColorWhite)
} }
yesNoModal.GetFrame().SetTitle("") yesNoModal.GetFrame().SetTitle("")
yesNoModal.SetText(prompt) yesNoModal.SetText(prompt)
tabPages.ShowPage("yesno") panels.ShowPanel("yesno")
tabPages.SendToFront("yesno") panels.SendToFront("yesno")
App.SetFocus(yesNoModal) App.SetFocus(yesNoModal)
App.Draw() App.Draw()
resp := <-yesNoCh resp := <-yesNoCh
tabPages.SwitchToPage(strconv.Itoa(curTab)) panels.HidePanel("yesno")
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
App.Draw() App.Draw()
return resp return resp
@ -251,36 +280,34 @@ func YesNo(prompt string) bool {
func Tofu(host string, expiry time.Time) bool { func Tofu(host string, expiry time.Time) bool {
// Reuses yesNoModal, with error color // Reuses yesNoModal, with error color
m := yesNoModal
frame := yesNoModal.GetFrame()
if viper.GetBool("a-general.color") { if viper.GetBool("a-general.color") {
yesNoModal. m.SetBackgroundColor(config.GetColor("tofu_modal_bg"))
SetBackgroundColor(config.GetColor("tofu_modal_bg")). m.SetTextColor(config.GetColor("tofu_modal_text"))
SetTextColor(config.GetColor("tofu_modal_text")) frame.SetBorderColor(config.GetColor("tofu_modal_text"))
yesNoModal.GetFrame(). frame.SetTitleColor(config.GetColor("tofu_modal_text"))
SetBorderColor(config.GetColor("tofu_modal_text")).
SetTitleColor(config.GetColor("tofu_modal_text"))
} else { } else {
yesNoModal. m.SetBackgroundColor(tcell.ColorBlack)
SetBackgroundColor(tcell.ColorBlack). m.SetTextColor(tcell.ColorWhite)
SetTextColor(tcell.ColorWhite) m.SetBorderColor(tcell.ColorWhite)
yesNoModal. m.SetTitleColor(tcell.ColorWhite)
SetBorderColor(tcell.ColorWhite).
SetTitleColor(tcell.ColorWhite)
} }
yesNoModal.GetFrame().SetTitle(" TOFU ") frame.SetTitle(" TOFU ")
yesNoModal.SetText( m.SetText(
//nolint:lll //nolint:lll
fmt.Sprintf("%s's certificate has changed, possibly indicating an security issue. The certificate would have expired %s. Are you sure you want to continue? ", fmt.Sprintf("%s's certificate has changed, possibly indicating an security issue. The certificate would have expired %s. Are you sure you want to continue? ",
host, host,
humanize.Time(expiry), humanize.Time(expiry),
), ),
) )
tabPages.ShowPage("yesno") panels.ShowPanel("yesno")
tabPages.SendToFront("yesno") panels.SendToFront("yesno")
App.SetFocus(yesNoModal) App.SetFocus(yesNoModal)
App.Draw() App.Draw()
resp := <-yesNoCh resp := <-yesNoCh
tabPages.SwitchToPage(strconv.Itoa(curTab)) panels.HidePanel("yesno")
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
App.Draw() App.Draw()
return resp return resp

View File

@ -1,15 +1,12 @@
package display package display
import ( import (
"fmt"
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
"github.com/makeworld-the-better-one/amfora/config"
"github.com/makeworld-the-better-one/amfora/renderer" "github.com/makeworld-the-better-one/amfora/renderer"
"github.com/makeworld-the-better-one/amfora/structs" "github.com/makeworld-the-better-one/amfora/structs"
"github.com/spf13/viper"
) )
// This file contains the functions that aren't part of the public API. // This file contains the functions that aren't part of the public API.
@ -68,11 +65,11 @@ func reformatPage(p *structs.Page) {
strings.HasPrefix(p.URL, "file") { strings.HasPrefix(p.URL, "file") {
proxied = false proxied = false
} }
rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), leftMargin(), proxied) rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), proxied)
case structs.TextPlain: case structs.TextPlain:
rendered = renderer.RenderPlainText(p.Raw, leftMargin()) rendered = renderer.RenderPlainText(p.Raw)
case structs.TextAnsi: case structs.TextAnsi:
rendered = renderer.RenderANSI(p.Raw, leftMargin()) rendered = renderer.RenderANSI(p.Raw)
default: default:
// Rendering this type is not implemented // Rendering this type is not implemented
return return
@ -92,6 +89,8 @@ func reformatPageAndSetView(t *tab, p *structs.Page) {
reformatPage(p) reformatPage(p)
t.view.SetText(p.Content) t.view.SetText(p.Content)
t.applyScroll() // Go back to where you were, roughly t.applyScroll() // Go back to where you were, roughly
App.Draw()
} }
// setPage displays a Page on the passed tab number. // setPage displays a Page on the passed tab number.
@ -107,20 +106,23 @@ func setPage(t *tab, p *structs.Page) {
// Make sure the page content is fitted to the terminal every time it's displayed // Make sure the page content is fitted to the terminal every time it's displayed
reformatPage(p) reformatPage(p)
oldFav := t.page.Favicon
t.page = p t.page = p
go func() {
parsed, _ := url.Parse(p.URL)
handleFavicon(t, parsed.Host, oldFav)
}()
// Change page on screen // Change page on screen
t.view.SetText(p.Content) t.view.SetText(p.Content)
t.view.Highlight("") // Turn off highlights, other funcs may restore if necessary t.view.Highlight("") // Turn off highlights, other funcs may restore if necessary
t.view.ScrollToBeginning() t.view.ScrollToBeginning()
// Set tab number in case a favicon from before overwrote it
tabNum := tabNumber(t)
browser.SetTabLabel(strconv.Itoa(tabNum), makeTabLabel(strconv.Itoa(tabNum+1)))
App.Draw()
go func() {
parsed, _ := url.Parse(p.URL)
handleFavicon(t, parsed.Host)
}()
// Setup display // Setup display
App.SetFocus(t.view) App.SetFocus(t.view)
@ -144,32 +146,3 @@ func goURL(t *tab, u string) {
t.applyBottomBar() t.applyBottomBar()
} }
} }
// 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()
}

View File

@ -9,7 +9,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/gdamore/tcell" "github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/cache" "github.com/makeworld-the-better-one/amfora/cache"
"github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/config"
"github.com/makeworld-the-better-one/amfora/renderer" "github.com/makeworld-the-better-one/amfora/renderer"
@ -149,7 +149,7 @@ func Subscriptions(t *tab, u string) string {
} }
} }
content, links := renderer.RenderGemini(rawPage, textWidth(), leftMargin(), false) content, links := renderer.RenderGemini(rawPage, textWidth(), false)
page := structs.Page{ page := structs.Page{
Raw: rawPage, Raw: rawPage,
Content: content, Content: content,
@ -191,7 +191,7 @@ func ManageSubscriptions(t *tab, u string) {
) )
} }
content, links := renderer.RenderGemini(rawPage, textWidth(), leftMargin(), false) content, links := renderer.RenderGemini(rawPage, textWidth(), false)
page := structs.Page{ page := structs.Page{
Raw: rawPage, Raw: rawPage,
Content: content, Content: content,
@ -230,19 +230,19 @@ func openSubscriptionModal(validFeed, subscribed bool) bool {
// Reuses yesNoModal // Reuses yesNoModal
if viper.GetBool("a-general.color") { if viper.GetBool("a-general.color") {
yesNoModal. m := yesNoModal
SetBackgroundColor(config.GetColor("subscription_modal_bg")). m.SetBackgroundColor(config.GetColor("subscription_modal_bg"))
SetTextColor(config.GetColor("subscription_modal_text")) m.SetTextColor(config.GetColor("subscription_modal_text"))
yesNoModal.GetFrame(). frame := yesNoModal.GetFrame()
SetBorderColor(config.GetColor("subscription_modal_text")). frame.SetBorderColor(config.GetColor("subscription_modal_text"))
SetTitleColor(config.GetColor("subscription_modal_text")) frame.SetTitleColor(config.GetColor("subscription_modal_text"))
} else { } else {
yesNoModal. m := yesNoModal
SetBackgroundColor(tcell.ColorBlack). m.SetBackgroundColor(tcell.ColorBlack)
SetTextColor(tcell.ColorWhite) m.SetTextColor(tcell.ColorWhite)
yesNoModal.GetFrame(). frame := yesNoModal.GetFrame()
SetBorderColor(tcell.ColorWhite). frame.SetBorderColor(tcell.ColorWhite)
SetTitleColor(tcell.ColorWhite) frame.SetTitleColor(tcell.ColorWhite)
} }
if validFeed { if validFeed {
yesNoModal.GetFrame().SetTitle("Feed Subscription") yesNoModal.GetFrame().SetTitle("Feed Subscription")
@ -260,13 +260,13 @@ func openSubscriptionModal(validFeed, subscribed bool) bool {
} }
} }
tabPages.ShowPage("yesno") panels.ShowPanel("yesno")
tabPages.SendToFront("yesno") panels.SendToFront("yesno")
App.SetFocus(yesNoModal) App.SetFocus(yesNoModal)
App.Draw() App.Draw()
resp := <-yesNoCh resp := <-yesNoCh
tabPages.SwitchToPage(strconv.Itoa(curTab)) panels.HidePanel("yesno")
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
App.Draw() App.Draw()
return resp return resp

View File

@ -3,9 +3,9 @@ package display
import ( import (
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/gdamore/tcell" "github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/config"
"github.com/makeworld-the-better-one/amfora/structs" "github.com/makeworld-the-better-one/amfora/structs"
"gitlab.com/tslocum/cview" "gitlab.com/tslocum/cview"
) )
@ -24,33 +24,34 @@ type tabHistory struct {
// tab hold the information needed for each browser tab. // tab hold the information needed for each browser tab.
type tab struct { type tab struct {
page *structs.Page page *structs.Page
view *cview.TextView view *cview.TextView
history *tabHistory history *tabHistory
mode tabMode mode tabMode
reformatMu *sync.Mutex // Mutex for reformatting, so there's only one reformat job at once barLabel string // The bottomBar label for the tab
barLabel string // The bottomBar label for the tab barText string // The bottomBar text for the tab
barText string // The bottomBar text for the tab
} }
// makeNewTab initializes an tab struct with no content. // makeNewTab initializes an tab struct with no content.
func makeNewTab() *tab { func makeNewTab() *tab {
t := tab{ t := tab{
page: &structs.Page{Mode: structs.ModeOff}, page: &structs.Page{Mode: structs.ModeOff},
view: cview.NewTextView(). view: cview.NewTextView(),
SetDynamicColors(true). history: &tabHistory{},
SetRegions(true). mode: tabModeDone,
SetScrollable(true).
SetWrap(false).
SetChangedFunc(func() {
App.Draw()
}),
history: &tabHistory{},
reformatMu: &sync.Mutex{},
mode: tabModeDone,
} }
t.view.SetDynamicColors(true)
t.view.SetRegions(true)
t.view.SetScrollable(true)
t.view.SetWrap(false)
t.view.SetScrollBarVisibility(config.ScrollBar)
t.view.SetScrollBarColor(config.GetColor("scrollbar"))
t.view.SetChangedFunc(func() {
App.Draw()
})
t.view.SetDoneFunc(func(key tcell.Key) { t.view.SetDoneFunc(func(key tcell.Key) {
// Altered from: https://gitlab.com/tslocum/cview/-/blob/master/demos/textview/main.go // Altered from:
// https://gitlab.com/tslocum/cview/-/blob/1f765c8695c3f4b35dae57f469d3aee0b1adbde7/demos/textview/main.go
// Handles being able to select and "click" links with the enter and tab keys // Handles being able to select and "click" links with the enter and tab keys
tab := curTab // Don't let it change in the middle of the code tab := curTab // Don't let it change in the middle of the code
@ -89,7 +90,8 @@ func makeNewTab() *tab {
// They've started link highlighting // They've started link highlighting
tabs[tab].page.Mode = structs.ModeLinkSelect tabs[tab].page.Mode = structs.ModeLinkSelect
tabs[tab].view.Highlight("0").ScrollToHighlight() tabs[tab].view.Highlight("0")
tabs[tab].view.ScrollToHighlight()
// Display link URL in bottomBar // Display link URL in bottomBar
bottomBar.SetLabel("[::b]Link: [::-]") bottomBar.SetLabel("[::b]Link: [::-]")
bottomBar.SetText(tabs[tab].page.Links[0]) bottomBar.SetText(tabs[tab].page.Links[0])
@ -109,7 +111,8 @@ func makeNewTab() *tab {
} else { } else {
return return
} }
tabs[tab].view.Highlight(strconv.Itoa(index)).ScrollToHighlight() tabs[tab].view.Highlight(strconv.Itoa(index))
tabs[tab].view.ScrollToHighlight()
// Display link URL in bottomBar // Display link URL in bottomBar
bottomBar.SetLabel("[::b]Link: [::-]") bottomBar.SetLabel("[::b]Link: [::-]")
bottomBar.SetText(tabs[tab].page.Links[index]) bottomBar.SetText(tabs[tab].page.Links[index])

View File

@ -13,6 +13,43 @@ import (
// This file contains funcs that are small, self-contained utilities. // This file contains funcs that are small, self-contained utilities.
// makeContentLayout returns a flex that contains the given TextView
// along with the current correct left margin, as well as a single empty
// line at the top, for a top margin.
func makeContentLayout(tv *cview.TextView) *cview.Flex {
// Create horizontal flex with the left margin as an empty space
horiz := cview.NewFlex()
horiz.SetDirection(cview.FlexColumn)
horiz.AddItem(nil, leftMargin(), 0, false)
horiz.AddItem(tv, 0, 1, true)
// Create a vertical flex with the other one and a top margin
vert := cview.NewFlex()
vert.SetDirection(cview.FlexRow)
vert.AddItem(nil, 1, 0, false)
vert.AddItem(horiz, 0, 1, true)
return vert
}
// makeTabLabel takes a string and adds spacing to it, making it
// suitable for display as a tab label.
func makeTabLabel(s string) string {
return " " + s + " "
}
// tabNumber gets the index of the tab in the tabs slice. It returns -1
// if the tab is not in that slice.
func tabNumber(t *tab) int {
tempTabs := tabs
for i := range tempTabs {
if tempTabs[i] == t {
return i
}
}
return -1
}
// escapeMeta santizes a META string for use within a cview modal. // escapeMeta santizes a META string for use within a cview modal.
func escapeMeta(meta string) string { func escapeMeta(meta string) string {
return cview.Escape(strings.ReplaceAll(meta, "\n", "")) return cview.Escape(strings.ReplaceAll(meta, "\n", ""))
@ -20,13 +57,7 @@ func escapeMeta(meta string) string {
// isValidTab indicates whether the passed tab is still being used, even if it's not currently displayed. // isValidTab indicates whether the passed tab is still being used, even if it's not currently displayed.
func isValidTab(t *tab) bool { func isValidTab(t *tab) bool {
tempTabs := tabs return tabNumber(t) != -1
for i := range tempTabs {
if tempTabs[i] == t {
return true
}
}
return false
} }
func leftMargin() int { func leftMargin() int {

10
go.mod
View File

@ -5,7 +5,7 @@ go 1.14
require ( require (
github.com/dustin/go-humanize v1.0.0 github.com/dustin/go-humanize v1.0.0
github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606 github.com/gdamore/tcell/v2 v2.1.1-0.20210125004847-19e17097d8fe
github.com/google/go-cmp v0.5.0 // indirect github.com/google/go-cmp v0.5.0 // indirect
github.com/makeworld-the-better-one/go-gemini v0.11.0 github.com/makeworld-the-better-one/go-gemini v0.11.0
github.com/makeworld-the-better-one/go-isemoji v1.1.0 github.com/makeworld-the-better-one/go-isemoji v1.1.0
@ -19,12 +19,12 @@ require (
github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.0 github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.6.1
gitlab.com/tslocum/cview v1.4.8-0.20200713214710-cc7796c4ca44 gitlab.com/tslocum/cview v1.5.4-0.20210207045010-d776e728ef6d
golang.org/x/text v0.3.5-0.20201208001344-75a595aef632 golang.org/x/text v0.3.5
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/ini.v1 v1.57.0 // indirect gopkg.in/ini.v1 v1.62.0 // indirect
) )
replace github.com/mmcdole/gofeed => github.com/makeworld-the-better-one/gofeed v1.1.1-0.20201123002655-c0c6354134fe replace github.com/mmcdole/gofeed => github.com/makeworld-the-better-one/gofeed v1.1.1-0.20201123002655-c0c6354134fe

45
go.sum
View File

@ -14,7 +14,7 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
@ -50,9 +50,9 @@ 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/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 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= github.com/gdamore/tcell/v2 v2.0.0-dev/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606 h1:Y00kKKKYVyn7InlCMRcnZbwcjHFIsgkjU0Bn1F5re4o= github.com/gdamore/tcell/v2 v2.1.1-0.20210125004847-19e17097d8fe h1:D4zQq/0ep0XCtgkmA+dUvQNYMoiW2+2336rdlAucr10=
github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0= github.com/gdamore/tcell/v2 v2.1.1-0.20210125004847-19e17097d8fe/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -128,9 +128,10 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 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 h1:MNGiULJFvcqls9oCy40tE897hDeKvNmEK9i5kRucgQk=
@ -144,11 +145,11 @@ github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20201220005701-b036c
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 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.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@ -192,8 +193,9 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rkoesters/xdg v0.0.0-20181125232953-edd15b846f9b h1:8NiY6v9/IlFU8osj1L7kqzRbrG6e3izRQQjGze1Q1R0= github.com/rkoesters/xdg v0.0.0-20181125232953-edd15b846f9b h1:8NiY6v9/IlFU8osj1L7kqzRbrG6e3izRQQjGze1Q1R0=
github.com/rkoesters/xdg v0.0.0-20181125232953-edd15b846f9b/go.mod h1:T1HolqzmdHnJIH6p7A9LDuvYGQgEHx9ijX3vKgDKU60= github.com/rkoesters/xdg v0.0.0-20181125232953-edd15b846f9b/go.mod h1:T1HolqzmdHnJIH6p7A9LDuvYGQgEHx9ijX3vKgDKU60=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@ -221,8 +223,8 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -235,10 +237,10 @@ 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/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/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
gitlab.com/tslocum/cbind v0.1.1 h1:JXXtxMWHgWLvoF+QkrvcNvOQ59juy7OE1RhT7hZfdt0= gitlab.com/tslocum/cbind v0.1.4 h1:cbZXPPcieXspk8cShoT6efz7HAT8yMNQcofYWNizis4=
gitlab.com/tslocum/cbind v0.1.1/go.mod h1:rX7vkl0pUSg/yy427MmD1FZAf99S7WwpUlxF/qTpPqk= gitlab.com/tslocum/cbind v0.1.4/go.mod h1:RvwYE3auSjBNlCmWeGspzn+jdLUVQ8C2QGC+0nP9ChI=
gitlab.com/tslocum/cview v1.4.8-0.20200713214710-cc7796c4ca44 h1:YddMqXJ6jI3SkP8Nfxc+S2pcvI5o8mmXmHL2D9hkwQI= gitlab.com/tslocum/cview v1.5.4-0.20210207045010-d776e728ef6d h1:ck3gAnCoraAI2doDfH2MZsz+DxVpvNwnaXa453jH5aI=
gitlab.com/tslocum/cview v1.4.8-0.20200713214710-cc7796c4ca44/go.mod h1:QctoEJaR2AqZTy0KKo12P1ZjHgQJyVkAXaeDanBBhlE= gitlab.com/tslocum/cview v1.5.4-0.20210207045010-d776e728ef6d/go.mod h1:lCEqP/zDhBihNbyiEn59LgOCk09ejefHaS7kNZ57Nmc=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 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.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@ -315,19 +317,22 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201113135734-0a15ea8d9b02/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 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.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.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 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5-0.20201208001344-75a595aef632 h1:clKlpQ6BheG1zIRhU2SPRAXpLgol/tqWVEeRkjpsaDI= golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5-0.20201208001344-75a595aef632/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/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-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/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= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -378,8 +383,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 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.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.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -69,7 +69,7 @@ func CanDisplay(res *gemini.Response) bool {
// MakePage creates a formatted, rendered Page from the given network response and params. // MakePage creates a formatted, rendered Page from the given network response and params.
// You must set the Page.Width value yourself. // You must set the Page.Width value yourself.
func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied bool) (*structs.Page, error) { func MakePage(url string, res *gemini.Response, width int, proxied bool) (*structs.Page, error) {
if !CanDisplay(res) { if !CanDisplay(res) {
return nil, ErrCantDisplay return nil, ErrCantDisplay
} }
@ -112,7 +112,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b
} }
if mediatype == "text/gemini" { if mediatype == "text/gemini" {
rendered, links := RenderGemini(utfText, width, leftMargin, proxied) rendered, links := RenderGemini(utfText, width, proxied)
return &structs.Page{ return &structs.Page{
Mediatype: structs.TextGemini, Mediatype: structs.TextGemini,
RawMediatype: mediatype, RawMediatype: mediatype,
@ -130,7 +130,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b
RawMediatype: mediatype, RawMediatype: mediatype,
URL: url, URL: url,
Raw: utfText, Raw: utfText,
Content: RenderANSI(utfText, leftMargin), Content: RenderANSI(utfText),
Links: []string{}, Links: []string{},
MadeAt: time.Now(), MadeAt: time.Now(),
}, nil }, nil
@ -142,7 +142,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b
RawMediatype: mediatype, RawMediatype: mediatype,
URL: url, URL: url,
Raw: utfText, Raw: utfText,
Content: RenderPlainText(utfText, leftMargin), Content: RenderPlainText(utfText),
Links: []string{}, Links: []string{},
MadeAt: time.Now(), MadeAt: time.Now(),
}, nil }, nil

View File

@ -21,31 +21,27 @@ var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;]*m`)
// RenderANSI renders plain text pages containing ANSI codes. // RenderANSI renders plain text pages containing ANSI codes.
// Practically, it is used for the text/x-ansi. // Practically, it is used for the text/x-ansi.
func RenderANSI(s string, leftMargin int) string { func RenderANSI(s string) string {
s = cview.Escape(s) s = cview.Escape(s)
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") { if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
s = cview.TranslateANSI(s) s = cview.TranslateANSI(s)
// The TranslateANSI function injects tags like [-:-:-]
// but this will reset the background to use the user's terminal color.
// These tags need to be replaced with resets that use the theme color.
s = strings.ReplaceAll(s, "[-:-:-]",
fmt.Sprintf("[-:%s:-]", config.GetColorString("bg")))
} else { } else {
s = ansiRegex.ReplaceAllString(s, "") s = ansiRegex.ReplaceAllString(s, "")
} }
var shifted string return s
lines := strings.Split(s, "\n")
for i := range lines {
shifted += strings.Repeat(" ", leftMargin) + lines[i] + "\n"
}
return shifted
} }
// RenderPlainText should be used to format plain text pages. // RenderPlainText should be used to format plain text pages.
func RenderPlainText(s string, leftMargin int) string { func RenderPlainText(s string) string {
var shifted string // It used to add a left margin, now this is done elsewhere.
lines := strings.Split(cview.Escape(s), "\n") // The function is kept for convenience and in case rendering
for i := range lines { // is needed in the future.
shifted += strings.Repeat(" ", leftMargin) + return s
"[" + config.GetColorString("regular_text") + "]" + lines[i] + "[-]" +
"\n"
}
return shifted
} }
// wrapLine wraps a line to the provided width, and adds the provided prefix and suffix to each wrapped line. // wrapLine wraps a line to the provided width, and adds the provided prefix and suffix to each wrapped line.
@ -278,7 +274,7 @@ func convertRegularGemini(s string, numLinks, width int, proxied bool) (string,
// //
// proxied is whether the request is through the gemini:// scheme. // proxied is whether the request is through the gemini:// scheme.
// If it's not a gemini:// page, set this to true. // If it's not a gemini:// page, set this to true.
func RenderGemini(s string, width, leftMargin int, proxied bool) (string, []string) { func RenderGemini(s string, width int, proxied bool) (string, []string) {
s = cview.Escape(s) s = cview.Escape(s)
lines := strings.Split(s, "\n") lines := strings.Split(s, "\n")
@ -289,30 +285,52 @@ func RenderGemini(s string, width, leftMargin int, proxied bool) (string, []stri
rendered := "" // Final result rendered := "" // Final result
pre := false pre := false
buf := "" // Block of regular or preformatted lines buf := "" // Block of regular or preformatted lines
// processPre is for rendering preformatted blocks
processPre := func() {
// Support ANSI color codes in preformatted blocks - see #59
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
buf = cview.TranslateANSI(buf)
// The TranslateANSI function injects tags like [-:-:-]
// but this will reset the background to use the user's terminal color.
// These tags need to be replaced with resets that use the theme color.
buf = strings.ReplaceAll(buf, "[-:-:-]",
fmt.Sprintf("[%s:%s:-]", config.GetColorString("preformatted_text"), config.GetColorString("bg")))
} else {
buf = ansiRegex.ReplaceAllString(buf, "")
}
// The final newline is removed (and re-added) to prevent background glitches
// where the terminal background color slips through. This only happens on
// preformatted blocks with ANSI characters.
//
// Lines are modified below to always end with \r\n
buf = strings.TrimSuffix(buf, "\r\n")
rendered += fmt.Sprintf("[%s]", config.GetColorString("preformatted_text")) +
buf + fmt.Sprintf("[%s:%s:-]\r\n", config.GetColorString("regular_text"), config.GetColorString("bg"))
}
// processRegular processes non-preformatted sections
processRegular := func() {
// ANSI not allowed in regular text - see #59
buf = ansiRegex.ReplaceAllString(buf, "")
ren, lks := convertRegularGemini(buf, len(links), width, proxied)
links = append(links, lks...)
rendered += ren
}
for i := range lines { for i := range lines {
if strings.HasPrefix(lines[i], "```") { if strings.HasPrefix(lines[i], "```") {
if pre { if pre {
// In a preformatted block, so add the text as is // In a preformatted block, so add the text as is
// Don't add the current line with backticks // Don't add the current line with backticks
processPre()
// Support ANSI color codes in preformatted blocks - see #59
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
buf = cview.TranslateANSI(buf)
} else {
buf = ansiRegex.ReplaceAllString(buf, "")
}
rendered += fmt.Sprintf("[%s]", config.GetColorString("preformatted_text")) +
buf + "[-]"
} else { } else {
// Not preformatted, regular text // Not preformatted, regular text
processRegular()
// ANSI not allowed in regular text - see #59
buf = ansiRegex.ReplaceAllString(buf, "")
ren, lks := convertRegularGemini(buf, len(links), width, proxied)
links = append(links, lks...)
rendered += ren
} }
buf = "" // Clear buffer for next block buf = "" // Clear buffer for next block
pre = !pre pre = !pre
@ -324,32 +342,10 @@ func RenderGemini(s string, width, leftMargin int, proxied bool) (string, []stri
// Gone through all the lines, but there still is likely a block in the buffer // Gone through all the lines, but there still is likely a block in the buffer
if pre { if pre {
// File ended without closing the preformatted block // File ended without closing the preformatted block
// Same code as in the loop above processPre()
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
buf = cview.TranslateANSI(buf)
} else {
buf = ansiRegex.ReplaceAllString(buf, "")
}
rendered += fmt.Sprintf("[%s]", config.GetColorString("preformatted_text")) +
buf + "[-]"
} else { } else {
// Not preformatted, regular text // Not preformatted, regular text
// Same code as in the loop above processRegular()
buf = ansiRegex.ReplaceAllString(buf, "")
ren, lks := convertRegularGemini(buf, len(links), width, proxied)
links = append(links, lks...)
rendered += ren
}
if leftMargin > 0 {
renLines := strings.Split(rendered, "\n")
for i := range renLines {
renLines[i] = strings.Repeat(" ", leftMargin) + renLines[i]
}
return strings.Join(renLines, "\n"), links
} }
return rendered, links return rendered, links

View File

@ -374,9 +374,9 @@ func updateAll() {
defer wg.Done() defer wg.Done()
for j := range jobs { for j := range jobs {
if j[0] == "feed" { if j[0] == "feed" {
updateFeed(j[1]) //nolint:errcheck updateFeed(j[1])
} else if j[0] == "page" { } else if j[0] == "page" {
updatePage(j[1]) //nolint:errcheck updatePage(j[1])
} }
} }
} }