package main import ( "errors" "io" "net/url" "os" "strconv" "strings" "git.sr.ht/~adnano/go-gemini" ) type Command func(b *Browser, args ...string) error var commands = map[string]Command{ "prompt": func(b *Browser, args ...string) error { b.CommandMode() prompt := strings.Join(args, " ") b.input.SetInput(prompt) return nil }, "follow": func(b *Browser, args ...string) error { return b.FollowMode() }, "open": func(b *Browser, args ...string) error { if len(args) == 0 { return errors.New("usage: open ") } rawurl := args[0] u, err := url.Parse(rawurl) if err != nil { return err } if len(u.Scheme) == 0 && len(u.Host) == 0 { u2, err := url.Parse("gemini://" + rawurl) if err == nil { u = u2 } } b.tabs[b.tab].DoBackground(&gemini.Request{URL: u}) return nil }, "close": func(b *Browser, args ...string) error { b.CloseTab(b.tab) return nil }, "newtab": func(b *Browser, args ...string) error { tab := b.NewTab() b.tab++ b.InsertTab(b.tab, tab) tab.Do(&gemini.Request{ URL: &url.URL{ Scheme: "about", Host: "newtab", }, }) b.makeTabVisible() return nil }, "clone": func(b *Browser, args ...string) error { t := b.tabs[b.tab] clone := t.Clone() b.tab++ b.InsertTab(b.tab, clone) clone.Reload() b.makeTabVisible() return nil }, "reload": func(b *Browser, args ...string) error { b.tabs[b.tab].Reload() return nil }, "cancel": func(b *Browser, args ...string) error { b.tabs[b.tab].Cancel() return nil }, "back": func(b *Browser, args ...string) error { tab := b.tabs[b.tab] if !tab.Back() { b.Message("Already at beginning of history") } return nil }, "forward": func(b *Browser, args ...string) error { tab := b.tabs[b.tab] if !tab.Forward() { b.Message("Already at end of history") } return nil }, "previous": func(b *Browser, args ...string) error { b.Previous() return nil }, "next": func(b *Browser, args ...string) error { b.Next() return nil }, "quit": func(b *Browser, args ...string) error { b.view.Exit() return nil }, "scroll": func(b *Browser, args ...string) error { const usage = "usage: scroll [%]" if len(args) == 0 { return errors.New(usage) } amount := args[0] switch amount { case "top": b.tabView.Top() b.tabView.Invalidate() case "bottom": b.tabView.Bottom() b.tabView.Invalidate() default: var scroll int if strings.HasSuffix(amount, "%") { percent, err := strconv.Atoi(amount[:len(amount)-1]) if err != nil { return errors.New(usage) } _, h := b.tabView.Size() scroll = h * percent / 100 } else { var err error scroll, err = strconv.Atoi(amount) if err != nil { return errors.New(usage) } } b.tabView.ScrollDown(scroll) b.tabView.Invalidate() } return nil }, "save": func(b *Browser, args ...string) error { if len(args) == 0 { return errors.New("usage: save ") } tab := b.tabs[b.tab] f, err := os.Create(args[0]) if err != nil { return err } defer f.Close() io.Copy(f, &tab.buf) return nil }, "bookmark": func(b *Browser, args ...string) error { tab := b.tabs[b.tab] if b.bookmarks.Add(gemini.LineLink{ URL: tab.URL(), Name: tab.Title(), }) { b.Message("Bookmarked") } else { b.Message("Already bookmarked") } return nil }, "search": func(b *Browser, args ...string) error { if len(args) == 0 { return errors.New("usage: search ") } u := b.config.Search.URL.ResolveReference(&url.URL{ RawQuery: gemini.QueryEscape(strings.Join(args, " ")), }) b.tabs[b.tab].DoBackground(&gemini.Request{URL: u}) return nil }, }