mirror of
https://github.com/makew0rld/amfora.git
synced 2024-11-23 22:23:48 +03:00
✨ Themeing support
This commit is contained in:
parent
fdd8a6e59b
commit
56a56896c9
@ -6,18 +6,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- **Themeing** - check out [default-config.toml](./default-config.toml) for details (#46)
|
||||
- <kbd>Tab</kbd> now also enters link selecting mode, like <kbd>Enter</kbd> (#48)
|
||||
- Number keys can be pressed to navigate to links 1 through 10 (#47)
|
||||
- Permanent redirects are cached for the session (#22)
|
||||
- `.ansi` is also supported for `text/x-ansi` files, as well as the already supported `.ans`
|
||||
|
||||
### Changed
|
||||
- Documented <kbd>Ctrl-C</kbd> has "Hard quit"
|
||||
- Updated [cview](https://gitlab.com/tslocum/cview/) to latest commit: `cc7796c4ca44e3908f80d93e92e73694562d936a`
|
||||
- The bottom bar label now uses the same color as the tabs at the top
|
||||
- Tab and blue link colors were changed very slightly to be part of the 256 Xterm colors, for better terminal support
|
||||
|
||||
### Fixed
|
||||
- You can't change link selection while the page is loading
|
||||
- Only one request is made for each URL - `v1.3.0` accidentally made two requests each time (#50)
|
||||
- Using the `..` command doesn't keep the query string (#49)
|
||||
- Any error that occurs when downloading a file will be displayed, and the partially download file will be deleted
|
||||
- Any error that occurs when downloading a file will be displayed, and the partially downloaded file will be deleted
|
||||
|
||||
|
||||
## [1.3.0] - 2020-07-10
|
||||
@ -34,7 +39,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Changed
|
||||
- Pages are rewrapped dynamically, whenever the terminal size changes (#33)
|
||||
- TOFU warning message mentions how long the previous cert was still valid for (#34)
|
||||
- Update [cview](https://gitlab.com/tslocum/cview/) to latest commit
|
||||
|
||||
### Fixed
|
||||
- Many potential network and display race conditions eliminated
|
||||
|
3
NOTES.md
3
NOTES.md
@ -3,6 +3,9 @@
|
||||
## Issues
|
||||
- URL for each tab should not be stored as a string - in the current code there's lots of reparsing the URL
|
||||
- Can't go back or do other things while page is loading - need a way to stop `handleURL`
|
||||
- Allow for opening a new tab while the current one is loading
|
||||
- Can't leave the help window
|
||||
- Can't interact with page after clicking Cancel on bkmk (and other?) modal(s)
|
||||
|
||||
## Upstream Bugs
|
||||
- Wrapping messes up on brackets
|
||||
|
@ -73,6 +73,7 @@ Features in *italics* are in the master branch, but not in the latest release.
|
||||
- [x] Built-in search (uses GUS by default)
|
||||
- [x] Bookmarks
|
||||
- [x] Download pages and arbitrary data
|
||||
- [x] *Themeing*
|
||||
- [ ] Search in pages with <kbd>Ctrl-F</kbd>
|
||||
- [ ] Emoji favicons
|
||||
- See `gemini://mozz.us/files/rfc_gemini_favicon.gmi` for details
|
||||
|
18
cache/redir.go
vendored
18
cache/redir.go
vendored
@ -7,12 +7,12 @@ import (
|
||||
// Functions for caching redirects.
|
||||
|
||||
var redirUrls = make(map[string]string) // map original URL to redirect
|
||||
var redirMut = sync.RWMutex{}
|
||||
var redirMu = sync.RWMutex{}
|
||||
|
||||
// AddRedir adds a original-to-redirect pair to the cache.
|
||||
func AddRedir(og, redir string) {
|
||||
redirMut.Lock()
|
||||
defer redirMut.Unlock()
|
||||
redirMu.Lock()
|
||||
defer redirMu.Unlock()
|
||||
|
||||
for k, v := range redirUrls {
|
||||
if og == v {
|
||||
@ -32,8 +32,8 @@ func AddRedir(og, redir string) {
|
||||
|
||||
// ClearRedirs removes all redirects from the cache.
|
||||
func ClearRedirs() {
|
||||
redirMut.Lock()
|
||||
defer redirMut.Unlock()
|
||||
redirMu.Lock()
|
||||
defer redirMu.Unlock()
|
||||
redirUrls = make(map[string]string)
|
||||
}
|
||||
|
||||
@ -41,8 +41,8 @@ func ClearRedirs() {
|
||||
// exists for that URL in the cache.
|
||||
// If one does not then the original URL is returned.
|
||||
func Redirect(u string) string {
|
||||
redirMut.RLock()
|
||||
defer redirMut.RUnlock()
|
||||
redirMu.RLock()
|
||||
defer redirMu.RUnlock()
|
||||
|
||||
// A single lookup is enough, because AddRedir
|
||||
// removes loops and chains.
|
||||
@ -54,7 +54,7 @@ func Redirect(u string) string {
|
||||
}
|
||||
|
||||
func NumRedirs() int {
|
||||
redirMut.RLock()
|
||||
defer redirMut.RUnlock()
|
||||
redirMu.RLock()
|
||||
defer redirMu.RUnlock()
|
||||
return len(redirUrls)
|
||||
}
|
||||
|
@ -7,9 +7,11 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/makeworld-the-better-one/amfora/cache"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
|
||||
var amforaAppData string // Where amfora files are stored on Windows - cached here
|
||||
@ -196,5 +198,24 @@ func Init() error {
|
||||
cache.SetMaxSize(viper.GetInt("cache.max_size"))
|
||||
cache.SetMaxPages(viper.GetInt("cache.max_pages"))
|
||||
|
||||
// Theme
|
||||
configTheme := viper.Sub("theme")
|
||||
if configTheme != nil {
|
||||
for k, v := range configTheme.AllSettings() {
|
||||
colorStr, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf(`value for "%s" is not a string: %v`, k, v)
|
||||
}
|
||||
color := tcell.GetColor(strings.ToLower(colorStr))
|
||||
if color == tcell.ColorDefault {
|
||||
return fmt.Errorf(`invalid color format for "%s": %s`, k, colorStr)
|
||||
}
|
||||
SetColor(k, color)
|
||||
}
|
||||
}
|
||||
if viper.GetBool("a-general.color") {
|
||||
cview.Styles.PrimitiveBackgroundColor = GetColor("bg")
|
||||
} // Otherwise it's black by default
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package config
|
||||
|
||||
//go:generate ./default.sh
|
||||
var defaultConf = []byte(`# This is the default config file.
|
||||
# It also shows all the default values, if you don't create the file.
|
||||
|
||||
@ -39,4 +40,73 @@ page_max_time = 10
|
||||
# Zero values mean there is no limit
|
||||
max_size = 0 # Size in bytes
|
||||
max_pages = 30 # The maximum number of pages the cache will store
|
||||
`)
|
||||
|
||||
[theme]
|
||||
# This section is for changing the COLORS used in Amfora.
|
||||
# These colors only apply if color is enabled above.
|
||||
# Colors can be set using a W3C color name, or a hex value such as #ffffff".
|
||||
|
||||
# Note that not all colors will work on terminals that do not have truecolor support.
|
||||
# If you want to stick to the standard 16 or 256 colors, you can get
|
||||
# a list of those here: https://jonasjacek.github.io/colors/
|
||||
# Do NOT use the names from that site, just the hex codes.
|
||||
|
||||
# Definitions:
|
||||
# bg = background
|
||||
# fg = foreground
|
||||
# dl = download
|
||||
# btn = button
|
||||
# hdg = heading
|
||||
# bkmk = bookmark
|
||||
# modal = a popup window/box in the middle of the screen
|
||||
|
||||
# EXAMPLES:
|
||||
# hdg_1 = "green"
|
||||
# hdg_2 = "#5f0000"
|
||||
|
||||
# Available keys to set:
|
||||
|
||||
# bg: background for pages, tab row, app in general
|
||||
# tab_num: The number/highlight of the tabs at the top
|
||||
# tab_divider: The color of the divider character between tab numbers: |
|
||||
# bottombar_label: The color of the prompt that appears when you press space
|
||||
# bottombar_text: The color of the text you type
|
||||
# bottombar_bg
|
||||
|
||||
# hdg_1
|
||||
# hdg_2
|
||||
# hdg_3
|
||||
# amfora_link: A link that Amfora supports viewing. For now this is only gemini://
|
||||
# foreign_link: HTTP(S), Gopher, etc
|
||||
# link_number: The silver number that appears to the left of a link
|
||||
# regular_text: Normal gemini text, and plaintext documents
|
||||
# quote_text
|
||||
# preformatted_text
|
||||
# list_text
|
||||
|
||||
# btn_bg: The bg color for all modal buttons
|
||||
# btn_text: The text color for all modal buttons
|
||||
|
||||
# dl_choice_modal_bg
|
||||
# dl_choice_modal_text
|
||||
# dl_modal_bg
|
||||
# dl_modal_text
|
||||
# info_modal_bg
|
||||
# info_modal_text
|
||||
# error_modal_bg
|
||||
# error_modal_text
|
||||
# yesno_modal_bg
|
||||
# yesno_modal_text
|
||||
# tofu_modal_bg
|
||||
# tofu_modal_text
|
||||
|
||||
# input_modal_bg
|
||||
# input_modal_text
|
||||
# input_modal_field_bg: The bg of the input field, where you type the text
|
||||
# input_modal_field_text: The color of the text you type
|
||||
|
||||
# bkmk_modal_bg
|
||||
# bkmk_modal_text
|
||||
# bkmk_modal_label
|
||||
# bkmk_modal_field_bg
|
||||
# bkmk_modal_field_text`)
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
head -n 1 default.go | tee default.go > /dev/null
|
||||
head -n 3 default.go | tee default.go > /dev/null
|
||||
echo -n 'var defaultConf = []byte(`' >> default.go
|
||||
cat ../default-config.toml >> default.go
|
||||
echo '`)' >> default.go
|
85
config/theme.go
Normal file
85
config/theme.go
Normal file
@ -0,0 +1,85 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// Functions to allow themeing configuration.
|
||||
// UI element colors are mapped to a string key, such as "error" or "tab_background"
|
||||
// These are the same keys used in the config file.
|
||||
|
||||
var themeMu = sync.RWMutex{}
|
||||
var theme = map[string]tcell.Color{
|
||||
// Default values below
|
||||
|
||||
"bg": tcell.ColorBlack, // Used for cview.Styles.PrimitiveBackgroundColor
|
||||
"tab_num": tcell.Color30, // xterm:Turquoise4, #008787
|
||||
"tab_divider": tcell.ColorWhite,
|
||||
"bottombar_label": tcell.Color30,
|
||||
"bottombar_text": tcell.ColorBlack,
|
||||
"bottombar_bg": tcell.ColorWhite,
|
||||
|
||||
// Modals
|
||||
"btn_bg": tcell.ColorNavy, // All modal buttons
|
||||
"btn_text": tcell.ColorWhite,
|
||||
|
||||
"dl_choice_modal_bg": tcell.ColorPurple,
|
||||
"dl_choice_modal_text": tcell.ColorWhite,
|
||||
"dl_modal_bg": tcell.Color130, // xterm:DarkOrange3, #af5f00
|
||||
"dl_modal_text": tcell.ColorWhite,
|
||||
"info_modal_bg": tcell.ColorGray,
|
||||
"info_modal_text": tcell.ColorWhite,
|
||||
"error_modal_bg": tcell.ColorMaroon,
|
||||
"error_modal_text": tcell.ColorWhite,
|
||||
"yesno_modal_bg": tcell.ColorPurple,
|
||||
"yesno_modal_text": tcell.ColorWhite,
|
||||
"tofu_modal_bg": tcell.ColorMaroon,
|
||||
"tofu_modal_text": tcell.ColorWhite,
|
||||
|
||||
"input_modal_bg": tcell.ColorGreen,
|
||||
"input_modal_text": tcell.ColorWhite,
|
||||
"input_modal_field_bg": tcell.ColorBlue,
|
||||
"input_modal_field_text": tcell.ColorWhite,
|
||||
|
||||
"bkmk_modal_bg": tcell.ColorTeal,
|
||||
"bkmk_modal_text": tcell.ColorWhite,
|
||||
"bkmk_modal_label": tcell.ColorYellow,
|
||||
"bkmk_modal_field_bg": tcell.ColorBlue,
|
||||
"bkmk_modal_field_text": tcell.ColorWhite,
|
||||
|
||||
"hdg_1": tcell.ColorRed,
|
||||
"hdg_2": tcell.ColorLime,
|
||||
"hdg_3": tcell.ColorFuchsia,
|
||||
"amfora_link": tcell.Color33, // xterm:DodgerBlue1, #0087ff
|
||||
"foreign_link": tcell.Color92, // xterm:DarkViolet, #8700d7
|
||||
"link_number": tcell.ColorSilver,
|
||||
"regular_text": tcell.ColorWhite,
|
||||
"quote_text": tcell.ColorWhite,
|
||||
"preformatted_text": tcell.ColorWhite,
|
||||
"list_text": tcell.ColorWhite,
|
||||
}
|
||||
|
||||
func SetColor(key string, color tcell.Color) {
|
||||
themeMu.Lock()
|
||||
defer themeMu.Unlock()
|
||||
theme[key] = color
|
||||
}
|
||||
|
||||
// GetColor will return tcell.ColorBlack if there is no color for the provided key.
|
||||
func GetColor(key string) tcell.Color {
|
||||
themeMu.RLock()
|
||||
defer themeMu.RUnlock()
|
||||
return theme[key]
|
||||
}
|
||||
|
||||
// GetColorString returns a string that can be used in a cview color tag,
|
||||
// for the given theme key.
|
||||
// It will return "#000000" if there is no color for the provided key.
|
||||
func GetColorString(key string) string {
|
||||
themeMu.RLock()
|
||||
defer themeMu.RUnlock()
|
||||
return fmt.Sprintf("#%06x", theme[key].Hex())
|
||||
}
|
@ -37,3 +37,73 @@ page_max_time = 10
|
||||
# Zero values mean there is no limit
|
||||
max_size = 0 # Size in bytes
|
||||
max_pages = 30 # The maximum number of pages the cache will store
|
||||
|
||||
[theme]
|
||||
# This section is for changing the COLORS used in Amfora.
|
||||
# These colors only apply if color is enabled above.
|
||||
# Colors can be set using a W3C color name, or a hex value such as #ffffff".
|
||||
|
||||
# Note that not all colors will work on terminals that do not have truecolor support.
|
||||
# If you want to stick to the standard 16 or 256 colors, you can get
|
||||
# a list of those here: https://jonasjacek.github.io/colors/
|
||||
# Do NOT use the names from that site, just the hex codes.
|
||||
|
||||
# Definitions:
|
||||
# bg = background
|
||||
# fg = foreground
|
||||
# dl = download
|
||||
# btn = button
|
||||
# hdg = heading
|
||||
# bkmk = bookmark
|
||||
# modal = a popup window/box in the middle of the screen
|
||||
|
||||
# EXAMPLES:
|
||||
# hdg_1 = "green"
|
||||
# hdg_2 = "#5f0000"
|
||||
|
||||
# Available keys to set:
|
||||
|
||||
# bg: background for pages, tab row, app in general
|
||||
# tab_num: The number/highlight of the tabs at the top
|
||||
# tab_divider: The color of the divider character between tab numbers: |
|
||||
# bottombar_label: The color of the prompt that appears when you press space
|
||||
# bottombar_text: The color of the text you type
|
||||
# bottombar_bg
|
||||
|
||||
# hdg_1
|
||||
# hdg_2
|
||||
# hdg_3
|
||||
# amfora_link: A link that Amfora supports viewing. For now this is only gemini://
|
||||
# foreign_link: HTTP(S), Gopher, etc
|
||||
# link_number: The silver number that appears to the left of a link
|
||||
# regular_text: Normal gemini text, and plaintext documents
|
||||
# quote_text
|
||||
# preformatted_text
|
||||
# list_text
|
||||
|
||||
# btn_bg: The bg color for all modal buttons
|
||||
# btn_text: The text color for all modal buttons
|
||||
|
||||
# dl_choice_modal_bg
|
||||
# dl_choice_modal_text
|
||||
# dl_modal_bg
|
||||
# dl_modal_text
|
||||
# info_modal_bg
|
||||
# info_modal_text
|
||||
# error_modal_bg
|
||||
# error_modal_text
|
||||
# yesno_modal_bg
|
||||
# yesno_modal_text
|
||||
# tofu_modal_bg
|
||||
# tofu_modal_text
|
||||
|
||||
# input_modal_bg
|
||||
# input_modal_text
|
||||
# input_modal_field_bg: The bg of the input field, where you type the text
|
||||
# input_modal_field_text: The color of the text you type
|
||||
|
||||
# bkmk_modal_bg
|
||||
# bkmk_modal_text
|
||||
# bkmk_modal_label
|
||||
# bkmk_modal_field_bg
|
||||
# bkmk_modal_field_text
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/makeworld-the-better-one/amfora/bookmarks"
|
||||
"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"
|
||||
@ -14,8 +15,7 @@ import (
|
||||
)
|
||||
|
||||
// For adding and removing bookmarks, basically a clone of the input modal.
|
||||
var bkmkModal = cview.NewModal().
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
var bkmkModal = cview.NewModal()
|
||||
|
||||
// bkmkCh is for the user action
|
||||
var bkmkCh = make(chan int) // 1, 0, -1 for add/update, cancel, and remove
|
||||
@ -23,21 +23,35 @@ var bkmkModalText string // The current text of the input field in the modal
|
||||
|
||||
func bkmkInit() {
|
||||
if viper.GetBool("a-general.color") {
|
||||
bkmkModal.SetBackgroundColor(tcell.ColorTeal).
|
||||
SetButtonBackgroundColor(tcell.ColorNavy).
|
||||
SetButtonTextColor(tcell.ColorWhite)
|
||||
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"))
|
||||
} else {
|
||||
bkmkModal.SetBackgroundColor(tcell.ColorBlack).
|
||||
SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack)
|
||||
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)
|
||||
}
|
||||
|
||||
bkmkModal.SetBorder(true)
|
||||
bkmkModal.SetBorderColor(tcell.ColorWhite)
|
||||
bkmkModal.GetFrame().
|
||||
SetTitleAlign(cview.AlignCenter).
|
||||
SetTitle(" Add Bookmark ")
|
||||
bkmkModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
switch buttonLabel {
|
||||
case "Add":
|
||||
@ -52,9 +66,6 @@ func bkmkInit() {
|
||||
|
||||
//tabPages.SwitchToPage(strconv.Itoa(curTab)) - handled in bkmk()
|
||||
})
|
||||
bkmkModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
bkmkModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
bkmkModal.GetFrame().SetTitle(" Add Bookmark ")
|
||||
}
|
||||
|
||||
// Bkmk displays the "Add a bookmark" modal.
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/makeworld-the-better-one/amfora/cache"
|
||||
"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"
|
||||
@ -23,9 +24,7 @@ var termW int
|
||||
var termH int
|
||||
|
||||
// The user input and URL display bar at the bottom
|
||||
var bottomBar = cview.NewInputField().
|
||||
SetFieldBackgroundColor(tcell.ColorWhite).
|
||||
SetFieldTextColor(tcell.ColorBlack)
|
||||
var bottomBar = cview.NewInputField()
|
||||
|
||||
// Viewer for the tab primitives
|
||||
// Pages are named as strings of tab numbers - so the textview for the first tab
|
||||
@ -50,18 +49,7 @@ var tabRow = cview.NewTextView().
|
||||
|
||||
// Root layout
|
||||
var layout = cview.NewFlex().
|
||||
SetDirection(cview.FlexRow).
|
||||
AddItem(tabRow, 1, 1, false).
|
||||
AddItem(nil, 1, 1, false). // One line of empty space above the page
|
||||
AddItem(tabPages, 0, 1, true).
|
||||
// AddItem(cview.NewFlex(). // The page text in the middle is held in another flex, to center it
|
||||
// SetDirection(cview.FlexColumn).
|
||||
// AddItem(nil, 0, 1, false).
|
||||
// AddItem(tabPages, 0, 7, true). // The text occupies 7/9 of the screen horizontally
|
||||
// AddItem(nil, 0, 1, false),
|
||||
// 0, 1, true).
|
||||
AddItem(nil, 1, 1, false). // One line of empty space before bottomBar
|
||||
AddItem(bottomBar, 1, 1, false)
|
||||
SetDirection(cview.FlexRow)
|
||||
|
||||
var renderedNewTabContent string
|
||||
var newTabLinks []string
|
||||
@ -77,8 +65,8 @@ var App = cview.NewApplication().
|
||||
|
||||
// Make sure the current tab content is reformatted when the terminal size changes
|
||||
go func(t *tab) {
|
||||
t.reformatMut.Lock() // Only one reformat job per tab
|
||||
defer t.reformatMut.Unlock()
|
||||
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)
|
||||
}(tabs[curTab])
|
||||
@ -91,12 +79,29 @@ func Init() {
|
||||
|
||||
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)
|
||||
|
||||
if viper.GetBool("a-general.color") {
|
||||
bottomBar.SetLabelColor(tcell.ColorGreen)
|
||||
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"))
|
||||
} else {
|
||||
bottomBar.SetLabelColor(tcell.ColorBlack)
|
||||
bottomBar.SetBackgroundColor(tcell.ColorWhite)
|
||||
bottomBar.
|
||||
SetLabelColor(tcell.ColorBlack).
|
||||
SetFieldBackgroundColor(tcell.ColorWhite).
|
||||
SetFieldTextColor(tcell.ColorBlack)
|
||||
}
|
||||
bottomBar.SetBackgroundColor(tcell.ColorWhite)
|
||||
bottomBar.SetDoneFunc(func(key tcell.Key) {
|
||||
tab := curTab
|
||||
|
||||
@ -432,7 +437,12 @@ func NewTab() {
|
||||
// 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"][darkcyan] %d [white][""]|`, curTab, curTab+1)
|
||||
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)
|
||||
}
|
||||
@ -477,7 +487,12 @@ func CloseTab() {
|
||||
tabRow.Clear()
|
||||
if viper.GetBool("a-general.color") {
|
||||
for i := 0; i < NumTabs(); i++ {
|
||||
fmt.Fprintf(tabRow, `["%d"][darkcyan] %d [white][""]|`, i, i+1)
|
||||
fmt.Fprintf(tabRow, `["%d"][%s] %d [%s][""]|`,
|
||||
i,
|
||||
config.GetColorString("tab_num"),
|
||||
i+1,
|
||||
config.GetColorString("tab_divider"),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < NumTabs(); i++ {
|
||||
|
@ -23,50 +23,62 @@ import (
|
||||
|
||||
// For choosing between download and the portal - copy of YesNo basically
|
||||
var dlChoiceModal = cview.NewModal().
|
||||
SetTextColor(tcell.ColorWhite).
|
||||
AddButtons([]string{"Download", "Open in portal", "Cancel"})
|
||||
|
||||
// Channel to indicate what choice they made using the button text
|
||||
var dlChoiceCh = make(chan string)
|
||||
|
||||
var dlModal = cview.NewModal().
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
var dlModal = cview.NewModal()
|
||||
|
||||
func dlInit() {
|
||||
if viper.GetBool("a-general.color") {
|
||||
dlChoiceModal.SetButtonBackgroundColor(tcell.ColorNavy).
|
||||
SetButtonTextColor(tcell.ColorWhite).
|
||||
SetBackgroundColor(tcell.ColorPurple)
|
||||
dlModal.SetButtonBackgroundColor(tcell.ColorNavy).
|
||||
SetButtonTextColor(tcell.ColorWhite).
|
||||
SetBackgroundColor(tcell.Color130) // DarkOrange3, #af5f00
|
||||
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"))
|
||||
|
||||
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"))
|
||||
} else {
|
||||
dlChoiceModal.SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack).
|
||||
SetBackgroundColor(tcell.ColorBlack)
|
||||
SetBackgroundColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
dlChoiceModal.SetBorderColor(tcell.ColorWhite)
|
||||
dlChoiceModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
|
||||
dlModal.SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack).
|
||||
SetBackgroundColor(tcell.ColorBlack)
|
||||
SetBackgroundColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
dlModal.GetFrame().
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
}
|
||||
|
||||
dlChoiceModal.SetBorder(true)
|
||||
dlChoiceModal.SetBorderColor(tcell.ColorWhite)
|
||||
dlChoiceModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
dlChoiceModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
dlChoiceCh <- buttonLabel
|
||||
})
|
||||
dlChoiceModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
dlChoiceModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
|
||||
dlModal.SetBorder(true)
|
||||
dlModal.SetBorderColor(tcell.ColorWhite)
|
||||
dlModal.GetFrame().
|
||||
SetTitleAlign(cview.AlignCenter).
|
||||
SetTitle(" Download ")
|
||||
dlModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
if buttonLabel == "Ok" {
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
}
|
||||
})
|
||||
dlModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
dlModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
dlModal.GetFrame().SetTitle(" Download ")
|
||||
}
|
||||
|
||||
// dlChoice displays the download choice modal and acts on the user's choice.
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
var helpCells = strings.TrimSpace(`
|
||||
?|Bring up this help.
|
||||
?|Bring up this help. You can scroll!
|
||||
Esc|Leave the help
|
||||
Arrow keys, h/j/k/l|Scroll and move a page.
|
||||
Tab|Navigate to the next item in a popup.
|
||||
@ -22,6 +22,7 @@ spacebar|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.
|
||||
Numbers|Go to links 1-10 respectively.
|
||||
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.
|
||||
@ -37,13 +38,14 @@ Ctrl-B|View bookmarks
|
||||
Ctrl-D|Add, change, or remove a bookmark for the current page.
|
||||
Ctrl-S|Save the current page to your downloads.
|
||||
q, Ctrl-Q|Quit
|
||||
Ctrl-C|Hard quit. This can be used when in the middle of downloading, for example.
|
||||
Ctrl-C|Hard quit. This can be used when in the middle of downloading,
|
||||
|for example.
|
||||
`)
|
||||
|
||||
var helpTable = cview.NewTable().
|
||||
SetSelectable(false, false).
|
||||
SetBorders(false).
|
||||
SetBordersColor(tcell.ColorGray)
|
||||
SetScrollBarVisibility(cview.ScrollBarNever)
|
||||
|
||||
// Help displays the help and keybindings.
|
||||
func Help() {
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/makeworld-the-better-one/amfora/config"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
@ -16,22 +17,16 @@ import (
|
||||
// The bookmark modal is in bookmarks.go
|
||||
|
||||
var infoModal = cview.NewModal().
|
||||
SetTextColor(tcell.ColorWhite).
|
||||
AddButtons([]string{"Ok"})
|
||||
|
||||
var errorModal = cview.NewModal().
|
||||
SetTextColor(tcell.ColorWhite).
|
||||
AddButtons([]string{"Ok"})
|
||||
|
||||
var inputModal = cview.NewModal().
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
//AddButtons([]string{"Send", "Cancel"}) - Added in func
|
||||
|
||||
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().
|
||||
SetTextColor(tcell.ColorWhite).
|
||||
AddButtons([]string{"Yes", "No"})
|
||||
|
||||
// Channel to receive yesNo answer on
|
||||
@ -48,29 +43,60 @@ func modalInit() {
|
||||
|
||||
// Color setup
|
||||
if viper.GetBool("a-general.color") {
|
||||
infoModal.SetBackgroundColor(tcell.ColorGray).
|
||||
SetButtonBackgroundColor(tcell.ColorNavy).
|
||||
SetButtonTextColor(tcell.ColorWhite)
|
||||
errorModal.SetBackgroundColor(tcell.ColorMaroon).
|
||||
SetButtonBackgroundColor(tcell.ColorNavy).
|
||||
SetButtonTextColor(tcell.ColorWhite)
|
||||
inputModal.SetBackgroundColor(tcell.ColorGreen).
|
||||
SetButtonBackgroundColor(tcell.ColorNavy).
|
||||
SetButtonTextColor(tcell.ColorWhite)
|
||||
yesNoModal.SetButtonBackgroundColor(tcell.ColorNavy).
|
||||
SetButtonTextColor(tcell.ColorWhite)
|
||||
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"))
|
||||
|
||||
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"))
|
||||
|
||||
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"))
|
||||
|
||||
yesNoModal.SetButtonBackgroundColor(config.GetColor("btn_bg")).
|
||||
SetButtonTextColor(config.GetColor("btn_text"))
|
||||
} else {
|
||||
infoModal.SetBackgroundColor(tcell.ColorBlack).
|
||||
SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack)
|
||||
SetButtonTextColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
infoModal.GetFrame().
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
|
||||
errorModal.SetBackgroundColor(tcell.ColorBlack).
|
||||
SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack)
|
||||
SetButtonTextColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
errorModal.GetFrame().
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
|
||||
inputModal.SetBackgroundColor(tcell.ColorBlack).
|
||||
SetButtonBackgroundColor(tcell.ColorWhite).
|
||||
SetButtonTextColor(tcell.ColorBlack)
|
||||
SetButtonTextColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
inputModal.GetFrame().
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
inputModal.GetForm().
|
||||
SetLabelColor(tcell.ColorWhite).
|
||||
SetFieldBackgroundColor(tcell.ColorWhite).
|
||||
SetFieldTextColor(tcell.ColorBlack)
|
||||
|
||||
@ -82,24 +108,23 @@ func modalInit() {
|
||||
// Modal functions that can't be added up above, because they return the wrong type
|
||||
|
||||
infoModal.SetBorder(true)
|
||||
infoModal.SetBorderColor(tcell.ColorWhite)
|
||||
infoModal.GetFrame().
|
||||
SetTitleAlign(cview.AlignCenter).
|
||||
SetTitle(" Info ")
|
||||
infoModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
})
|
||||
infoModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
infoModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
infoModal.GetFrame().SetTitle(" Info ")
|
||||
|
||||
errorModal.SetBorder(true)
|
||||
errorModal.SetBorderColor(tcell.ColorWhite)
|
||||
errorModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
errorModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
||||
})
|
||||
errorModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
errorModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
|
||||
inputModal.SetBorder(true)
|
||||
inputModal.SetBorderColor(tcell.ColorWhite)
|
||||
inputModal.GetFrame().
|
||||
SetTitleAlign(cview.AlignCenter).
|
||||
SetTitle(" Input ")
|
||||
inputModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
if buttonLabel == "Send" {
|
||||
inputCh <- inputModalText
|
||||
@ -107,15 +132,10 @@ func modalInit() {
|
||||
}
|
||||
// Empty string indicates no input
|
||||
inputCh <- ""
|
||||
|
||||
//tabPages.SwitchToPage(strconv.Itoa(curTab)) - handled in Input()
|
||||
})
|
||||
inputModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
inputModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
inputModal.GetFrame().SetTitle(" Input ")
|
||||
|
||||
yesNoModal.SetBorder(true)
|
||||
yesNoModal.SetBorderColor(tcell.ColorWhite)
|
||||
yesNoModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
yesNoModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||
if buttonLabel == "Yes" {
|
||||
yesNoCh <- true
|
||||
@ -125,8 +145,6 @@ func modalInit() {
|
||||
|
||||
//tabPages.SwitchToPage(strconv.Itoa(curTab)) - Handled in YesNo()
|
||||
})
|
||||
yesNoModal.GetFrame().SetTitleColor(tcell.ColorWhite)
|
||||
yesNoModal.GetFrame().SetTitleAlign(cview.AlignCenter)
|
||||
|
||||
bkmkInit()
|
||||
dlInit()
|
||||
@ -174,7 +192,7 @@ func Input(prompt string) (string, bool) {
|
||||
inputModalText = text
|
||||
})
|
||||
|
||||
inputModal.SetText(prompt)
|
||||
inputModal.SetText(prompt + " ")
|
||||
tabPages.ShowPage("input")
|
||||
tabPages.SendToFront("input")
|
||||
App.SetFocus(inputModal)
|
||||
@ -191,9 +209,19 @@ 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(tcell.ColorPurple)
|
||||
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"))
|
||||
} else {
|
||||
yesNoModal.SetBackgroundColor(tcell.ColorBlack)
|
||||
yesNoModal.
|
||||
SetBackgroundColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
yesNoModal.GetFrame().
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
}
|
||||
yesNoModal.GetFrame().SetTitle("")
|
||||
yesNoModal.SetText(prompt)
|
||||
@ -210,12 +238,22 @@ func YesNo(prompt string) bool {
|
||||
// Tofu displays the TOFU warning modal.
|
||||
// It returns a bool indicating whether the user wants to continue.
|
||||
func Tofu(host string, expiry time.Time) bool {
|
||||
// Reuses yesNoModal, with error colour
|
||||
// Reuses yesNoModal, with error color
|
||||
|
||||
if viper.GetBool("a-general.color") {
|
||||
yesNoModal.SetBackgroundColor(tcell.ColorMaroon)
|
||||
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"))
|
||||
} else {
|
||||
yesNoModal.SetBackgroundColor(tcell.ColorBlack)
|
||||
yesNoModal.
|
||||
SetBackgroundColor(tcell.ColorBlack).
|
||||
SetTextColor(tcell.ColorWhite)
|
||||
yesNoModal.
|
||||
SetBorderColor(tcell.ColorWhite).
|
||||
SetTitleColor(tcell.ColorWhite)
|
||||
}
|
||||
yesNoModal.GetFrame().SetTitle(" TOFU ")
|
||||
yesNoModal.SetText(
|
||||
|
@ -24,13 +24,13 @@ 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
|
||||
reformatMut *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
|
||||
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
|
||||
}
|
||||
|
||||
// makeNewTab initializes an tab struct with no content.
|
||||
@ -45,9 +45,9 @@ func makeNewTab() *tab {
|
||||
SetChangedFunc(func() {
|
||||
App.Draw()
|
||||
}),
|
||||
history: &tabHistory{},
|
||||
reformatMut: &sync.Mutex{},
|
||||
mode: tabModeDone,
|
||||
history: &tabHistory{},
|
||||
reformatMu: &sync.Mutex{},
|
||||
mode: tabModeDone,
|
||||
}
|
||||
t.view.SetDoneFunc(func(key tcell.Key) {
|
||||
// Altered from: https://gitlab.com/tslocum/cview/-/blob/master/demos/textview/main.go
|
||||
|
@ -109,7 +109,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int) (*structs
|
||||
Links: links,
|
||||
}, nil
|
||||
} else if strings.HasPrefix(mediatype, "text/") {
|
||||
if mediatype == "text/x-ansi" || strings.HasSuffix(url, ".ans") {
|
||||
if mediatype == "text/x-ansi" || strings.HasSuffix(url, ".ans") || strings.HasSuffix(url, ".ansi") {
|
||||
// ANSI
|
||||
return &structs.Page{
|
||||
Mediatype: structs.TextAnsi,
|
||||
|
@ -5,10 +5,12 @@
|
||||
package renderer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
urlPkg "net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/makeworld-the-better-one/amfora/config"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
@ -33,7 +35,9 @@ 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) + lines[i] + "\n"
|
||||
shifted += strings.Repeat(" ", leftMargin) +
|
||||
"[" + config.GetColorString("regular_text") + "]" + lines[i] + "[-]" +
|
||||
"\n"
|
||||
}
|
||||
return shifted
|
||||
}
|
||||
@ -70,6 +74,17 @@ func wrapLine(line string, width int, prefix, suffix string, includeFirst bool)
|
||||
return ret
|
||||
}
|
||||
|
||||
// tagLines splits a string into lines and adds a the given
|
||||
// string to the start and another to the end.
|
||||
// It is used for adding cview color tags.
|
||||
func tagLines(s, start, end string) string {
|
||||
lines := strings.Split(s, "\n")
|
||||
for i := range lines {
|
||||
lines[i] = start + lines[i] + end
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
// convertRegularGemini converts non-preformatted blocks of text/gemini
|
||||
// into a cview-compatible format.
|
||||
// It also returns a slice of link URLs.
|
||||
@ -88,14 +103,16 @@ func convertRegularGemini(s string, numLinks, width int) (string, []string) {
|
||||
|
||||
if strings.HasPrefix(lines[i], "#") {
|
||||
// Headings
|
||||
var tag string
|
||||
if viper.GetBool("a-general.color") {
|
||||
if strings.HasPrefix(lines[i], "###") {
|
||||
wrappedLines = append(wrappedLines, wrapLine(lines[i], width, "[fuchsia::b]", "[-::-]", true)...)
|
||||
tag = fmt.Sprintf("[%s::b]", config.GetColorString("hdg_3"))
|
||||
} else if strings.HasPrefix(lines[i], "##") {
|
||||
wrappedLines = append(wrappedLines, wrapLine(lines[i], width, "[lime::b]", "[-::-]", true)...)
|
||||
tag = fmt.Sprintf("[%s::b]", config.GetColorString("hdg_2"))
|
||||
} else if strings.HasPrefix(lines[i], "#") {
|
||||
wrappedLines = append(wrappedLines, wrapLine(lines[i], width, "[red::b]", "[-::-]", true)...)
|
||||
tag = fmt.Sprintf("[%s::b]", config.GetColorString("hdg_1"))
|
||||
}
|
||||
wrappedLines = append(wrappedLines, wrapLine(lines[i], width, tag, "[-::-]", true)...)
|
||||
} else {
|
||||
// Just bold, no colors
|
||||
wrappedLines = append(wrappedLines, wrapLine(lines[i], width, "[::b]", "[-::-]", true)...)
|
||||
@ -141,34 +158,37 @@ func convertRegularGemini(s string, numLinks, width int) (string, []string) {
|
||||
if err == nil && (pU.Scheme == "" || pU.Scheme == "gemini" || pU.Scheme == "about") {
|
||||
// A gemini link
|
||||
// Add the link text in blue (in a region), and a gray link number to the left of it
|
||||
// Those are the default colors, anyway
|
||||
|
||||
wrappedLink = wrapLine(linkText, width,
|
||||
strings.Repeat(" ", len(strconv.Itoa(numLinks+len(links)))+4)+ // +4 for spaces and brackets
|
||||
`["`+strconv.Itoa(numLinks+len(links)-1)+`"][dodgerblue]`,
|
||||
`["`+strconv.Itoa(numLinks+len(links)-1)+`"][`+config.GetColorString("amfora_link")+`]`,
|
||||
`[-][""]`,
|
||||
false, // Don't indent the first line, it's the one with link number
|
||||
)
|
||||
|
||||
// Add special stuff to first line, like the link number
|
||||
wrappedLink[0] = `[silver::b][` + strconv.Itoa(numLinks+len(links)) + "[]" + "[-::-] " +
|
||||
`["` + strconv.Itoa(numLinks+len(links)-1) + `"][dodgerblue]` +
|
||||
wrappedLink[0] = fmt.Sprintf(`[%s::b][`, config.GetColorString("link_number")) +
|
||||
strconv.Itoa(numLinks+len(links)) + "[]" + "[-::-] " +
|
||||
`["` + strconv.Itoa(numLinks+len(links)-1) + `"][` + config.GetColorString("amfora_link") + `]` +
|
||||
wrappedLink[0] + `[-][""]`
|
||||
} else {
|
||||
// Not a gemini link, use purple instead
|
||||
// Not a gemini link
|
||||
|
||||
wrappedLink = wrapLine(linkText, width,
|
||||
strings.Repeat(" ", len(strconv.Itoa(numLinks+len(links)))+4)+ // +4 for spaces and brackets
|
||||
`["`+strconv.Itoa(numLinks+len(links)-1)+`"][#8700d7]`,
|
||||
`["`+strconv.Itoa(numLinks+len(links)-1)+`"][`+config.GetColorString("foreign_link")+`]`,
|
||||
`[-][""]`,
|
||||
false, // Don't indent the first line, it's the one with link number
|
||||
)
|
||||
|
||||
wrappedLink[0] = `[silver::b][` + strconv.Itoa(numLinks+len(links)) + "[]" + "[-::-] " +
|
||||
`["` + strconv.Itoa(numLinks+len(links)-1) + `"][#8700d7]` +
|
||||
wrappedLink[0] = fmt.Sprintf(`[%s::b][`, config.GetColorString("link_number")) +
|
||||
strconv.Itoa(numLinks+len(links)) + "[]" + "[-::-] " +
|
||||
`["` + strconv.Itoa(numLinks+len(links)-1) + `"][` + config.GetColorString("foreign_link") + `]` +
|
||||
wrappedLink[0] + `[-][""]`
|
||||
}
|
||||
} else {
|
||||
// No colours allowed
|
||||
// No colors allowed
|
||||
|
||||
wrappedLink = wrapLine(linkText, width,
|
||||
strings.Repeat(" ", len(strconv.Itoa(numLinks+len(links)))+4)+ // +4 for spaces and brackets
|
||||
@ -188,9 +208,12 @@ func convertRegularGemini(s string, numLinks, width int) (string, []string) {
|
||||
} else if strings.HasPrefix(lines[i], "* ") {
|
||||
if viper.GetBool("a-general.bullets") {
|
||||
// Wrap list item, and indent wrapped lines past the bullet
|
||||
wrappedItem := wrapLine(lines[i][1:], width, " ", "", false)
|
||||
wrappedItem := wrapLine(lines[i][1:], width,
|
||||
fmt.Sprintf(" [%s]", config.GetColorString("list_text")),
|
||||
"[-]", false)
|
||||
// Add bullet
|
||||
wrappedItem[0] = " \u2022" + wrappedItem[0]
|
||||
wrappedItem[0] = fmt.Sprintf(" [%s]\u2022", config.GetColorString("list_text")) +
|
||||
wrappedItem[0] + "[-]"
|
||||
wrappedLines = append(wrappedLines, wrappedItem...)
|
||||
}
|
||||
// Optionally list lines could be colored here too, if color is enabled
|
||||
@ -200,14 +223,19 @@ func convertRegularGemini(s string, numLinks, width int) (string, []string) {
|
||||
// Remove beginning quote and maybe space
|
||||
lines[i] = strings.TrimPrefix(lines[i], ">")
|
||||
lines[i] = strings.TrimPrefix(lines[i], " ")
|
||||
wrappedLines = append(wrappedLines, wrapLine(lines[i], width, "> [::i]", "[::-]", true)...)
|
||||
wrappedLines = append(wrappedLines,
|
||||
wrapLine(lines[i], width, fmt.Sprintf("[%s::i]> ", config.GetColorString("quote_text")),
|
||||
"[-::-]", true)...,
|
||||
)
|
||||
|
||||
} else if strings.TrimSpace(lines[i]) == "" {
|
||||
// Just add empty line without processing
|
||||
wrappedLines = append(wrappedLines, "")
|
||||
} else {
|
||||
// Regular line, just wrap it
|
||||
wrappedLines = append(wrappedLines, wrapLine(lines[i], width, "", "", true)...)
|
||||
wrappedLines = append(wrappedLines, wrapLine(lines[i], width,
|
||||
fmt.Sprintf("[%s]", config.GetColorString("regular_text")),
|
||||
"[-]", true)...)
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,7 +265,11 @@ func RenderGemini(s string, width, leftMargin int) (string, []string) {
|
||||
if pre {
|
||||
// In a preformatted block, so add the text as is
|
||||
// Don't add the current line with backticks
|
||||
rendered += buf
|
||||
rendered += tagLines(
|
||||
buf,
|
||||
fmt.Sprintf("[%s]", config.GetColorString("preformatted_text")),
|
||||
"[-]",
|
||||
)
|
||||
} else {
|
||||
// Not preformatted, regular text
|
||||
ren, lks := convertRegularGemini(buf, len(links), width)
|
||||
|
@ -21,7 +21,7 @@ type Page struct {
|
||||
Url string
|
||||
Mediatype Mediatype
|
||||
Raw string // The raw response, as received over the network
|
||||
Content string // The processed content, NOT raw. Uses cview colour tags. All link/link texts must have region tags. It will also have a left margin.
|
||||
Content string // The processed content, NOT raw. Uses cview color tags. All link/link texts must have region tags. It will also have a left margin.
|
||||
Links []string // URLs, for each region in the content.
|
||||
Row int // Scroll position
|
||||
Column int // ditto
|
||||
|
Loading…
Reference in New Issue
Block a user