mirror of
https://github.com/makeworld-the-better-one/amfora.git
synced 2024-08-17 18:50:25 +03: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:
parent
332aa6af9f
commit
9198572f34
2
.github/workflows/golangci-lint.yml
vendored
2
.github/workflows/golangci-lint.yml
vendored
@ -24,6 +24,6 @@ jobs:
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
# 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`.
|
||||
only-new-issues: true
|
||||
|
10
CHANGELOG.md
10
CHANGELOG.md
@ -8,8 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Added
|
||||
- **Media type handlers** - open non-text files in another application (#121, #134)
|
||||
- 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)
|
||||
|
||||
### Changed
|
||||
- Update cview to `d776e728ef6d2a9990a5cd86a70b31f0678613e2` for large performance and feature updates (#107)
|
||||
- Update to tcell v2 (dependency of cview)
|
||||
|
||||
### Fixed
|
||||
- Don't use cache when URL is typed in bottom bar (#159)
|
||||
- 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)
|
||||
- Whitespace around the URL entered in the bottom bar is stripped (#184)
|
||||
- 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
|
||||
|
16
NOTES.md
16
NOTES.md
@ -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
|
||||
|
||||
## 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 `""`
|
||||
- Waiting on [this viper PR](https://github.com/spf13/viper/pull/519) to be merged
|
||||
- Help table cells aren't dynamically wrapped
|
||||
- Filed [issue 29](https://gitlab.com/tslocum/cview/-/issues/29)
|
||||
- [cview.Styles not being used](https://gitlab.com/tslocum/cview/-/issues/47) - issue is circumvented in Amfora
|
||||
- [ANSI conversion is messed up](https://gitlab.com/tslocum/cview/-/issues/48)
|
||||
- [WordWrap is broken in some cases](https://gitlab.com/tslocum/cview/-/issues/27#note_475438483) - close #156 if this is fixed
|
||||
- [Prevent panic when reformatting](https://gitlab.com/tslocum/cview/-/issues/50) - can't reliably reproduce or debug
|
||||
|
@ -178,7 +178,7 @@ You can also check out [all the issues with the bug label](https://github.com/ma
|
||||
## Libraries
|
||||
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 uses [tcell](https://github.com/gdamore/tcell) for low level terminal operations
|
||||
- [Viper](https://github.com/spf13/viper) for configuration and TOFU storing
|
||||
|
@ -52,14 +52,19 @@ func main() {
|
||||
|
||||
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.NewTab()
|
||||
display.NewTab() // Open extra tab and close it to fully initialize the app and wrapping
|
||||
display.CloseTab()
|
||||
if len(os.Args[1:]) > 0 {
|
||||
display.URL(os.Args[1])
|
||||
}
|
||||
|
||||
// Start
|
||||
if err = display.App.Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/makeworld-the-better-one/amfora/cache"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/rkoesters/xdg/basedir"
|
||||
@ -55,6 +55,10 @@ type MediaHandler struct {
|
||||
|
||||
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 {
|
||||
|
||||
// *** Set paths ***
|
||||
@ -204,6 +208,7 @@ func Init() error {
|
||||
viper.SetDefault("a-general.page_max_size", 2097152)
|
||||
viper.SetDefault("a-general.page_max_time", 10)
|
||||
viper.SetDefault("a-general.emoji_favicons", false)
|
||||
viper.SetDefault("a-general.scrollbar", "auto")
|
||||
viper.SetDefault("keybindings.bind_reload", []string{"R", "Ctrl-R"})
|
||||
viper.SetDefault("keybindings.bind_home", "Backspace")
|
||||
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
|
||||
}
|
||||
|
@ -73,6 +73,10 @@ page_max_time = 10
|
||||
# Whether to replace tab numbers with emoji favicons, which are cached.
|
||||
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]
|
||||
# Authentication settings
|
||||
@ -301,6 +305,7 @@ entries_per_page = 20
|
||||
# bottombar_label: The color of the prompt that appears when you press space
|
||||
# bottombar_text: The color of the text you type
|
||||
# bottombar_bg
|
||||
# scrollbar: The scrollbar that appears on the right for long pages
|
||||
|
||||
# hdg_1
|
||||
# hdg_2
|
||||
|
@ -3,7 +3,7 @@ package config
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
||||
// Functions to allow themeing configuration.
|
||||
@ -21,6 +21,7 @@ var theme = map[string]tcell.Color{
|
||||
"bottombar_label": tcell.Color30,
|
||||
"bottombar_text": tcell.ColorBlack,
|
||||
"bottombar_bg": tcell.ColorWhite,
|
||||
"scrollbar": tcell.ColorWhite,
|
||||
|
||||
// Modals
|
||||
"btn_bg": tcell.ColorNavy, // All modal buttons
|
||||
@ -74,7 +75,7 @@ func SetColor(key string, color tcell.Color) {
|
||||
func GetColor(key string) tcell.Color {
|
||||
themeMu.RLock()
|
||||
defer themeMu.RUnlock()
|
||||
return theme[key]
|
||||
return theme[key].TrueColor()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
themeMu.RLock()
|
||||
defer themeMu.RUnlock()
|
||||
return fmt.Sprintf("#%06x", theme[key].Hex())
|
||||
return fmt.Sprintf("#%06x", theme[key].TrueColor().Hex())
|
||||
}
|
||||
|
@ -70,6 +70,10 @@ page_max_time = 10
|
||||
# Whether to replace tab numbers with emoji favicons, which are cached.
|
||||
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]
|
||||
# Authentication settings
|
||||
@ -298,6 +302,7 @@ entries_per_page = 20
|
||||
# bottombar_label: The color of the prompt that appears when you press space
|
||||
# bottombar_text: The color of the text you type
|
||||
# bottombar_bg
|
||||
# scrollbar: The scrollbar that appears on the right for long pages
|
||||
|
||||
# hdg_1
|
||||
# hdg_2
|
||||
|
@ -34,7 +34,7 @@ func aboutInit(version, commit, builtBy string) {
|
||||
}
|
||||
|
||||
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{
|
||||
Raw: content,
|
||||
Content: renderContent,
|
||||
|
@ -2,9 +2,8 @@ package display
|
||||
|
||||
import (
|
||||
"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/config"
|
||||
"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
|
||||
|
||||
func bkmkInit() {
|
||||
panels.AddPanel("bkmk", bkmkModal, false, false)
|
||||
|
||||
m := bkmkModal
|
||||
if viper.GetBool("a-general.color") {
|
||||
bkmkModal.SetBackgroundColor(config.GetColor("bkmk_modal_bg")).
|
||||
SetButtonBackgroundColor(config.GetColor("btn_bg")).
|
||||
SetButtonTextColor(config.GetColor("btn_text")).
|
||||
SetTextColor(config.GetColor("bkmk_modal_text"))
|
||||
bkmkModal.GetForm().
|
||||
SetLabelColor(config.GetColor("bkmk_modal_label")).
|
||||
SetFieldBackgroundColor(config.GetColor("bkmk_modal_field_bg")).
|
||||
SetFieldTextColor(config.GetColor("bkmk_modal_field_text"))
|
||||
bkmkModal.GetFrame().
|
||||
SetBorderColor(config.GetColor("bkmk_modal_text")).
|
||||
SetTitleColor(config.GetColor("bkmk_modal_text"))
|
||||
m.SetBackgroundColor(config.GetColor("bkmk_modal_bg"))
|
||||
m.SetButtonBackgroundColor(config.GetColor("btn_bg"))
|
||||
m.SetButtonTextColor(config.GetColor("btn_text"))
|
||||
m.SetTextColor(config.GetColor("bkmk_modal_text"))
|
||||
form := m.GetForm()
|
||||
form.SetLabelColor(config.GetColor("bkmk_modal_label"))
|
||||
form.SetFieldBackgroundColor(config.GetColor("bkmk_modal_field_bg"))
|
||||
form.SetFieldTextColor(config.GetColor("bkmk_modal_field_text"))
|
||||
form.SetButtonBackgroundColorFocused(config.GetColor("btn_text"))
|
||||
form.SetButtonTextColorFocused(config.GetColor("btn_bg"))
|
||||
frame := m.GetFrame()
|
||||
frame.SetBorderColor(config.GetColor("bkmk_modal_text"))
|
||||
frame.SetTitleColor(config.GetColor("bkmk_modal_text"))
|
||||
} else {
|
||||
bkmkModal.SetBackgroundColor(tcell.ColorBlack).
|
||||
SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
bkmkModal.GetForm().
|
||||
SetLabelColor(tcell.ColorWhite).
|
||||
SetFieldBackgroundColor(tcell.ColorWhite).
|
||||
SetFieldTextColor(tcell.ColorBlack)
|
||||
bkmkModal.GetFrame().
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
m.SetBackgroundColor(tcell.ColorBlack)
|
||||
m.SetButtonBackgroundColor(tcell.ColorWhite)
|
||||
m.SetButtonTextColor(tcell.ColorBlack)
|
||||
m.SetTextColor(tcell.ColorWhite)
|
||||
form := m.GetForm()
|
||||
form.SetLabelColor(tcell.ColorWhite)
|
||||
form.SetFieldBackgroundColor(tcell.ColorWhite)
|
||||
form.SetFieldTextColor(tcell.ColorBlack)
|
||||
form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
|
||||
form.SetButtonTextColorFocused(tcell.ColorWhite)
|
||||
frame := m.GetFrame()
|
||||
frame.SetBorderColor(tcell.ColorWhite)
|
||||
frame.SetTitleColor(tcell.ColorWhite)
|
||||
}
|
||||
|
||||
bkmkModal.SetBorder(true)
|
||||
bkmkModal.GetFrame().
|
||||
SetTitleAlign(cview.AlignCenter).
|
||||
SetTitle(" Add Bookmark ")
|
||||
bkmkModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
m.SetBorder(true)
|
||||
frame := m.GetFrame()
|
||||
frame.SetTitleAlign(cview.AlignCenter)
|
||||
frame.SetTitle(" Add Bookmark ")
|
||||
m.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
switch buttonLabel {
|
||||
case "Add":
|
||||
bkmkCh <- 1
|
||||
@ -97,13 +103,13 @@ func openBkmkModal(name string, exists bool, favicon string) (string, int) {
|
||||
bkmkModalText = text
|
||||
})
|
||||
|
||||
tabPages.ShowPage("bkmk")
|
||||
tabPages.SendToFront("bkmk")
|
||||
panels.ShowPanel("bkmk")
|
||||
panels.SendToFront("bkmk")
|
||||
App.SetFocus(bkmkModal)
|
||||
App.Draw()
|
||||
|
||||
action := <-bkmkCh
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
panels.HidePanel("bkmk")
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
|
||||
@ -120,7 +126,7 @@ func Bookmarks(t *tab) {
|
||||
bkmkPageRaw += fmt.Sprintf("=> %s %s\r\n", keys[i], m[keys[i]])
|
||||
}
|
||||
// Render and display
|
||||
content, links := renderer.RenderGemini(bkmkPageRaw, textWidth(), leftMargin(), false)
|
||||
content, links := renderer.RenderGemini(bkmkPageRaw, textWidth(), false)
|
||||
page := structs.Page{
|
||||
Raw: bkmkPageRaw,
|
||||
Content: content,
|
||||
|
@ -6,8 +6,9 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"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/config"
|
||||
"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,
|
||||
// which are used to draw modals on top the current tab.
|
||||
// Ex: "info", "error", "input", "yesno"
|
||||
var tabPages = cview.NewPages()
|
||||
var panels = cview.NewPanels()
|
||||
|
||||
// The tabs at the top with titles
|
||||
var tabRow = cview.NewTextView().
|
||||
SetDynamicColors(true).
|
||||
SetRegions(true).
|
||||
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
|
||||
})
|
||||
// Tabbed viewer for primitives
|
||||
// Panels are named as strings of tab numbers - so the textview for the first tab
|
||||
// is held in the page named "0".
|
||||
var browser = cview.NewTabbedPanels()
|
||||
|
||||
// Root layout
|
||||
var layout = cview.NewFlex().
|
||||
SetDirection(cview.FlexRow)
|
||||
var layout = cview.NewFlex()
|
||||
|
||||
var newTabPage structs.Page
|
||||
|
||||
var App = cview.NewApplication().
|
||||
EnableMouse(false).
|
||||
SetRoot(layout, true).
|
||||
SetAfterResizeFunc(func(width int, height int) {
|
||||
// Global mutex for changing the size of the left margin on all tabs.
|
||||
var reformatMu = sync.Mutex{}
|
||||
|
||||
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
|
||||
termW = width
|
||||
termH = height
|
||||
|
||||
// Make sure the current tab content is reformatted when the terminal size changes
|
||||
go func(t *tab) {
|
||||
t.reformatMu.Lock() // Only one reformat job per tab
|
||||
defer t.reformatMu.Unlock()
|
||||
// Use the current tab, but don't affect other tabs if the user switches tabs
|
||||
reformatPageAndSetView(t, t.page)
|
||||
reformatMu.Lock() // Only allow one reformat job at a time
|
||||
for i := range tabs {
|
||||
// Overwrite all tabs with a new, differently sized, left margin
|
||||
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])
|
||||
})
|
||||
|
||||
func Init(version, commit, builtBy string) {
|
||||
aboutInit(version, commit, builtBy)
|
||||
|
||||
tabRow.SetChangedFunc(func() {
|
||||
App.Draw()
|
||||
})
|
||||
panels.AddPanel("browser", browser, true, true)
|
||||
|
||||
helpInit()
|
||||
|
||||
layout.
|
||||
AddItem(tabRow, 1, 1, false).
|
||||
AddItem(nil, 1, 1, false). // One line of empty space above the page
|
||||
AddItem(tabPages, 0, 1, true).
|
||||
AddItem(nil, 1, 1, false). // One line of empty space before bottomBar
|
||||
AddItem(bottomBar, 1, 1, false)
|
||||
layout.SetDirection(cview.FlexRow)
|
||||
layout.AddItem(panels, 0, 1, true)
|
||||
layout.AddItem(bottomBar, 1, 1, false)
|
||||
|
||||
if viper.GetBool("a-general.color") {
|
||||
layout.SetBackgroundColor(config.GetColor("bg"))
|
||||
tabRow.SetBackgroundColor(config.GetColor("bg"))
|
||||
|
||||
bottomBar.SetBackgroundColor(config.GetColor("bottombar_bg"))
|
||||
bottomBar.
|
||||
SetLabelColor(config.GetColor("bottombar_label")).
|
||||
SetFieldBackgroundColor(config.GetColor("bottombar_bg")).
|
||||
SetFieldTextColor(config.GetColor("bottombar_text"))
|
||||
bottomBar.SetLabelColor(config.GetColor("bottombar_label"))
|
||||
bottomBar.SetFieldBackgroundColor(config.GetColor("bottombar_bg"))
|
||||
bottomBar.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 {
|
||||
bottomBar.SetBackgroundColor(tcell.ColorWhite)
|
||||
bottomBar.
|
||||
SetLabelColor(tcell.ColorBlack).
|
||||
SetFieldBackgroundColor(tcell.ColorWhite).
|
||||
SetFieldTextColor(tcell.ColorBlack)
|
||||
bottomBar.SetLabelColor(tcell.ColorBlack)
|
||||
bottomBar.SetFieldBackgroundColor(tcell.ColorWhite)
|
||||
bottomBar.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) {
|
||||
tab := curTab
|
||||
|
||||
@ -230,7 +247,7 @@ func Init(version, commit, builtBy string) {
|
||||
// Render the default new tab content ONCE and store it for later
|
||||
// This code is repeated in Reload()
|
||||
newTabContent := getNewTabContent()
|
||||
renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), leftMargin(), false)
|
||||
renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), false)
|
||||
newTabPage = structs.Page{
|
||||
Raw: newTabContent,
|
||||
Content: renderedNewTabContent,
|
||||
@ -426,23 +443,10 @@ func NewTab() {
|
||||
tabs[curTab].addToHistory("about:newtab")
|
||||
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)
|
||||
|
||||
// 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.SetText("")
|
||||
tabs[curTab].saveBottomBar()
|
||||
@ -469,7 +473,7 @@ func CloseTab() {
|
||||
}
|
||||
|
||||
tabs = tabs[:len(tabs)-1]
|
||||
tabPages.RemovePage(strconv.Itoa(curTab))
|
||||
browser.RemoveTab(strconv.Itoa(curTab))
|
||||
|
||||
if curTab <= 0 {
|
||||
curTab = NumTabs() - 1
|
||||
@ -477,8 +481,7 @@ func CloseTab() {
|
||||
curTab--
|
||||
}
|
||||
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab)) // Go to previous page
|
||||
rewriteTabRow()
|
||||
browser.SetCurrentTab(strconv.Itoa(curTab)) // Go to previous page
|
||||
// Restore previous tab's state
|
||||
tabs[curTab].applyAll()
|
||||
|
||||
@ -510,8 +513,7 @@ func SwitchTab(tab int) {
|
||||
|
||||
// Display tab
|
||||
reformatPageAndSetView(tabs[curTab], tabs[curTab].page)
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
tabRow.Highlight(strconv.Itoa(curTab)).ScrollToHighlight()
|
||||
browser.SetCurrentTab(strconv.Itoa(curTab))
|
||||
tabs[curTab].applyAll()
|
||||
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
@ -525,7 +527,7 @@ func Reload() {
|
||||
// Re-render new tab, similar to Init()
|
||||
newTabContent := getNewTabContent()
|
||||
tmpTermW := termW
|
||||
renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), leftMargin(), false)
|
||||
renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), false)
|
||||
newTabPage = structs.Page{
|
||||
Raw: newTabContent,
|
||||
Content: renderedNewTabContent,
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
"strings"
|
||||
"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/structs"
|
||||
"github.com/makeworld-the-better-one/amfora/sysopen"
|
||||
@ -24,9 +24,8 @@ import (
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
|
||||
// For choosing between download and opening - copy of YesNo basically
|
||||
var dlChoiceModal = cview.NewModal().
|
||||
AddButtons([]string{"Open", "Download", "Cancel"})
|
||||
// For choosing between download and the portal - copy of YesNo basically
|
||||
var dlChoiceModal = cview.NewModal()
|
||||
|
||||
// Channel to indicate what choice they made using the button text
|
||||
var dlChoiceCh = make(chan string)
|
||||
@ -34,52 +33,70 @@ var dlChoiceCh = make(chan string)
|
||||
var dlModal = cview.NewModal()
|
||||
|
||||
func dlInit() {
|
||||
panels.AddPanel("dl", dlModal, false, false)
|
||||
panels.AddPanel("dlChoice", dlChoiceModal, false, false)
|
||||
|
||||
dlm := dlModal
|
||||
chm := dlChoiceModal
|
||||
if viper.GetBool("a-general.color") {
|
||||
dlChoiceModal.SetButtonBackgroundColor(config.GetColor("btn_bg")).
|
||||
SetButtonTextColor(config.GetColor("btn_text")).
|
||||
SetBackgroundColor(config.GetColor("dl_choice_modal_bg")).
|
||||
SetTextColor(config.GetColor("dl_choice_modal_text"))
|
||||
dlChoiceModal.GetFrame().
|
||||
SetBorderColor(config.GetColor("dl_choice_modal_text")).
|
||||
SetTitleColor(config.GetColor("dl_choice_modal_text"))
|
||||
chm.SetButtonBackgroundColor(config.GetColor("btn_bg"))
|
||||
chm.SetButtonTextColor(config.GetColor("btn_text"))
|
||||
chm.SetBackgroundColor(config.GetColor("dl_choice_modal_bg"))
|
||||
chm.SetTextColor(config.GetColor("dl_choice_modal_text"))
|
||||
form := chm.GetForm()
|
||||
form.SetButtonBackgroundColorFocused(config.GetColor("btn_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")).
|
||||
SetButtonTextColor(config.GetColor("btn_text")).
|
||||
SetBackgroundColor(config.GetColor("dl_modal_bg")).
|
||||
SetTextColor(config.GetColor("dl_modal_text"))
|
||||
dlModal.GetFrame().
|
||||
SetBorderColor(config.GetColor("dl_modal_text")).
|
||||
SetTitleColor(config.GetColor("dl_modal_text"))
|
||||
dlm.SetButtonBackgroundColor(config.GetColor("btn_bg"))
|
||||
dlm.SetButtonTextColor(config.GetColor("btn_text"))
|
||||
dlm.SetBackgroundColor(config.GetColor("dl_modal_bg"))
|
||||
dlm.SetTextColor(config.GetColor("dl_modal_text"))
|
||||
form = dlm.GetForm()
|
||||
form.SetButtonBackgroundColorFocused(config.GetColor("btn_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 {
|
||||
dlChoiceModal.SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack).
|
||||
SetBackgroundColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
dlChoiceModal.SetBorderColor(tcell.ColorWhite)
|
||||
dlChoiceModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
chm.SetButtonBackgroundColor(tcell.ColorWhite)
|
||||
chm.SetButtonTextColor(tcell.ColorBlack)
|
||||
chm.SetBackgroundColor(tcell.ColorBlack)
|
||||
chm.SetTextColor(tcell.ColorWhite)
|
||||
chm.SetBorderColor(tcell.ColorWhite)
|
||||
chm.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
form := chm.GetForm()
|
||||
form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
|
||||
form.SetButtonTextColorFocused(tcell.ColorWhite)
|
||||
|
||||
dlModal.SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack).
|
||||
SetBackgroundColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
dlModal.GetFrame().
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
dlm.SetButtonBackgroundColor(tcell.ColorWhite)
|
||||
dlm.SetButtonTextColor(tcell.ColorBlack)
|
||||
dlm.SetBackgroundColor(tcell.ColorBlack)
|
||||
dlm.SetTextColor(tcell.ColorWhite)
|
||||
form = dlm.GetForm()
|
||||
form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
|
||||
form.SetButtonTextColorFocused(tcell.ColorWhite)
|
||||
frame := dlm.GetFrame()
|
||||
frame.SetBorderColor(tcell.ColorWhite)
|
||||
frame.SetTitleColor(tcell.ColorWhite)
|
||||
}
|
||||
|
||||
dlChoiceModal.SetBorder(true)
|
||||
dlChoiceModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
dlChoiceModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
chm.AddButtons([]string{"Open", "Download", "Cancel"})
|
||||
chm.SetBorder(true)
|
||||
chm.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
chm.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
dlChoiceCh <- buttonLabel
|
||||
})
|
||||
|
||||
dlModal.SetBorder(true)
|
||||
dlModal.GetFrame().
|
||||
SetTitleAlign(cview.AlignCenter).
|
||||
SetTitle(" Download ")
|
||||
dlModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
dlm.SetBorder(true)
|
||||
frame := dlm.GetFrame()
|
||||
frame.SetTitleAlign(cview.AlignCenter)
|
||||
frame.SetTitle(" Download ")
|
||||
dlm.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
if buttonLabel == "Ok" {
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
panels.HidePanel("dl")
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
}
|
||||
@ -124,27 +141,29 @@ func dlChoice(text, u string, resp *gemini.Response) {
|
||||
choice = "Open"
|
||||
} else {
|
||||
dlChoiceModal.SetText(text)
|
||||
tabPages.ShowPage("dlChoice")
|
||||
tabPages.SendToFront("dlChoice")
|
||||
panels.ShowPanel("dlChoice")
|
||||
panels.SendToFront("dlChoice")
|
||||
App.SetFocus(dlChoiceModal)
|
||||
App.Draw()
|
||||
choice = <-dlChoiceCh
|
||||
}
|
||||
|
||||
if choice == "Download" {
|
||||
tabPages.HidePage("dlChoice")
|
||||
panels.HidePanel("dlChoice")
|
||||
App.Draw()
|
||||
downloadURL(config.DownloadsDir, u, resp)
|
||||
resp.Body.Close() // Only close when the file is downloaded
|
||||
return
|
||||
}
|
||||
if choice == "Open" {
|
||||
tabPages.HidePage("dlChoice")
|
||||
panels.HidePanel("dlChoice")
|
||||
App.Draw()
|
||||
open(u, resp)
|
||||
return
|
||||
}
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
|
||||
// They chose the "Cancel" button
|
||||
panels.HidePanel("dlChoice")
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
}
|
||||
@ -180,9 +199,11 @@ func open(u string, resp *gemini.Response) {
|
||||
if path == "" {
|
||||
return
|
||||
}
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
|
||||
panels.HidePanel("dl")
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
|
||||
if mediaHandler.Cmd == nil {
|
||||
// Open with system default viewer
|
||||
_, err := sysopen.Open(path)
|
||||
@ -246,15 +267,15 @@ func downloadURL(dir, u string, resp *gemini.Response) string {
|
||||
// Display
|
||||
dlModal.ClearButtons()
|
||||
dlModal.AddButtons([]string{"Downloading..."})
|
||||
tabPages.ShowPage("dl")
|
||||
tabPages.SendToFront("dl")
|
||||
panels.ShowPanel("dl")
|
||||
panels.SendToFront("dl")
|
||||
App.SetFocus(dlModal)
|
||||
App.Draw()
|
||||
|
||||
_, err = io.Copy(io.MultiWriter(f, bar), resp.Body)
|
||||
done = true
|
||||
if err != nil {
|
||||
tabPages.HidePage("dl")
|
||||
panels.HidePanel("dl")
|
||||
Error("Download Error", err.Error())
|
||||
f.Close()
|
||||
os.Remove(savePath) // Remove partial file
|
||||
|
@ -59,7 +59,7 @@ func handleFile(u string) (*structs.Page, bool) {
|
||||
}
|
||||
|
||||
if mimetype == "text/gemini" {
|
||||
rendered, links := renderer.RenderGemini(string(content), textWidth(), leftMargin(), false)
|
||||
rendered, links := renderer.RenderGemini(string(content), textWidth(), false)
|
||||
page = &structs.Page{
|
||||
Mediatype: structs.TextGemini,
|
||||
URL: u,
|
||||
@ -73,7 +73,7 @@ func handleFile(u string) (*structs.Page, bool) {
|
||||
Mediatype: structs.TextPlain,
|
||||
URL: u,
|
||||
Raw: string(content),
|
||||
Content: renderer.RenderPlainText(string(content), leftMargin()),
|
||||
Content: renderer.RenderPlainText(string(content)),
|
||||
Links: []string{},
|
||||
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)
|
||||
}
|
||||
|
||||
rendered, links := renderer.RenderGemini(content, textWidth(), leftMargin(), false)
|
||||
rendered, links := renderer.RenderGemini(content, textWidth(), false)
|
||||
page = &structs.Page{
|
||||
Mediatype: structs.TextGemini,
|
||||
URL: u,
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"net/url"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/makeworld-the-better-one/amfora/cache"
|
||||
@ -90,12 +91,12 @@ func handleOther(u string) {
|
||||
}
|
||||
|
||||
// handleFavicon handles getting and displaying a favicon.
|
||||
// `old` is the previous favicon for the tab.
|
||||
func handleFavicon(t *tab, host, old string) {
|
||||
func handleFavicon(t *tab, host string) {
|
||||
defer func() {
|
||||
// Update display if needed
|
||||
if t.page.Favicon != old && isValidTab(t) {
|
||||
rewriteTabRow()
|
||||
if t.page.Favicon != "" && isValidTab(t) {
|
||||
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 != "" {
|
||||
t.page.Favicon = fav
|
||||
rewriteTabRow()
|
||||
return
|
||||
}
|
||||
|
||||
@ -389,7 +389,7 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) {
|
||||
res.Body = rr.NewRestartReader(res.Body)
|
||||
|
||||
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
|
||||
if !isValidTab(t) {
|
||||
return ret("", false)
|
||||
|
131
display/help.go
131
display/help.go
@ -2,72 +2,69 @@ package display
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/makeworld-the-better-one/amfora/config"
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
|
||||
var helpCells = strings.TrimSpace(`
|
||||
?|Bring up this help. You can scroll!
|
||||
Enter|Close this help page
|
||||
Esc|Close this help page or any active modal popups
|
||||
Arrow keys, h/j/k/l|Scroll and move a page.
|
||||
%s|Go up a page in document
|
||||
%s|Go down a page in document
|
||||
g|Go to top of document
|
||||
G|Go to bottom of document
|
||||
Tab|Navigate to the next item in a popup.
|
||||
Shift-Tab|Navigate to the previous item in a popup.
|
||||
%s|Go back in the history
|
||||
%s|Go forward in the history
|
||||
%s|Open bar at the bottom - type a URL, link number, search term.
|
||||
|You can also type two dots (..) to go up a directory in the URL.
|
||||
|Typing new:N will open link number N in a new tab
|
||||
|instead of the current one.
|
||||
%s|Go to links 1-10 respectively.
|
||||
%s|Edit current URL
|
||||
Enter, Tab|On a page this will start link highlighting.
|
||||
|Press Tab and Shift-Tab to pick different links.
|
||||
|Press Enter again to go to one, or Esc to stop.
|
||||
%s|Go to a specific tab. (Default: Shift-NUMBER)
|
||||
%s|Go to the last tab.
|
||||
%s|Previous tab
|
||||
%s|Next tab
|
||||
%s|Go home
|
||||
%s|New tab, or if a link is selected,
|
||||
|this will open the link in a new tab.
|
||||
%s|Close tab. For now, only the right-most tab can be closed.
|
||||
%s|Reload a page, discarding the cached version.
|
||||
|This can also be used if you resize your terminal.
|
||||
%s|View bookmarks
|
||||
%s|Add, change, or remove a bookmark for the current page.
|
||||
%s|Save the current page to your downloads.
|
||||
%s|View subscriptions
|
||||
%s|Add or update a subscription
|
||||
%s|Quit
|
||||
`)
|
||||
var helpCells = strings.TrimSpace(
|
||||
"?\tBring up this help. You can scroll!\n" +
|
||||
"Esc\tLeave the help\n" +
|
||||
"Arrow keys, h/j/k/l\tScroll and move a page.\n" +
|
||||
"%s\tGo up a page in document\n" +
|
||||
"%s\tGo down a page in document\n" +
|
||||
"g\tGo to top of document\n" +
|
||||
"G\tGo to bottom of document\n" +
|
||||
"Tab\tNavigate to the next item in a popup.\n" +
|
||||
"Shift-Tab\tNavigate to the previous item in a popup.\n" +
|
||||
"%s\tGo back in the history\n" +
|
||||
"%s\tGo forward in the history\n" +
|
||||
"%s\tOpen bar at the bottom - type a URL, link number, search term.\n" +
|
||||
"\tYou can also type two dots (..) to go up a directory in the URL.\n" +
|
||||
"\tTyping new:N will open link number N in a new tab\n" +
|
||||
"\tinstead of the current one.\n" +
|
||||
"%s\tGo to links 1-10 respectively.\n" +
|
||||
"%s\tEdit current URL\n" +
|
||||
"Enter, Tab\tOn a page this will start link highlighting.\n" +
|
||||
"\tPress Tab and Shift-Tab to pick different links.\n" +
|
||||
"\tPress Enter again to go to one, or Esc to stop.\n" +
|
||||
"%s\tGo to a specific tab. (Default: Shift-NUMBER)\n" +
|
||||
"%s\tGo to the last tab.\n" +
|
||||
"%s\tPrevious tab\n" +
|
||||
"%s\tNext tab\n" +
|
||||
"%s\tGo home\n" +
|
||||
"%s\tNew tab, or if a link is selected,\n" +
|
||||
"\tthis will open the link in a new tab.\n" +
|
||||
"%s\tClose tab. For now, only the right-most tab can be closed.\n" +
|
||||
"%s\tReload a page, discarding the cached version.\n" +
|
||||
"\tThis can also be used if you resize your terminal.\n" +
|
||||
"%s\tView bookmarks\n" +
|
||||
"%s\tAdd, change, or remove a bookmark for the current page.\n" +
|
||||
"%s\tSave the current page to your downloads.\n" +
|
||||
"%s\tView subscriptions\n" +
|
||||
"%s\tAdd or update a subscription\n" +
|
||||
"%s\tQuit\n")
|
||||
|
||||
var helpTable = cview.NewTable().
|
||||
SetSelectable(false, false).
|
||||
SetBorders(false).
|
||||
SetScrollBarVisibility(cview.ScrollBarNever)
|
||||
var helpTable = cview.NewTextView()
|
||||
|
||||
// Help displays the help and keybindings.
|
||||
func Help() {
|
||||
helpTable.ScrollToBeginning()
|
||||
tabPages.SwitchToPage("help")
|
||||
panels.ShowPanel("help")
|
||||
panels.SendToFront("help")
|
||||
App.SetFocus(helpTable)
|
||||
App.Draw()
|
||||
}
|
||||
|
||||
func helpInit() {
|
||||
// Populate help table
|
||||
helpTable.SetBackgroundColor(config.GetColor("bg"))
|
||||
helpTable.SetPadding(0, 0, 1, 1)
|
||||
helpTable.SetDoneFunc(func(key tcell.Key) {
|
||||
if key == tcell.KeyEsc || key == tcell.KeyEnter {
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
panels.HidePanel("help")
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
}
|
||||
@ -102,34 +99,16 @@ func helpInit() {
|
||||
config.GetKeyBinding(config.CmdQuit),
|
||||
)
|
||||
|
||||
rows := strings.Count(helpCells, "\n") + 1
|
||||
cells := strings.Split(
|
||||
strings.ReplaceAll(helpCells, "\n", "|"),
|
||||
"|")
|
||||
cell := 0
|
||||
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++
|
||||
lines := strings.Split(helpCells, "\n")
|
||||
w := tabwriter.NewWriter(helpTable, 0, 8, 2, ' ', 0)
|
||||
for i, line := range lines {
|
||||
if i > 0 && line[0] != '\t' {
|
||||
fmt.Fprintln(w, "\t")
|
||||
}
|
||||
fmt.Fprintln(w, line)
|
||||
}
|
||||
tabPages.AddPage("help", helpTable, true, false)
|
||||
|
||||
w.Flush()
|
||||
|
||||
panels.AddPanel("help", helpTable, true, false)
|
||||
}
|
||||
|
@ -2,12 +2,11 @@ package display
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
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/spf13/viper"
|
||||
"gitlab.com/tslocum/cview"
|
||||
@ -16,103 +15,133 @@ import (
|
||||
// This file contains code for the popups / modals used in the display.
|
||||
// The bookmark modal is in bookmarks.go
|
||||
|
||||
var infoModal = cview.NewModal().
|
||||
AddButtons([]string{"Ok"})
|
||||
var infoModal = cview.NewModal()
|
||||
|
||||
var errorModal = cview.NewModal().
|
||||
AddButtons([]string{"Ok"})
|
||||
var errorModal = cview.NewModal()
|
||||
|
||||
var inputModal = cview.NewModal()
|
||||
var inputCh = make(chan string)
|
||||
var inputModalText string // The current text of the input field in the modal
|
||||
|
||||
var yesNoModal = cview.NewModal().
|
||||
AddButtons([]string{"Yes", "No"})
|
||||
var yesNoModal = cview.NewModal()
|
||||
|
||||
// Channel to receive yesNo answer on
|
||||
var yesNoCh = make(chan bool)
|
||||
|
||||
func modalInit() {
|
||||
tabPages.AddPage("info", infoModal, false, false).
|
||||
AddPage("error", errorModal, false, false).
|
||||
AddPage("input", inputModal, false, false).
|
||||
AddPage("yesno", yesNoModal, false, false).
|
||||
AddPage("bkmk", bkmkModal, false, false).
|
||||
AddPage("dlChoice", dlChoiceModal, false, false).
|
||||
AddPage("dl", dlModal, false, false)
|
||||
infoModal.AddButtons([]string{"Ok"})
|
||||
|
||||
errorModal.AddButtons([]string{"Ok"})
|
||||
|
||||
yesNoModal.AddButtons([]string{"Yes", "No"})
|
||||
|
||||
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
|
||||
if viper.GetBool("a-general.color") {
|
||||
infoModal.SetBackgroundColor(config.GetColor("info_modal_bg")).
|
||||
SetButtonBackgroundColor(config.GetColor("btn_bg")).
|
||||
SetButtonTextColor(config.GetColor("btn_text")).
|
||||
SetTextColor(config.GetColor("info_modal_text"))
|
||||
infoModal.GetFrame().
|
||||
SetBorderColor(config.GetColor("info_modal_text")).
|
||||
SetTitleColor(config.GetColor("info_modal_text"))
|
||||
m := infoModal
|
||||
m.SetBackgroundColor(config.GetColor("info_modal_bg"))
|
||||
m.SetButtonBackgroundColor(config.GetColor("btn_bg"))
|
||||
m.SetButtonTextColor(config.GetColor("btn_text"))
|
||||
m.SetTextColor(config.GetColor("info_modal_text"))
|
||||
form := m.GetForm()
|
||||
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")).
|
||||
SetButtonBackgroundColor(config.GetColor("btn_bg")).
|
||||
SetButtonTextColor(config.GetColor("btn_text")).
|
||||
SetTextColor(config.GetColor("error_modal_text"))
|
||||
errorModal.GetFrame().
|
||||
SetBorderColor(config.GetColor("error_modal_text")).
|
||||
SetTitleColor(config.GetColor("error_modal_text"))
|
||||
m = errorModal
|
||||
m.SetBackgroundColor(config.GetColor("error_modal_bg"))
|
||||
m.SetButtonBackgroundColor(config.GetColor("btn_bg"))
|
||||
m.SetButtonTextColor(config.GetColor("btn_text"))
|
||||
m.SetTextColor(config.GetColor("error_modal_text"))
|
||||
form = m.GetForm()
|
||||
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")).
|
||||
SetButtonBackgroundColor(config.GetColor("btn_bg")).
|
||||
SetButtonTextColor(config.GetColor("btn_text")).
|
||||
SetTextColor(config.GetColor("input_modal_text"))
|
||||
inputModal.GetFrame().
|
||||
SetBorderColor(config.GetColor("input_modal_text")).
|
||||
SetTitleColor(config.GetColor("input_modal_text"))
|
||||
inputModal.GetForm().
|
||||
SetFieldBackgroundColor(config.GetColor("input_modal_field_bg")).
|
||||
SetFieldTextColor(config.GetColor("input_modal_field_text"))
|
||||
m = inputModal
|
||||
m.SetBackgroundColor(config.GetColor("input_modal_bg"))
|
||||
m.SetButtonBackgroundColor(config.GetColor("btn_bg"))
|
||||
m.SetButtonTextColor(config.GetColor("btn_text"))
|
||||
m.SetTextColor(config.GetColor("input_modal_text"))
|
||||
frame = inputModal.GetFrame()
|
||||
frame.SetBorderColor(config.GetColor("input_modal_text"))
|
||||
frame.SetTitleColor(config.GetColor("input_modal_text"))
|
||||
form = inputModal.GetForm()
|
||||
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")).
|
||||
SetButtonTextColor(config.GetColor("btn_text"))
|
||||
m = yesNoModal
|
||||
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 {
|
||||
infoModal.SetBackgroundColor(tcell.ColorBlack).
|
||||
SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
infoModal.GetFrame().
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
m := infoModal
|
||||
m.SetBackgroundColor(tcell.ColorBlack)
|
||||
m.SetButtonBackgroundColor(tcell.ColorWhite)
|
||||
m.SetButtonTextColor(tcell.ColorBlack)
|
||||
m.SetTextColor(tcell.ColorWhite)
|
||||
form := m.GetForm()
|
||||
form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
|
||||
form.SetButtonTextColorFocused(tcell.ColorWhite)
|
||||
frame := infoModal.GetFrame()
|
||||
frame.SetBorderColor(tcell.ColorWhite)
|
||||
frame.SetTitleColor(tcell.ColorWhite)
|
||||
|
||||
errorModal.SetBackgroundColor(tcell.ColorBlack).
|
||||
SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
errorModal.GetFrame().
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
m = errorModal
|
||||
m.SetBackgroundColor(tcell.ColorBlack)
|
||||
m.SetButtonBackgroundColor(tcell.ColorWhite)
|
||||
m.SetButtonTextColor(tcell.ColorBlack)
|
||||
m.SetTextColor(tcell.ColorWhite)
|
||||
form = m.GetForm()
|
||||
form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
|
||||
form.SetButtonTextColorFocused(tcell.ColorWhite)
|
||||
frame = errorModal.GetFrame()
|
||||
frame.SetBorderColor(tcell.ColorWhite)
|
||||
frame.SetTitleColor(tcell.ColorWhite)
|
||||
|
||||
inputModal.SetBackgroundColor(tcell.ColorBlack).
|
||||
SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
inputModal.GetFrame().
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
inputModal.GetForm().
|
||||
SetFieldBackgroundColor(tcell.ColorWhite).
|
||||
SetFieldTextColor(tcell.ColorBlack)
|
||||
m = inputModal
|
||||
m.SetBackgroundColor(tcell.ColorBlack)
|
||||
m.SetButtonBackgroundColor(tcell.ColorWhite)
|
||||
m.SetButtonTextColor(tcell.ColorBlack)
|
||||
m.SetTextColor(tcell.ColorWhite)
|
||||
frame = inputModal.GetFrame()
|
||||
frame.SetBorderColor(tcell.ColorWhite)
|
||||
frame.SetTitleColor(tcell.ColorWhite)
|
||||
form = inputModal.GetForm()
|
||||
form.SetFieldBackgroundColor(tcell.ColorWhite)
|
||||
form.SetFieldTextColor(tcell.ColorBlack)
|
||||
form.SetButtonBackgroundColorFocused(tcell.ColorBlack)
|
||||
form.SetButtonTextColorFocused(tcell.ColorWhite)
|
||||
|
||||
// YesNo background color is changed in funcs
|
||||
yesNoModal.SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack)
|
||||
m = yesNoModal
|
||||
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
|
||||
|
||||
infoModal.SetBorder(true)
|
||||
infoModal.GetFrame().
|
||||
SetTitleAlign(cview.AlignCenter).
|
||||
SetTitle(" Info ")
|
||||
frame := infoModal.GetFrame()
|
||||
frame.SetTitleAlign(cview.AlignCenter)
|
||||
frame.SetTitle(" Info ")
|
||||
infoModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
panels.HidePanel("info")
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
})
|
||||
@ -120,15 +149,15 @@ func modalInit() {
|
||||
errorModal.SetBorder(true)
|
||||
errorModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
errorModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
panels.HidePanel("error")
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
})
|
||||
|
||||
inputModal.SetBorder(true)
|
||||
inputModal.GetFrame().
|
||||
SetTitleAlign(cview.AlignCenter).
|
||||
SetTitle(" Input ")
|
||||
frame = inputModal.GetFrame()
|
||||
frame.SetTitleAlign(cview.AlignCenter)
|
||||
frame.SetTitle(" Input ")
|
||||
inputModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
if buttonLabel == "Send" {
|
||||
inputCh <- inputModalText
|
||||
@ -167,8 +196,8 @@ func Error(title, text string) {
|
||||
|
||||
errorModal.GetFrame().SetTitle(title)
|
||||
errorModal.SetText(text)
|
||||
tabPages.ShowPage("error")
|
||||
tabPages.SendToFront("error")
|
||||
panels.ShowPanel("error")
|
||||
panels.SendToFront("error")
|
||||
App.SetFocus(errorModal)
|
||||
App.Draw()
|
||||
}
|
||||
@ -176,8 +205,8 @@ func Error(title, text string) {
|
||||
// Info displays some info on the screen in a modal.
|
||||
func Info(s string) {
|
||||
infoModal.SetText(s)
|
||||
tabPages.ShowPage("info")
|
||||
tabPages.SendToFront("info")
|
||||
panels.ShowPanel("info")
|
||||
panels.SendToFront("info")
|
||||
App.SetFocus(infoModal)
|
||||
App.Draw()
|
||||
}
|
||||
@ -198,14 +227,14 @@ func Input(prompt string) (string, bool) {
|
||||
})
|
||||
|
||||
inputModal.SetText(prompt + " ")
|
||||
tabPages.ShowPage("input")
|
||||
tabPages.SendToFront("input")
|
||||
panels.ShowPanel("input")
|
||||
panels.SendToFront("input")
|
||||
App.SetFocus(inputModal)
|
||||
App.Draw()
|
||||
|
||||
resp := <-inputCh
|
||||
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
panels.HidePanel("input")
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
|
||||
@ -218,29 +247,29 @@ func Input(prompt string) (string, bool) {
|
||||
// YesNo displays a modal asking a yes-or-no question.
|
||||
func YesNo(prompt string) bool {
|
||||
if viper.GetBool("a-general.color") {
|
||||
yesNoModal.
|
||||
SetBackgroundColor(config.GetColor("yesno_modal_bg")).
|
||||
SetTextColor(config.GetColor("yesno_modal_text"))
|
||||
yesNoModal.GetFrame().
|
||||
SetBorderColor(config.GetColor("yesno_modal_text")).
|
||||
SetTitleColor(config.GetColor("yesno_modal_text"))
|
||||
m := yesNoModal
|
||||
m.SetBackgroundColor(config.GetColor("yesno_modal_bg"))
|
||||
m.SetTextColor(config.GetColor("yesno_modal_text"))
|
||||
frame := yesNoModal.GetFrame()
|
||||
frame.SetBorderColor(config.GetColor("yesno_modal_text"))
|
||||
frame.SetTitleColor(config.GetColor("yesno_modal_text"))
|
||||
} else {
|
||||
yesNoModal.
|
||||
SetBackgroundColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
yesNoModal.GetFrame().
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
m := yesNoModal
|
||||
m.SetBackgroundColor(tcell.ColorBlack)
|
||||
m.SetTextColor(tcell.ColorWhite)
|
||||
frame := yesNoModal.GetFrame()
|
||||
frame.SetBorderColor(tcell.ColorWhite)
|
||||
frame.SetTitleColor(tcell.ColorWhite)
|
||||
}
|
||||
yesNoModal.GetFrame().SetTitle("")
|
||||
yesNoModal.SetText(prompt)
|
||||
tabPages.ShowPage("yesno")
|
||||
tabPages.SendToFront("yesno")
|
||||
panels.ShowPanel("yesno")
|
||||
panels.SendToFront("yesno")
|
||||
App.SetFocus(yesNoModal)
|
||||
App.Draw()
|
||||
|
||||
resp := <-yesNoCh
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
panels.HidePanel("yesno")
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
return resp
|
||||
@ -251,36 +280,34 @@ func YesNo(prompt string) bool {
|
||||
func Tofu(host string, expiry time.Time) bool {
|
||||
// Reuses yesNoModal, with error color
|
||||
|
||||
m := yesNoModal
|
||||
frame := yesNoModal.GetFrame()
|
||||
if viper.GetBool("a-general.color") {
|
||||
yesNoModal.
|
||||
SetBackgroundColor(config.GetColor("tofu_modal_bg")).
|
||||
SetTextColor(config.GetColor("tofu_modal_text"))
|
||||
yesNoModal.GetFrame().
|
||||
SetBorderColor(config.GetColor("tofu_modal_text")).
|
||||
SetTitleColor(config.GetColor("tofu_modal_text"))
|
||||
m.SetBackgroundColor(config.GetColor("tofu_modal_bg"))
|
||||
m.SetTextColor(config.GetColor("tofu_modal_text"))
|
||||
frame.SetBorderColor(config.GetColor("tofu_modal_text"))
|
||||
frame.SetTitleColor(config.GetColor("tofu_modal_text"))
|
||||
} else {
|
||||
yesNoModal.
|
||||
SetBackgroundColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
yesNoModal.
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
m.SetBackgroundColor(tcell.ColorBlack)
|
||||
m.SetTextColor(tcell.ColorWhite)
|
||||
m.SetBorderColor(tcell.ColorWhite)
|
||||
m.SetTitleColor(tcell.ColorWhite)
|
||||
}
|
||||
yesNoModal.GetFrame().SetTitle(" TOFU ")
|
||||
yesNoModal.SetText(
|
||||
frame.SetTitle(" TOFU ")
|
||||
m.SetText(
|
||||
//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? ",
|
||||
host,
|
||||
humanize.Time(expiry),
|
||||
),
|
||||
)
|
||||
tabPages.ShowPage("yesno")
|
||||
tabPages.SendToFront("yesno")
|
||||
panels.ShowPanel("yesno")
|
||||
panels.SendToFront("yesno")
|
||||
App.SetFocus(yesNoModal)
|
||||
App.Draw()
|
||||
|
||||
resp := <-yesNoCh
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
panels.HidePanel("yesno")
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
return resp
|
||||
|
@ -1,15 +1,12 @@
|
||||
package display
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/makeworld-the-better-one/amfora/config"
|
||||
"github.com/makeworld-the-better-one/amfora/renderer"
|
||||
"github.com/makeworld-the-better-one/amfora/structs"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// 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") {
|
||||
proxied = false
|
||||
}
|
||||
rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), leftMargin(), proxied)
|
||||
rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), proxied)
|
||||
case structs.TextPlain:
|
||||
rendered = renderer.RenderPlainText(p.Raw, leftMargin())
|
||||
rendered = renderer.RenderPlainText(p.Raw)
|
||||
case structs.TextAnsi:
|
||||
rendered = renderer.RenderANSI(p.Raw, leftMargin())
|
||||
rendered = renderer.RenderANSI(p.Raw)
|
||||
default:
|
||||
// Rendering this type is not implemented
|
||||
return
|
||||
@ -92,6 +89,8 @@ func reformatPageAndSetView(t *tab, p *structs.Page) {
|
||||
reformatPage(p)
|
||||
t.view.SetText(p.Content)
|
||||
t.applyScroll() // Go back to where you were, roughly
|
||||
|
||||
App.Draw()
|
||||
}
|
||||
|
||||
// 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
|
||||
reformatPage(p)
|
||||
|
||||
oldFav := t.page.Favicon
|
||||
|
||||
t.page = p
|
||||
|
||||
go func() {
|
||||
parsed, _ := url.Parse(p.URL)
|
||||
handleFavicon(t, parsed.Host, oldFav)
|
||||
}()
|
||||
|
||||
// Change page on screen
|
||||
t.view.SetText(p.Content)
|
||||
t.view.Highlight("") // Turn off highlights, other funcs may restore if necessary
|
||||
t.view.ScrollToBeginning()
|
||||
|
||||
// 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
|
||||
App.SetFocus(t.view)
|
||||
|
||||
@ -144,32 +146,3 @@ func goURL(t *tab, u string) {
|
||||
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()
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
"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/config"
|
||||
"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{
|
||||
Raw: rawPage,
|
||||
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{
|
||||
Raw: rawPage,
|
||||
Content: content,
|
||||
@ -230,19 +230,19 @@ func openSubscriptionModal(validFeed, subscribed bool) bool {
|
||||
// Reuses yesNoModal
|
||||
|
||||
if viper.GetBool("a-general.color") {
|
||||
yesNoModal.
|
||||
SetBackgroundColor(config.GetColor("subscription_modal_bg")).
|
||||
SetTextColor(config.GetColor("subscription_modal_text"))
|
||||
yesNoModal.GetFrame().
|
||||
SetBorderColor(config.GetColor("subscription_modal_text")).
|
||||
SetTitleColor(config.GetColor("subscription_modal_text"))
|
||||
m := yesNoModal
|
||||
m.SetBackgroundColor(config.GetColor("subscription_modal_bg"))
|
||||
m.SetTextColor(config.GetColor("subscription_modal_text"))
|
||||
frame := yesNoModal.GetFrame()
|
||||
frame.SetBorderColor(config.GetColor("subscription_modal_text"))
|
||||
frame.SetTitleColor(config.GetColor("subscription_modal_text"))
|
||||
} else {
|
||||
yesNoModal.
|
||||
SetBackgroundColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
yesNoModal.GetFrame().
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
m := yesNoModal
|
||||
m.SetBackgroundColor(tcell.ColorBlack)
|
||||
m.SetTextColor(tcell.ColorWhite)
|
||||
frame := yesNoModal.GetFrame()
|
||||
frame.SetBorderColor(tcell.ColorWhite)
|
||||
frame.SetTitleColor(tcell.ColorWhite)
|
||||
}
|
||||
if validFeed {
|
||||
yesNoModal.GetFrame().SetTitle("Feed Subscription")
|
||||
@ -260,13 +260,13 @@ func openSubscriptionModal(validFeed, subscribed bool) bool {
|
||||
}
|
||||
}
|
||||
|
||||
tabPages.ShowPage("yesno")
|
||||
tabPages.SendToFront("yesno")
|
||||
panels.ShowPanel("yesno")
|
||||
panels.SendToFront("yesno")
|
||||
App.SetFocus(yesNoModal)
|
||||
App.Draw()
|
||||
|
||||
resp := <-yesNoCh
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
panels.HidePanel("yesno")
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
return resp
|
||||
|
@ -3,9 +3,9 @@ package display
|
||||
import (
|
||||
"strconv"
|
||||
"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"
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
@ -24,33 +24,34 @@ type tabHistory struct {
|
||||
|
||||
// tab hold the information needed for each browser tab.
|
||||
type tab struct {
|
||||
page *structs.Page
|
||||
view *cview.TextView
|
||||
history *tabHistory
|
||||
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
|
||||
barText string // The bottomBar text for the tab
|
||||
page *structs.Page
|
||||
view *cview.TextView
|
||||
history *tabHistory
|
||||
mode tabMode
|
||||
barLabel string // The bottomBar label for the tab
|
||||
barText string // The bottomBar text for the tab
|
||||
}
|
||||
|
||||
// makeNewTab initializes an tab struct with no content.
|
||||
func makeNewTab() *tab {
|
||||
t := tab{
|
||||
page: &structs.Page{Mode: structs.ModeOff},
|
||||
view: cview.NewTextView().
|
||||
SetDynamicColors(true).
|
||||
SetRegions(true).
|
||||
SetScrollable(true).
|
||||
SetWrap(false).
|
||||
SetChangedFunc(func() {
|
||||
App.Draw()
|
||||
}),
|
||||
history: &tabHistory{},
|
||||
reformatMu: &sync.Mutex{},
|
||||
mode: tabModeDone,
|
||||
page: &structs.Page{Mode: structs.ModeOff},
|
||||
view: cview.NewTextView(),
|
||||
history: &tabHistory{},
|
||||
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) {
|
||||
// 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
|
||||
|
||||
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
|
||||
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
|
||||
bottomBar.SetLabel("[::b]Link: [::-]")
|
||||
bottomBar.SetText(tabs[tab].page.Links[0])
|
||||
@ -109,7 +111,8 @@ func makeNewTab() *tab {
|
||||
} else {
|
||||
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
|
||||
bottomBar.SetLabel("[::b]Link: [::-]")
|
||||
bottomBar.SetText(tabs[tab].page.Links[index])
|
||||
|
@ -13,6 +13,43 @@ import (
|
||||
|
||||
// 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.
|
||||
func escapeMeta(meta string) string {
|
||||
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.
|
||||
func isValidTab(t *tab) bool {
|
||||
tempTabs := tabs
|
||||
for i := range tempTabs {
|
||||
if tempTabs[i] == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return tabNumber(t) != -1
|
||||
}
|
||||
|
||||
func leftMargin() int {
|
||||
|
10
go.mod
10
go.mod
@ -5,7 +5,7 @@ go 1.14
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
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/makeworld-the-better-one/go-gemini v0.11.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/jwalterweatherman v1.1.0 // 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
|
||||
gitlab.com/tslocum/cview v1.4.8-0.20200713214710-cc7796c4ca44
|
||||
golang.org/x/text v0.3.5-0.20201208001344-75a595aef632
|
||||
gitlab.com/tslocum/cview v1.5.4-0.20210207045010-d776e728ef6d
|
||||
golang.org/x/text v0.3.5
|
||||
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
|
||||
|
45
go.sum
45
go.sum
@ -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/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
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/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
||||
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/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
|
||||
github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606 h1:Y00kKKKYVyn7InlCMRcnZbwcjHFIsgkjU0Bn1F5re4o=
|
||||
github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=
|
||||
github.com/gdamore/tcell/v2 v2.0.0-dev/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
|
||||
github.com/gdamore/tcell/v2 v2.1.1-0.20210125004847-19e17097d8fe h1:D4zQq/0ep0XCtgkmA+dUvQNYMoiW2+2336rdlAucr10=
|
||||
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/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=
|
||||
@ -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/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
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/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/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
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-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.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.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/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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
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-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
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.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/go.mod h1:T1HolqzmdHnJIH6p7A9LDuvYGQgEHx9ijX3vKgDKU60=
|
||||
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.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
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.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
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/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
gitlab.com/tslocum/cbind v0.1.1 h1:JXXtxMWHgWLvoF+QkrvcNvOQ59juy7OE1RhT7hZfdt0=
|
||||
gitlab.com/tslocum/cbind v0.1.1/go.mod h1:rX7vkl0pUSg/yy427MmD1FZAf99S7WwpUlxF/qTpPqk=
|
||||
gitlab.com/tslocum/cview v1.4.8-0.20200713214710-cc7796c4ca44 h1:YddMqXJ6jI3SkP8Nfxc+S2pcvI5o8mmXmHL2D9hkwQI=
|
||||
gitlab.com/tslocum/cview v1.4.8-0.20200713214710-cc7796c4ca44/go.mod h1:QctoEJaR2AqZTy0KKo12P1ZjHgQJyVkAXaeDanBBhlE=
|
||||
gitlab.com/tslocum/cbind v0.1.4 h1:cbZXPPcieXspk8cShoT6efz7HAT8yMNQcofYWNizis4=
|
||||
gitlab.com/tslocum/cbind v0.1.4/go.mod h1:RvwYE3auSjBNlCmWeGspzn+jdLUVQ8C2QGC+0nP9ChI=
|
||||
gitlab.com/tslocum/cview v1.5.4-0.20210207045010-d776e728ef6d h1:ck3gAnCoraAI2doDfH2MZsz+DxVpvNwnaXa453jH5aI=
|
||||
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.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
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-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-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201113135734-0a15ea8d9b02/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
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-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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
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-0.20201208001344-75a595aef632/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -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/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.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
|
||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
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/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=
|
||||
|
@ -69,7 +69,7 @@ func CanDisplay(res *gemini.Response) bool {
|
||||
|
||||
// MakePage creates a formatted, rendered Page from the given network response and params.
|
||||
// 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) {
|
||||
return nil, ErrCantDisplay
|
||||
}
|
||||
@ -112,7 +112,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b
|
||||
}
|
||||
|
||||
if mediatype == "text/gemini" {
|
||||
rendered, links := RenderGemini(utfText, width, leftMargin, proxied)
|
||||
rendered, links := RenderGemini(utfText, width, proxied)
|
||||
return &structs.Page{
|
||||
Mediatype: structs.TextGemini,
|
||||
RawMediatype: mediatype,
|
||||
@ -130,7 +130,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b
|
||||
RawMediatype: mediatype,
|
||||
URL: url,
|
||||
Raw: utfText,
|
||||
Content: RenderANSI(utfText, leftMargin),
|
||||
Content: RenderANSI(utfText),
|
||||
Links: []string{},
|
||||
MadeAt: time.Now(),
|
||||
}, nil
|
||||
@ -142,7 +142,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b
|
||||
RawMediatype: mediatype,
|
||||
URL: url,
|
||||
Raw: utfText,
|
||||
Content: RenderPlainText(utfText, leftMargin),
|
||||
Content: RenderPlainText(utfText),
|
||||
Links: []string{},
|
||||
MadeAt: time.Now(),
|
||||
}, nil
|
||||
|
@ -21,31 +21,27 @@ var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;]*m`)
|
||||
|
||||
// RenderANSI renders plain text pages containing ANSI codes.
|
||||
// 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)
|
||||
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
|
||||
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 {
|
||||
s = ansiRegex.ReplaceAllString(s, "")
|
||||
}
|
||||
var shifted string
|
||||
lines := strings.Split(s, "\n")
|
||||
for i := range lines {
|
||||
shifted += strings.Repeat(" ", leftMargin) + lines[i] + "\n"
|
||||
}
|
||||
return shifted
|
||||
return s
|
||||
}
|
||||
|
||||
// RenderPlainText should be used to format plain text pages.
|
||||
func RenderPlainText(s string, leftMargin int) string {
|
||||
var shifted string
|
||||
lines := strings.Split(cview.Escape(s), "\n")
|
||||
for i := range lines {
|
||||
shifted += strings.Repeat(" ", leftMargin) +
|
||||
"[" + config.GetColorString("regular_text") + "]" + lines[i] + "[-]" +
|
||||
"\n"
|
||||
}
|
||||
return shifted
|
||||
func RenderPlainText(s string) string {
|
||||
// It used to add a left margin, now this is done elsewhere.
|
||||
// The function is kept for convenience and in case rendering
|
||||
// is needed in the future.
|
||||
return s
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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)
|
||||
|
||||
lines := strings.Split(s, "\n")
|
||||
@ -289,30 +285,52 @@ func RenderGemini(s string, width, leftMargin int, proxied bool) (string, []stri
|
||||
rendered := "" // Final result
|
||||
pre := false
|
||||
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 {
|
||||
if strings.HasPrefix(lines[i], "```") {
|
||||
if pre {
|
||||
// In a preformatted block, so add the text as is
|
||||
// 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 {
|
||||
// Not preformatted, regular text
|
||||
|
||||
// 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
|
||||
processRegular()
|
||||
}
|
||||
buf = "" // Clear buffer for next block
|
||||
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
|
||||
if pre {
|
||||
// File ended without closing the preformatted block
|
||||
// Same code as in the loop above
|
||||
|
||||
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 + "[-]"
|
||||
processPre()
|
||||
} else {
|
||||
// Not preformatted, regular text
|
||||
// Same code as in the loop above
|
||||
|
||||
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
|
||||
processRegular()
|
||||
}
|
||||
|
||||
return rendered, links
|
||||
|
@ -374,9 +374,9 @@ func updateAll() {
|
||||
defer wg.Done()
|
||||
for j := range jobs {
|
||||
if j[0] == "feed" {
|
||||
updateFeed(j[1]) //nolint:errcheck
|
||||
updateFeed(j[1])
|
||||
} else if j[0] == "page" {
|
||||
updatePage(j[1]) //nolint:errcheck
|
||||
updatePage(j[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user