mirror of
https://github.com/makew0rld/amfora.git
synced 2024-11-03 02:37:23 -05:00
✨ Side-scrolling and margin UX greatly improved
#1 will be fixed completely once margins are added to the new tab page
This commit is contained in:
parent
847bd6eefa
commit
850d6f0454
2
NOTES.md
2
NOTES.md
@ -10,3 +10,5 @@
|
||||
- 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)
|
||||
- Newtab page doesn't get margin
|
||||
- Config funcs should become variables
|
@ -31,7 +31,7 @@ Just call `amfora` or `amfora <url> <other-url>` on the terminal. On Windows it
|
||||
|
||||
The project keeps many standard terminal keybindings and is intuitive. Press <kbd>?</kbd> inside the application to pull up the help menu with a list of all the keybindings, and <kbd>Esc</kbd> to leave it. If you have used Bombadillo you will find it similar.
|
||||
|
||||
It is designed with large or fullscreen terminals in mind. For optimal usage, make your terminal fullscreen. It was also designed with a dark background terminal in mind, but please file an issue if the colour choices look bad on your terminal setup.
|
||||
It is designed with large or fullscreen terminals in mind. For optimal usage, make your terminal fullscreen. It was also designed with a dark background terminal in mind, but please file an issue if the colour choices look bad on your terminal setup. It was tested with left-to-right languages, and will likely not work as well with right-to-left languages like Arabic.
|
||||
|
||||
## Features / Roadmap
|
||||
Features in *italics* are in the master branch, but not in the latest release.
|
||||
|
@ -95,7 +95,8 @@ func Init() error {
|
||||
viper.SetDefault("a-general.search", "gus.guru/search")
|
||||
viper.SetDefault("a-general.color", true)
|
||||
viper.SetDefault("a-general.bullets", true)
|
||||
viper.SetDefault("a-general.wrap_width", 100)
|
||||
viper.SetDefault("a-general.left_margin", 0.15)
|
||||
viper.SetDefault("a-general.max_width", 100)
|
||||
viper.SetDefault("cache.max_size", 0)
|
||||
viper.SetDefault("cache.max_pages", 20)
|
||||
|
||||
@ -112,11 +113,3 @@ func Init() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetWrapWidth() int {
|
||||
i := viper.GetInt("a-general.wrap_width")
|
||||
if i <= 0 {
|
||||
return 100 // The default
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
@ -23,7 +23,10 @@ http = "default"
|
||||
search = "gemini://gus.guru/search" # Any URL that will accept a query string can be put here
|
||||
color = true # Whether colors will be used in the terminal
|
||||
bullets = true # Whether to replace list asterisks with unicode bullets
|
||||
wrap_width = 100 # How many columns to wrap a page's text to. Preformatted blocks are not wrapped.
|
||||
|
||||
# A number from 0 to 1, indicating what percentage of the terminal width the left margin should take up.
|
||||
left_margin = 0.15
|
||||
max_width = 100 # The max number of columns to wrap a page's text to. Preformatted blocks are not wrapped.
|
||||
|
||||
[bookmarks]
|
||||
# Make sure to quote the key names if you edit this part yourself
|
||||
|
@ -20,7 +20,10 @@ http = "default"
|
||||
search = "gemini://gus.guru/search" # Any URL that will accept a query string can be put here
|
||||
color = true # Whether colors will be used in the terminal
|
||||
bullets = true # Whether to replace list asterisks with unicode bullets
|
||||
wrap_width = 100 # How many columns to wrap a page's text to. Preformatted blocks are not wrapped.
|
||||
|
||||
# A number from 0 to 1, indicating what percentage of the terminal width the left margin should take up.
|
||||
left_margin = 0.15
|
||||
max_width = 100 # The max number of columns to wrap a page's text to. Preformatted blocks are not wrapped.
|
||||
|
||||
[bookmarks]
|
||||
# Make sure to quote the key names if you edit this part yourself
|
||||
|
@ -13,7 +13,6 @@ import (
|
||||
"github.com/makeworld-the-better-one/amfora/cache"
|
||||
"github.com/makeworld-the-better-one/amfora/structs"
|
||||
"gitlab.com/tslocum/cview"
|
||||
//"github.com/makeworld-the-better-one/amfora/cview"
|
||||
)
|
||||
|
||||
var curTab = -1 // What number tab is currently visible, -1 means there are no tabs at all
|
||||
@ -21,6 +20,9 @@ var tabMap = make(map[int]*structs.Page) // Map of tab number to page
|
||||
// Holds the actual tab primitives
|
||||
var tabViews = make(map[int]*cview.TextView)
|
||||
|
||||
var termW int
|
||||
var termH int
|
||||
|
||||
// The user input and URL display bar at the bottom
|
||||
var bottomBar = cview.NewInputField().
|
||||
SetFieldBackgroundColor(tcell.ColorWhite).
|
||||
@ -64,17 +66,24 @@ 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 5/6 of the screen horizontally
|
||||
AddItem(nil, 0, 1, false),
|
||||
0, 1, true).
|
||||
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)
|
||||
|
||||
var App = cview.NewApplication().EnableMouse(false).SetRoot(layout, true)
|
||||
var App = cview.NewApplication().
|
||||
EnableMouse(false).
|
||||
SetRoot(layout, true).
|
||||
SetAfterResizeFunc(func(width int, height int) {
|
||||
// Store width and height for calculations
|
||||
termW = width
|
||||
termH = height
|
||||
})
|
||||
|
||||
var renderedNewTabContent string
|
||||
var newTabLinks []string
|
||||
@ -116,8 +125,8 @@ func Init() {
|
||||
bottomBar.SetDoneFunc(func(key tcell.Key) {
|
||||
switch key {
|
||||
case tcell.KeyEnter:
|
||||
// TODO: Support search
|
||||
// Send the URL/number typed in
|
||||
// Figure out whether it's a URL, link number, or search
|
||||
// And send out a request
|
||||
|
||||
query := bottomBar.GetText()
|
||||
|
||||
@ -163,7 +172,7 @@ func Init() {
|
||||
})
|
||||
|
||||
// Render the default new tab content ONCE and store it for later
|
||||
renderedNewTabContent, newTabLinks = renderer.RenderGemini(newTabContent)
|
||||
renderedNewTabContent, newTabLinks = renderer.RenderGemini(newTabContent, textWidth())
|
||||
newTabPage = structs.Page{Content: renderedNewTabContent, Links: newTabLinks}
|
||||
|
||||
modalInit()
|
||||
|
@ -13,11 +13,34 @@ import (
|
||||
"github.com/makeworld-the-better-one/go-gemini"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/tslocum/cview"
|
||||
//"github.com/makeworld-the-better-one/amfora/cview"
|
||||
)
|
||||
|
||||
// This file contains the functions that aren't part of the public API.
|
||||
|
||||
func leftMargin() int {
|
||||
return int(float64(termW) * viper.GetFloat64("a-general.left_margin"))
|
||||
}
|
||||
|
||||
func textWidth() int {
|
||||
if termW <= 0 {
|
||||
// This prevent a flash of 1-column text on startup, when the terminal
|
||||
// width hasn't been initialized.
|
||||
return viper.GetInt("a-general.max_width")
|
||||
}
|
||||
|
||||
rightMargin := leftMargin()
|
||||
if leftMargin() > 10 {
|
||||
// 10 is the max right margin
|
||||
rightMargin = 10
|
||||
}
|
||||
|
||||
max := termW - leftMargin() - rightMargin
|
||||
if max < viper.GetInt("a-general.max_width") {
|
||||
return max
|
||||
}
|
||||
return viper.GetInt("a-general.max_width")
|
||||
}
|
||||
|
||||
// pathEscape is the same as url.PathEscape, but it also replaces the +.
|
||||
func pathEscape(path string) string {
|
||||
return strings.ReplaceAll(url.PathEscape(path), "+", "%2B")
|
||||
@ -81,7 +104,17 @@ func followLink(prev, next string) {
|
||||
func setPage(p *structs.Page) {
|
||||
saveScroll() // Save the scroll of the previous page
|
||||
|
||||
// Change page
|
||||
if !p.Displayable {
|
||||
// Add margin to page based on terminal width
|
||||
var shifted string
|
||||
for _, line := range strings.Split(p.Content, "\n") {
|
||||
shifted += strings.Repeat(" ", leftMargin()) + line + "\n"
|
||||
}
|
||||
p.Content = shifted
|
||||
p.Displayable = true
|
||||
}
|
||||
|
||||
// Change page on screen
|
||||
tabMap[curTab] = p
|
||||
tabViews[curTab].SetText(p.Content)
|
||||
tabViews[curTab].Highlight("") // Turn off highlights
|
||||
@ -165,7 +198,7 @@ func handleURL(u string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
if renderer.CanDisplay(res) {
|
||||
page, err := renderer.MakePage(u, res)
|
||||
page, err := renderer.MakePage(u, res, textWidth())
|
||||
if err != nil {
|
||||
Error("Page Error", "Issuing creating page: "+err.Error())
|
||||
// Set the bar back to original URL
|
||||
|
@ -46,7 +46,7 @@ func CanDisplay(res *gemini.Response) bool {
|
||||
return err == nil && enc != nil
|
||||
}
|
||||
|
||||
func MakePage(url string, res *gemini.Response) (*structs.Page, error) {
|
||||
func MakePage(url string, res *gemini.Response, width int) (*structs.Page, error) {
|
||||
if !CanDisplay(res) {
|
||||
return nil, errors.New("not valid content for a Page")
|
||||
}
|
||||
@ -83,7 +83,7 @@ func MakePage(url string, res *gemini.Response) (*structs.Page, error) {
|
||||
}, nil
|
||||
}
|
||||
if mediatype == "text/gemini" {
|
||||
rendered, links := RenderGemini(utfText)
|
||||
rendered, links := RenderGemini(utfText, width)
|
||||
return &structs.Page{
|
||||
Url: url,
|
||||
Content: rendered,
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/makeworld-the-better-one/amfora/config"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
@ -18,10 +17,11 @@ import (
|
||||
// into a cview-compatible format.
|
||||
// It also returns a slice of link URLs.
|
||||
// numLinks is the number of links that exist so far.
|
||||
// width is the number of columns to wrap to.
|
||||
//
|
||||
// Since this only works on non-preformatted blocks, renderGemini
|
||||
// Since this only works on non-preformatted blocks, RenderGemini
|
||||
// should always be used instead.
|
||||
func convertRegularGemini(s string, numLinks int) (string, []string) {
|
||||
func convertRegularGemini(s string, numLinks int, width int) (string, []string) {
|
||||
links := make([]string, 0)
|
||||
lines := strings.Split(s, "\n")
|
||||
wrappedLines := make([]string, 0) // Final result
|
||||
@ -111,13 +111,13 @@ func convertRegularGemini(s string, numLinks int) (string, []string) {
|
||||
lines[i] = strings.TrimPrefix(lines[i], ">")
|
||||
lines[i] = strings.TrimPrefix(lines[i], " ")
|
||||
|
||||
temp := cview.WordWrap(lines[i], config.GetWrapWidth())
|
||||
temp := cview.WordWrap(lines[i], width)
|
||||
for i := range temp {
|
||||
temp[i] = "> " + temp[i]
|
||||
}
|
||||
wrappedLines = append(wrappedLines, temp...)
|
||||
} else {
|
||||
wrappedLines = append(wrappedLines, cview.WordWrap(lines[i], config.GetWrapWidth())...)
|
||||
wrappedLines = append(wrappedLines, cview.WordWrap(lines[i], width)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -125,9 +125,9 @@ func convertRegularGemini(s string, numLinks int) (string, []string) {
|
||||
return strings.Join(wrappedLines, "\r\n"), links
|
||||
}
|
||||
|
||||
// renderGemini converts text/gemini into a cview displayable format.
|
||||
// RenderGemini converts text/gemini into a cview displayable format.
|
||||
// It also returns a slice of link URLs.
|
||||
func RenderGemini(s string) (string, []string) {
|
||||
func RenderGemini(s string, width int) (string, []string) {
|
||||
s = cview.Escape(s)
|
||||
if viper.GetBool("a-general.color") {
|
||||
s = cview.TranslateANSI(s)
|
||||
@ -148,7 +148,7 @@ func RenderGemini(s string) (string, []string) {
|
||||
rendered += buf
|
||||
} else {
|
||||
// Not preformatted, regular text
|
||||
ren, lks := convertRegularGemini(buf, len(links))
|
||||
ren, lks := convertRegularGemini(buf, len(links), width)
|
||||
links = append(links, lks...)
|
||||
rendered += ren
|
||||
}
|
||||
@ -166,7 +166,7 @@ func RenderGemini(s string) (string, []string) {
|
||||
} else {
|
||||
// Not preformatted, regular text
|
||||
// Same code as in the loop above
|
||||
ren, lks := convertRegularGemini(buf, len(links))
|
||||
ren, lks := convertRegularGemini(buf, len(links), width)
|
||||
links = append(links, lks...)
|
||||
rendered += ren
|
||||
}
|
||||
|
@ -2,11 +2,12 @@ package structs
|
||||
|
||||
// Page is for storing UTF-8 text/gemini pages, as well as text/plain pages.
|
||||
type Page struct {
|
||||
Url string
|
||||
Content string // The processed content, NOT raw. Uses cview colour tags. All link/link texts must have region tags.
|
||||
Links []string // URLs, for each region in the content.
|
||||
Row int // Scroll position
|
||||
Column int
|
||||
Url string
|
||||
Content string // The processed content, NOT raw. Uses cview colour tags. All link/link texts must have region tags.
|
||||
Links []string // URLs, for each region in the content.
|
||||
Row int // Scroll position
|
||||
Column int // ditto
|
||||
Displayable bool // Set to true once the content has been modified to display nicely on the screen - margins added
|
||||
}
|
||||
|
||||
// Size returns an approx. size of a Page in bytes.
|
||||
|
Loading…
Reference in New Issue
Block a user