start of a bubbletea TUI
This commit is contained in:
parent
dfebc9013b
commit
dd2a06c1e1
148
actions.go
148
actions.go
|
@ -32,13 +32,22 @@ var (
|
|||
ErrCantMoveRelative = errors.New("next/previous only work after navigating to a link on a page")
|
||||
ErrAlreadyAtTop = errors.New("already at the site root")
|
||||
ErrInvalidNumericLink = errors.New("no link with that number")
|
||||
ErrInvalidLink = errors.New("that doesn't look like a valid URL")
|
||||
ErrSaveNeedsFilename = errors.New("save requires a filename argument")
|
||||
ErrInvalidMarkArgs = errors.New("mark what?")
|
||||
ErrInvalidTourArgs = errors.New("tour what?")
|
||||
ErrOnlyTextGemini = errors.New("that is only supported for text/gemini pages")
|
||||
)
|
||||
|
||||
func ErrInvalidLink(invalidURL string) error {
|
||||
return invalidLinkErr(invalidURL)
|
||||
}
|
||||
|
||||
type invalidLinkErr string
|
||||
|
||||
func (ie invalidLinkErr) Error() string {
|
||||
return fmt.Sprintf("that doesn't look like a valid URL: %s", string(ie))
|
||||
}
|
||||
|
||||
func About(_ *BrowserState) error {
|
||||
_, err := fmt.Println(`
|
||||
...
|
||||
|
@ -73,13 +82,13 @@ It was written by TJP and released to the public domain.
|
|||
return err
|
||||
}
|
||||
|
||||
func Navigate(state *BrowserState, target *url.URL, navIndex int, conf *Config) error {
|
||||
func Navigate(state *BrowserState, target *url.URL, navIndex int) error {
|
||||
if state.Url == nil || target.String() != state.Url.String() {
|
||||
pushHistory(state, target, navIndex)
|
||||
}
|
||||
state.Modal = nil
|
||||
|
||||
return Reload(state, conf)
|
||||
return Reload(state)
|
||||
}
|
||||
|
||||
func pushHistory(state *BrowserState, target *url.URL, navIndex int) {
|
||||
|
@ -91,6 +100,25 @@ func pushHistory(state *BrowserState, target *url.URL, navIndex int) {
|
|||
NavIndex: navIndex,
|
||||
}
|
||||
hist.Forward = state.History
|
||||
|
||||
purgeOldHistory(state)
|
||||
}
|
||||
|
||||
func purgeOldHistory(state *BrowserState) {
|
||||
if state.Depth <= state.SavedHistoryDepth {
|
||||
return
|
||||
}
|
||||
|
||||
d := state.SavedHistoryDepth
|
||||
h := state.History
|
||||
for d > 0 {
|
||||
h = h.Back
|
||||
d -= 1
|
||||
}
|
||||
|
||||
h.Body = nil
|
||||
h.Formatted = ""
|
||||
h.Links = nil
|
||||
}
|
||||
|
||||
func gopherURL(u *url.URL) (string, sliderule.Status) {
|
||||
|
@ -103,7 +131,7 @@ func gopherURL(u *url.URL) (string, sliderule.Status) {
|
|||
return clone.String(), sliderule.Status(itemType)
|
||||
}
|
||||
|
||||
func Reload(state *BrowserState, conf *Config) error {
|
||||
func Reload(state *BrowserState) error {
|
||||
if state.Url == nil {
|
||||
return ErrMustBeOnAPage
|
||||
}
|
||||
|
@ -144,9 +172,9 @@ func Reload(state *BrowserState, conf *Config) error {
|
|||
}
|
||||
}
|
||||
|
||||
if state.Url.Scheme == "gemini" {
|
||||
outer:
|
||||
for {
|
||||
if state.Url.Scheme == "gemini" {
|
||||
for {
|
||||
switch response.Status {
|
||||
case gemini.StatusInput:
|
||||
state.Readline.SetPrompt(response.Meta.(string) + " ")
|
||||
|
@ -178,23 +206,33 @@ outer:
|
|||
default:
|
||||
return fmt.Errorf("gemini response %s: %s", gemini.StatusName(response.Status), response.Meta.(string))
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
state.DocType = docType(state.Url, response)
|
||||
state.Url = returnedURL(state.Url, response)
|
||||
state.Body, err = io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
state.Formatted, state.Links, err = parseDoc(state.DocType, state.Body, conf)
|
||||
state.Formatted, state.Links, err = parseDoc(state.DocType, state.Body, state.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return HandleResource(state, conf)
|
||||
return HandleResource(state)
|
||||
}
|
||||
|
||||
func returnedURL(requested *url.URL, response *sliderule.Response) *url.URL {
|
||||
_, gopherType := gopherURL(requested)
|
||||
if gopherType == 0 {
|
||||
return response.Request.URL
|
||||
}
|
||||
|
||||
u := *response.Request.URL
|
||||
u.Path = "/" + string([]byte{byte(gopherType)}) + u.Path
|
||||
return &u
|
||||
}
|
||||
|
||||
func requestCtx(timeout time.Duration) (context.Context, context.CancelFunc) {
|
||||
|
@ -206,7 +244,7 @@ func requestCtx(timeout time.Duration) (context.Context, context.CancelFunc) {
|
|||
}
|
||||
|
||||
func fetch(state *BrowserState, u string, tlsConf *tls.Config) (*sliderule.Response, error) {
|
||||
ctx, cancel := requestCtx(state.Timeout)
|
||||
ctx, cancel := requestCtx(state.Timeout.Duration)
|
||||
defer cancel()
|
||||
|
||||
tlsConf.ClientSessionCache = nil
|
||||
|
@ -229,7 +267,7 @@ func fetch(state *BrowserState, u string, tlsConf *tls.Config) (*sliderule.Respo
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancel = requestCtx(state.Timeout)
|
||||
ctx, cancel = requestCtx(state.Timeout.Duration)
|
||||
defer cancel()
|
||||
return sliderule.NewClient(tlsConf).Fetch(ctx, u)
|
||||
} else if err != nil {
|
||||
|
@ -239,7 +277,7 @@ func fetch(state *BrowserState, u string, tlsConf *tls.Config) (*sliderule.Respo
|
|||
}
|
||||
|
||||
func upload(state *BrowserState, u string, body io.Reader, tlsConf *tls.Config) (*sliderule.Response, error) {
|
||||
ctx, cancel := requestCtx(state.Timeout)
|
||||
ctx, cancel := requestCtx(state.Timeout.Duration)
|
||||
defer cancel()
|
||||
|
||||
tlsConf.ClientSessionCache = nil
|
||||
|
@ -261,7 +299,7 @@ func upload(state *BrowserState, u string, body io.Reader, tlsConf *tls.Config)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancel = requestCtx(state.Timeout)
|
||||
ctx, cancel = requestCtx(state.Timeout.Duration)
|
||||
defer cancel()
|
||||
return sliderule.NewClient(tlsConf).Upload(ctx, u, body)
|
||||
} else if err != nil {
|
||||
|
@ -327,21 +365,25 @@ func back(state *BrowserState) error {
|
|||
if state.Back == nil {
|
||||
return ErrNoPreviousHistory
|
||||
}
|
||||
state.History = state.Back
|
||||
state.Modal = nil
|
||||
state.History = state.Back
|
||||
|
||||
if state.Body == nil {
|
||||
return Reload(state)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Back(state *BrowserState, conf *Config, num int) error {
|
||||
func Back(state *BrowserState, num int) error {
|
||||
for i := 0; i < num; i += 1 {
|
||||
if err := back(state); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return HandleResource(state, conf)
|
||||
return HandleResource(state)
|
||||
}
|
||||
|
||||
func Forward(state *BrowserState, conf *Config, num int) error {
|
||||
func Forward(state *BrowserState, num int) error {
|
||||
for i := 0; i < num; i += 1 {
|
||||
if state.Forward == nil {
|
||||
return ErrNoNextHistory
|
||||
|
@ -350,10 +392,10 @@ func Forward(state *BrowserState, conf *Config, num int) error {
|
|||
}
|
||||
state.Modal = nil
|
||||
|
||||
return HandleResource(state, conf)
|
||||
return HandleResource(state)
|
||||
}
|
||||
|
||||
func Next(state *BrowserState, conf *Config) error {
|
||||
func Next(state *BrowserState) error {
|
||||
switch state.NavIndex {
|
||||
case -1:
|
||||
return ErrCantMoveRelative
|
||||
|
@ -369,10 +411,10 @@ func Next(state *BrowserState, conf *Config) error {
|
|||
|
||||
u := state.Url.ResolveReference(state.Links[index].Target)
|
||||
|
||||
return Navigate(state, u, index, conf)
|
||||
return Navigate(state, u, index)
|
||||
}
|
||||
|
||||
func Previous(state *BrowserState, conf *Config) error {
|
||||
func Previous(state *BrowserState) error {
|
||||
switch state.NavIndex {
|
||||
case -1:
|
||||
return ErrCantMoveRelative
|
||||
|
@ -388,10 +430,10 @@ func Previous(state *BrowserState, conf *Config) error {
|
|||
|
||||
u := state.Url.ResolveReference(state.Links[index].Target)
|
||||
|
||||
return Navigate(state, u, index, conf)
|
||||
return Navigate(state, u, index)
|
||||
}
|
||||
|
||||
func Root(state *BrowserState, tilde bool, conf *Config) error {
|
||||
func Root(state *BrowserState, tilde bool) error {
|
||||
if state.Url == nil {
|
||||
return ErrMustBeOnAPage
|
||||
}
|
||||
|
@ -411,10 +453,10 @@ func Root(state *BrowserState, tilde bool, conf *Config) error {
|
|||
u.Path = base
|
||||
}
|
||||
|
||||
return Navigate(state, &u, -1, conf)
|
||||
return Navigate(state, &u, -1)
|
||||
}
|
||||
|
||||
func Up(state *BrowserState, conf *Config) error {
|
||||
func Up(state *BrowserState) error {
|
||||
if state.Url == nil {
|
||||
return ErrMustBeOnAPage
|
||||
}
|
||||
|
@ -429,16 +471,16 @@ func Up(state *BrowserState, conf *Config) error {
|
|||
u.RawQuery = ""
|
||||
u.Fragment = ""
|
||||
|
||||
return Navigate(state, &u, -1, conf)
|
||||
return Navigate(state, &u, -1)
|
||||
}
|
||||
|
||||
func Go(state *BrowserState, dest string, conf *Config) error {
|
||||
u, idx, err := parseURL(dest, state, conf.DefaultScheme)
|
||||
func Go(state *BrowserState, dest string) error {
|
||||
u, idx, err := parseURL(dest, state, state.DefaultScheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Navigate(state, u, idx, conf)
|
||||
return Navigate(state, u, idx)
|
||||
}
|
||||
|
||||
func parseURL(str string, state *BrowserState, defaultScheme string) (*url.URL, int, error) {
|
||||
|
@ -458,7 +500,7 @@ func parseURL(str string, state *BrowserState, defaultScheme string) (*url.URL,
|
|||
} else if strings.HasPrefix(str, "t:") {
|
||||
i, err := strconv.Atoi(str[2:])
|
||||
if err != nil {
|
||||
return nil, -1, ErrInvalidLink
|
||||
return nil, -1, ErrInvalidLink(str)
|
||||
}
|
||||
|
||||
if i < 0 || i >= len(state.CurrentTour.Links) {
|
||||
|
@ -468,10 +510,10 @@ func parseURL(str string, state *BrowserState, defaultScheme string) (*url.URL,
|
|||
} else if strings.HasPrefix(str, "t[") {
|
||||
idx := strings.IndexByte(str, ']')
|
||||
if idx < 0 || idx >= len(str)-2 || str[idx+1] != ':' {
|
||||
return nil, -1, ErrInvalidLink
|
||||
return nil, -1, ErrInvalidLink(str)
|
||||
}
|
||||
if i, err := strconv.Atoi(str[idx+2:]); err != nil {
|
||||
return nil, -1, ErrInvalidLink
|
||||
return nil, -1, ErrInvalidLink(str)
|
||||
} else {
|
||||
_, tour, err := findTour(state, str[2:idx])
|
||||
if err != nil {
|
||||
|
@ -499,7 +541,7 @@ func parseURL(str string, state *BrowserState, defaultScheme string) (*url.URL,
|
|||
i = -1
|
||||
u, err = url.Parse(str)
|
||||
if err != nil {
|
||||
return nil, -1, ErrInvalidLink
|
||||
return nil, -1, ErrInvalidLink(str)
|
||||
}
|
||||
if u.Scheme == "" {
|
||||
u.Scheme = defaultScheme
|
||||
|
@ -510,7 +552,7 @@ func parseURL(str string, state *BrowserState, defaultScheme string) (*url.URL,
|
|||
}
|
||||
|
||||
if u.Hostname() == "" {
|
||||
return nil, -1, ErrInvalidLink
|
||||
return nil, -1, ErrInvalidLink(u.String())
|
||||
}
|
||||
|
||||
return u, i, nil
|
||||
|
@ -563,12 +605,12 @@ func Print(state *BrowserState) error {
|
|||
return print(state)
|
||||
}
|
||||
|
||||
func HandleResource(state *BrowserState, conf *Config) error {
|
||||
func HandleResource(state *BrowserState) error {
|
||||
if state.Modal != nil {
|
||||
return Print(state)
|
||||
}
|
||||
|
||||
if handler, ok := conf.Handlers[state.DocType]; ok {
|
||||
if handler, ok := state.Handlers[state.DocType]; ok {
|
||||
return Pipe(state, handler)
|
||||
}
|
||||
|
||||
|
@ -577,10 +619,10 @@ func HandleResource(state *BrowserState, conf *Config) error {
|
|||
return print(state)
|
||||
}
|
||||
|
||||
return Save(state, path.Base(state.Url.Path), conf)
|
||||
return Save(state, path.Base(state.Url.Path))
|
||||
}
|
||||
|
||||
func Outline(state *BrowserState, conf *Config) error {
|
||||
func Outline(state *BrowserState) error {
|
||||
if state.Body == nil {
|
||||
return ErrMustBeOnAPage
|
||||
}
|
||||
|
@ -604,7 +646,7 @@ func Outline(state *BrowserState, conf *Config) error {
|
|||
}
|
||||
}
|
||||
|
||||
formatted, _, err := parseGemtextDoc(b.Bytes(), conf.SoftWrap)
|
||||
formatted, _, err := parseGemtextDoc(b.Bytes(), state.SoftWrap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -617,7 +659,7 @@ func Outline(state *BrowserState, conf *Config) error {
|
|||
return Print(state)
|
||||
}
|
||||
|
||||
func Links(state *BrowserState, conf *Config) error {
|
||||
func Links(state *BrowserState) error {
|
||||
if state.Links == nil {
|
||||
return ErrMustBeOnAPage
|
||||
}
|
||||
|
@ -626,7 +668,7 @@ func Links(state *BrowserState, conf *Config) error {
|
|||
for _, link := range state.Links {
|
||||
fmt.Fprintf(buf, "=> %s %s\n", link.Target.String(), link.Text)
|
||||
}
|
||||
formatted, _, err := parseDoc("text/gemini", buf.Bytes(), conf)
|
||||
formatted, _, err := parseDoc("text/gemini", buf.Bytes(), state.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -666,7 +708,7 @@ func HistoryCmd(state *BrowserState) error {
|
|||
return Print(state)
|
||||
}
|
||||
|
||||
func Save(state *BrowserState, filename string, conf *Config) error {
|
||||
func Save(state *BrowserState, filename string) error {
|
||||
if state.Body == nil {
|
||||
return ErrMustBeOnAPage
|
||||
}
|
||||
|
@ -674,7 +716,7 @@ func Save(state *BrowserState, filename string, conf *Config) error {
|
|||
return ErrSaveNeedsFilename
|
||||
}
|
||||
|
||||
p := filepath.Join(conf.DownloadFolder, filename)
|
||||
p := filepath.Join(state.DownloadFolder, filename)
|
||||
_, err := os.Stat(p)
|
||||
pbase := p
|
||||
i := 1
|
||||
|
@ -691,12 +733,12 @@ func Save(state *BrowserState, filename string, conf *Config) error {
|
|||
return Print(state)
|
||||
}
|
||||
|
||||
func Mark(state *BrowserState, args []string, conf *Config) error {
|
||||
func Mark(state *BrowserState, args []string) error {
|
||||
switch args[0] {
|
||||
case "add":
|
||||
return MarkAdd(state, conf, args[1], args[2])
|
||||
return MarkAdd(state, args[1], args[2])
|
||||
case "go":
|
||||
return MarkGo(state, conf, args[1])
|
||||
return MarkGo(state, args[1])
|
||||
case "list":
|
||||
return MarkList(state)
|
||||
case "delete":
|
||||
|
@ -706,27 +748,27 @@ func Mark(state *BrowserState, args []string, conf *Config) error {
|
|||
return ErrInvalidMarkArgs
|
||||
}
|
||||
|
||||
func TourCmd(state *BrowserState, args []string, conf *Config) error {
|
||||
func TourCmd(state *BrowserState, args []string) error {
|
||||
switch args[0] {
|
||||
case "add":
|
||||
if args[1] == "next" {
|
||||
return TourAddNext(state, conf, args[2:])
|
||||
return TourAddNext(state, args[2:])
|
||||
}
|
||||
return TourAdd(state, conf, args[1:])
|
||||
return TourAdd(state, args[1:])
|
||||
case "show":
|
||||
return TourShow(state)
|
||||
case "select":
|
||||
return TourSelect(state, args[1])
|
||||
case "next":
|
||||
return TourNext(state, conf)
|
||||
return TourNext(state)
|
||||
case "previous":
|
||||
return TourPrevious(state, conf)
|
||||
return TourPrevious(state)
|
||||
case "clear":
|
||||
return TourClear(state)
|
||||
case "list":
|
||||
return TourList(state)
|
||||
case "go":
|
||||
return TourGo(state, conf, args[1])
|
||||
return TourGo(state, args[1])
|
||||
}
|
||||
|
||||
return ErrInvalidTourArgs
|
||||
|
|
30
command.go
30
command.go
|
@ -368,54 +368,54 @@ func parseIdentityArgs(line string) ([]string, error) {
|
|||
return nil, ErrInvalidArgs
|
||||
}
|
||||
|
||||
func RunCommand(conf *Config, cmd *Command, state *BrowserState) error {
|
||||
func RunCommand(cmd *Command, state *BrowserState) error {
|
||||
switch cmd.Name {
|
||||
case "about":
|
||||
return About(state)
|
||||
case "root":
|
||||
return Root(state, true, conf)
|
||||
return Root(state, true)
|
||||
case "Root":
|
||||
return Root(state, false, conf)
|
||||
return Root(state, false)
|
||||
case "reload":
|
||||
return Reload(state, conf)
|
||||
return Reload(state)
|
||||
case "back":
|
||||
num := 1
|
||||
if len(cmd.Args) == 1 {
|
||||
num, _ = strconv.Atoi(cmd.Args[0])
|
||||
}
|
||||
return Back(state, conf, num)
|
||||
return Back(state, num)
|
||||
case "forward":
|
||||
num := 1
|
||||
if len(cmd.Args) == 1 {
|
||||
num, _ = strconv.Atoi(cmd.Args[0])
|
||||
}
|
||||
return Forward(state, conf, num)
|
||||
return Forward(state, num)
|
||||
case "next":
|
||||
return Next(state, conf)
|
||||
return Next(state)
|
||||
case "previous":
|
||||
return Previous(state, conf)
|
||||
return Previous(state)
|
||||
case "up":
|
||||
return Up(state, conf)
|
||||
return Up(state)
|
||||
case "go":
|
||||
return Go(state, cmd.Args[0], conf)
|
||||
return Go(state, cmd.Args[0])
|
||||
case "help":
|
||||
return Help(state, cmd.Args[0])
|
||||
case "outline":
|
||||
return Outline(state, conf)
|
||||
return Outline(state)
|
||||
case "pipe":
|
||||
return Pipe(state, cmd.Args[0])
|
||||
case "print":
|
||||
return Print(state)
|
||||
case "links":
|
||||
return Links(state, conf)
|
||||
return Links(state)
|
||||
case "history":
|
||||
return HistoryCmd(state)
|
||||
case "save":
|
||||
return Save(state, cmd.Args[0], conf)
|
||||
return Save(state, cmd.Args[0])
|
||||
case "mark":
|
||||
return Mark(state, cmd.Args, conf)
|
||||
return Mark(state, cmd.Args)
|
||||
case "tour":
|
||||
return TourCmd(state, cmd.Args, conf)
|
||||
return TourCmd(state, cmd.Args)
|
||||
case "identity":
|
||||
return IdentityCmd(state, cmd.Args)
|
||||
case "quit":
|
||||
|
|
16
files.go
16
files.go
|
@ -17,13 +17,14 @@ import (
|
|||
)
|
||||
|
||||
type ConfigMain struct {
|
||||
DefaultScheme string `toml:"default_scheme"`
|
||||
SoftWrap int `toml:"soft_wrap"`
|
||||
DownloadFolder string `toml:"download_folder"`
|
||||
VimKeys bool `toml:"vim_keys"`
|
||||
Quiet bool `toml:"quiet"`
|
||||
Pager string `toml:"pager"`
|
||||
Timeout duration `toml:"duration"`
|
||||
DefaultScheme string `toml:"default_scheme"`
|
||||
SoftWrap int `toml:"soft_wrap"`
|
||||
DownloadFolder string `toml:"download_folder"`
|
||||
VimKeys bool `toml:"vim_keys"`
|
||||
Quiet bool `toml:"quiet"`
|
||||
Pager string `toml:"pager"`
|
||||
Timeout duration `toml:"duration"`
|
||||
SavedHistoryDepth int `toml:"saved_history_depth"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
|
@ -63,6 +64,7 @@ func getConfig() (*Config, error) {
|
|||
Timeout: duration{
|
||||
time.Duration(10 * time.Second),
|
||||
},
|
||||
SavedHistoryDepth: 30,
|
||||
},
|
||||
Handlers: map[string]string{},
|
||||
}
|
||||
|
|
17
go.mod
17
go.mod
|
@ -9,7 +9,22 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/bubbletea v0.25.0 // indirect
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/term v0.6.0 // indirect
|
||||
golang.org/x/text v0.3.8 // indirect
|
||||
)
|
||||
|
|
36
go.sum
36
go.sum
|
@ -1,23 +1,59 @@
|
|||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
||||
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
|
||||
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
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/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
tildegit.org/tjp/sliderule v1.6.2-0.20240115025310-751f423f11bf h1:p0MqM4m/LcgLjRH24OOV+oNOu/8+alABAdI6kLantvE=
|
||||
|
|
15
handlers.go
15
handlers.go
|
@ -137,10 +137,10 @@ const (
|
|||
promptStyle = "\x1b[38;5;39m"
|
||||
quoteStyle = "\x1b[38;5;208m\x1b[3m"
|
||||
rawStyle = "\x1b[38;5;249m"
|
||||
h1Style = "\x1b[38;5;154m\x1b[1m\x1b[4m"
|
||||
h2Style = "\x1b[38;5;50m\x1b[4m"
|
||||
h3Style = "\x1b[38;5;6m\x1b[4m"
|
||||
listStyle = "\x1b[38;5;3m"
|
||||
h1Style = "\x1b[38;5;154m\x1b[1m\x1b[4m"
|
||||
h2Style = "\x1b[38;5;50m\x1b[4m"
|
||||
h3Style = "\x1b[38;5;6m\x1b[4m"
|
||||
listStyle = "\x1b[38;5;3m"
|
||||
)
|
||||
|
||||
func parseGemtextDoc(body []byte, softWrap int) (string, []Link, error) {
|
||||
|
@ -189,7 +189,7 @@ func parseGemtextDoc(body []byte, softWrap int) (string, []Link, error) {
|
|||
i += 1
|
||||
case gemtext.LineTypeQuote:
|
||||
q := item.(gemtext.QuoteLine)
|
||||
for _, line := range fold(q.Body(), softWrap - 1) {
|
||||
for _, line := range fold(q.Body(), softWrap-1) {
|
||||
line = strings.TrimSpace(line)
|
||||
if _, err := b.WriteString(textpad + "> " + quoteStyle + line + ansiClear + "\n"); err != nil {
|
||||
return "", nil, err
|
||||
|
@ -219,15 +219,16 @@ func parseGemtextDoc(body []byte, softWrap int) (string, []Link, error) {
|
|||
case 3:
|
||||
color = h3Style
|
||||
}
|
||||
|
||||
|
||||
for _, line := range fold(item.String(), softWrap) {
|
||||
line = strings.TrimRight(line, "\r\n")
|
||||
if _, err := b.WriteString(textpad + color + line + ansiClear + "\n"); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
case gemtext.LineTypeListItem:
|
||||
li := item.(gemtext.ListItemLine)
|
||||
for i, line := range fold(li.Body(), softWrap - 2) {
|
||||
for i, line := range fold(li.Body(), softWrap-2) {
|
||||
lpad := " "
|
||||
if i == 0 {
|
||||
lpad = "* "
|
||||
|
|
12
identity.go
12
identity.go
|
@ -156,10 +156,10 @@ func IdentityUseDomain(state *BrowserState, name string, domain string) error {
|
|||
ident := state.Identities.ByName[name]
|
||||
|
||||
u, _, err := parseURL(domain, state, "gemini")
|
||||
if errors.Is(err, ErrInvalidLink) {
|
||||
if errors.Is(err, invalidLinkErr("")) {
|
||||
u, err = url.Parse(domain)
|
||||
if err != nil {
|
||||
return ErrInvalidLink
|
||||
return ErrInvalidLink(domain)
|
||||
}
|
||||
if u.Hostname() == "" {
|
||||
u.Host = domain
|
||||
|
@ -185,10 +185,10 @@ func IdentityUseFolder(state *BrowserState, name string, domain string) error {
|
|||
ident := state.Identities.ByName[name]
|
||||
|
||||
u, _, err := parseURL(domain, state, "gemini")
|
||||
if errors.Is(err, ErrInvalidLink) {
|
||||
if errors.Is(err, invalidLinkErr("")) {
|
||||
u, err = url.Parse(domain)
|
||||
if err != nil {
|
||||
return ErrInvalidLink
|
||||
return ErrInvalidLink(domain)
|
||||
}
|
||||
if u.Hostname() == "" {
|
||||
u.Host = domain
|
||||
|
@ -215,10 +215,10 @@ func IdentityUsePage(state *BrowserState, name string, domain string) error {
|
|||
ident := state.Identities.ByName[name]
|
||||
|
||||
u, _, err := parseURL(domain, state, "gemini")
|
||||
if errors.Is(err, ErrInvalidLink) {
|
||||
if errors.Is(err, invalidLinkErr("")) {
|
||||
u, err = url.Parse(domain)
|
||||
if err != nil {
|
||||
return ErrInvalidLink
|
||||
return ErrInvalidLink(domain)
|
||||
}
|
||||
if u.Hostname() == "" {
|
||||
u.Host = domain
|
||||
|
|
108
main.go
108
main.go
|
@ -11,40 +11,18 @@ import (
|
|||
"github.com/chzyer/readline"
|
||||
)
|
||||
|
||||
var cmdMode = flag.String("c", "", "")
|
||||
var helpMode = flag.Bool("h", false, "")
|
||||
var quietMode = flag.Bool("q", false, "")
|
||||
var (
|
||||
cmdMode = flag.String("c", "", "")
|
||||
helpMode = flag.Bool("h", false, "")
|
||||
quietMode = flag.Bool("q", false, "")
|
||||
)
|
||||
|
||||
func main() {
|
||||
conf, err := getConfig()
|
||||
state, err := buildInitialState()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := getTofuStore(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
state := NewBrowserState(conf)
|
||||
|
||||
marks, err := getMarks()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
state.Marks = marks
|
||||
|
||||
tours, err := getTours()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
state.NamedTours = tours
|
||||
|
||||
idents, err := getIdentities()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
state.Identities = idents
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *helpMode {
|
||||
|
@ -55,9 +33,8 @@ func main() {
|
|||
}
|
||||
|
||||
if *cmdMode != "" {
|
||||
conf.Quiet = true
|
||||
state.Quiet = true
|
||||
if err := handleCmdLine(state, conf, *cmdMode); err != nil {
|
||||
if err := handleCmdLine(state, *cmdMode); err != nil {
|
||||
writeError(err.Error())
|
||||
}
|
||||
return
|
||||
|
@ -67,25 +44,72 @@ func main() {
|
|||
state.Quiet = true
|
||||
}
|
||||
|
||||
rl, err := readline.New(Prompt)
|
||||
if urls := flag.Args(); len(urls) > 0 {
|
||||
if err := Go(state, urls[0]); err != nil {
|
||||
writeError(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// runInteractivePrompt(state)
|
||||
runTUI(state)
|
||||
}
|
||||
|
||||
func buildReadline(prompt string, conf *Config) (*readline.Instance, error) {
|
||||
rl, err := readline.New(prompt)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if conf.VimKeys {
|
||||
rl.SetVimMode(true)
|
||||
}
|
||||
state.Readline = rl
|
||||
|
||||
if urls := flag.Args(); len(urls) > 0 {
|
||||
if err := Go(state, urls[0], conf); err != nil {
|
||||
writeError(err.Error())
|
||||
}
|
||||
return rl, nil
|
||||
}
|
||||
|
||||
func buildInitialState() (*BrowserState, error) {
|
||||
conf, err := getConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := getTofuStore(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
state := NewBrowserState(conf)
|
||||
|
||||
marks, err := getMarks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state.Marks = marks
|
||||
|
||||
tours, err := getTours()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state.NamedTours = tours
|
||||
|
||||
idents, err := getIdentities()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state.Identities = idents
|
||||
|
||||
rl, err := buildReadline(Prompt, conf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
state.Readline = rl
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func runInteractivePrompt(state *BrowserState) {
|
||||
for {
|
||||
rl.SetPrompt(Prompt)
|
||||
line, err := rl.Readline()
|
||||
state.Readline.SetPrompt(Prompt)
|
||||
line, err := state.Readline.Readline()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
@ -93,17 +117,17 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := handleCmdLine(state, conf, line); err != nil {
|
||||
if err := handleCmdLine(state, line); err != nil {
|
||||
writeError(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleCmdLine(state *BrowserState, conf *Config, line string) error {
|
||||
func handleCmdLine(state *BrowserState, line string) error {
|
||||
for _, cmd := range strings.Split(line, ";") {
|
||||
if c, err := ParseCommand(strings.TrimSpace(cmd)); err != nil {
|
||||
return err
|
||||
} else if err := RunCommand(conf, c, state); err != nil {
|
||||
} else if err := RunCommand(c, state); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
8
mark.go
8
mark.go
|
@ -12,8 +12,8 @@ var (
|
|||
ErrNotAMark = errors.New("that's not a known mark name")
|
||||
)
|
||||
|
||||
func MarkAdd(state *BrowserState, conf *Config, name, target string) error {
|
||||
u, _, err := parseURL(target, state, conf.DefaultScheme)
|
||||
func MarkAdd(state *BrowserState, name, target string) error {
|
||||
u, _, err := parseURL(target, state, state.DefaultScheme)
|
||||
if err != nil {
|
||||
return ErrInvalidURL
|
||||
}
|
||||
|
@ -27,13 +27,13 @@ func MarkAdd(state *BrowserState, conf *Config, name, target string) error {
|
|||
return Print(state)
|
||||
}
|
||||
|
||||
func MarkGo(state *BrowserState, conf *Config, name string) error {
|
||||
func MarkGo(state *BrowserState, name string) error {
|
||||
_, target, err := findMark(state, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Go(state, target, conf)
|
||||
return Go(state, target)
|
||||
}
|
||||
|
||||
func MarkList(state *BrowserState) error {
|
||||
|
|
11
state.go
11
state.go
|
@ -2,13 +2,13 @@ package main
|
|||
|
||||
import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/chzyer/readline"
|
||||
)
|
||||
|
||||
type BrowserState struct {
|
||||
*History
|
||||
*Config
|
||||
|
||||
Modal []byte
|
||||
|
||||
|
@ -20,10 +20,6 @@ type BrowserState struct {
|
|||
DefaultTour Tour
|
||||
CurrentTour *Tour
|
||||
|
||||
Quiet bool
|
||||
Pager string
|
||||
Timeout time.Duration
|
||||
|
||||
Readline *readline.Instance
|
||||
}
|
||||
|
||||
|
@ -59,10 +55,7 @@ func NewBrowserState(conf *Config) *BrowserState {
|
|||
Depth: 0,
|
||||
NavIndex: -1,
|
||||
},
|
||||
|
||||
Quiet: conf.Quiet,
|
||||
Pager: conf.Pager,
|
||||
Timeout: conf.Timeout.Duration,
|
||||
Config: conf,
|
||||
}
|
||||
state.CurrentTour = &state.DefaultTour
|
||||
return state
|
||||
|
|
20
tour.go
20
tour.go
|
@ -49,10 +49,10 @@ func parseURLs(state *BrowserState, defaultScheme, str string) ([]*url.URL, erro
|
|||
return []*url.URL{u}, nil
|
||||
}
|
||||
|
||||
func TourAdd(state *BrowserState, conf *Config, targets []string) error {
|
||||
func TourAdd(state *BrowserState, targets []string) error {
|
||||
newurls := []*url.URL{}
|
||||
for _, target := range targets {
|
||||
urls, err := parseURLs(state, conf.DefaultScheme, target)
|
||||
urls, err := parseURLs(state, state.DefaultScheme, target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -69,10 +69,10 @@ func TourAdd(state *BrowserState, conf *Config, targets []string) error {
|
|||
return Print(state)
|
||||
}
|
||||
|
||||
func TourAddNext(state *BrowserState, conf *Config, targets []string) error {
|
||||
func TourAddNext(state *BrowserState, targets []string) error {
|
||||
newurls := []*url.URL{}
|
||||
for _, target := range targets {
|
||||
urls, err := parseURLs(state, conf.DefaultScheme, target)
|
||||
urls, err := parseURLs(state, state.DefaultScheme, target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ func TourShow(state *BrowserState) error {
|
|||
return Print(state)
|
||||
}
|
||||
|
||||
func TourNext(state *BrowserState, conf *Config) error {
|
||||
func TourNext(state *BrowserState) error {
|
||||
tour := state.CurrentTour
|
||||
if tour.Index >= len(tour.Links) || len(tour.Links) == 0 {
|
||||
return ErrEndOfTour
|
||||
|
@ -121,10 +121,10 @@ func TourNext(state *BrowserState, conf *Config) error {
|
|||
page := tour.Links[tour.Index]
|
||||
tour.Index += 1
|
||||
|
||||
return Navigate(state, page, -1, conf)
|
||||
return Navigate(state, page, -1)
|
||||
}
|
||||
|
||||
func TourPrevious(state *BrowserState, conf *Config) error {
|
||||
func TourPrevious(state *BrowserState) error {
|
||||
tour := state.CurrentTour
|
||||
if tour.Index <= 0 {
|
||||
return ErrStartOfTour
|
||||
|
@ -135,7 +135,7 @@ func TourPrevious(state *BrowserState, conf *Config) error {
|
|||
}
|
||||
page := tour.Links[tour.Index-1]
|
||||
|
||||
return Navigate(state, page, -1, conf)
|
||||
return Navigate(state, page, -1)
|
||||
}
|
||||
|
||||
func TourClear(state *BrowserState) error {
|
||||
|
@ -173,7 +173,7 @@ func TourList(state *BrowserState) error {
|
|||
return Print(state)
|
||||
}
|
||||
|
||||
func TourGo(state *BrowserState, conf *Config, pos string) error {
|
||||
func TourGo(state *BrowserState, pos string) error {
|
||||
tour := state.CurrentTour
|
||||
|
||||
i, _ := strconv.Atoi(pos)
|
||||
|
@ -182,7 +182,7 @@ func TourGo(state *BrowserState, conf *Config, pos string) error {
|
|||
}
|
||||
|
||||
tour.Index = i + 1
|
||||
return Navigate(state, tour.Links[i], -1, conf)
|
||||
return Navigate(state, tour.Links[i], -1)
|
||||
}
|
||||
|
||||
func TourSelect(state *BrowserState, name string) error {
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
type TUIModel struct {
|
||||
State *BrowserState
|
||||
}
|
||||
|
||||
func NewTUIModel(state *BrowserState) TUIModel {
|
||||
return TUIModel{State: state}
|
||||
}
|
||||
|
||||
func (model TUIModel) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (model TUIModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "ctrl+d", "q":
|
||||
return model, tea.Quit
|
||||
}
|
||||
}
|
||||
|
||||
return model, nil
|
||||
}
|
||||
|
||||
func (model TUIModel) View() string {
|
||||
return "pardon our dust"
|
||||
}
|
||||
|
||||
func runTUI(state *BrowserState) {
|
||||
p := tea.NewProgram(NewTUIModel(state))
|
||||
if _, err := p.Run(); err != nil {
|
||||
writeError(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue