From 59c930f03c383ebb56382cc430a900972eb02613 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 2 Feb 2024 12:17:46 +0100 Subject: [PATCH 01/64] wip: gnobro Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/call.go | 316 +++++++++++++++++ contribs/gnobro/cmd/gnobro/list.go | 146 ++++++++ contribs/gnobro/cmd/gnobro/main.go | 466 ++++++++++++++++++++++++++ contribs/gnobro/cmd/gnobro/request.go | 150 +++++++++ contribs/gnobro/go.mod | 83 +++++ contribs/gnobro/go.sum | 285 ++++++++++++++++ 6 files changed, 1446 insertions(+) create mode 100644 contribs/gnobro/cmd/gnobro/call.go create mode 100644 contribs/gnobro/cmd/gnobro/list.go create mode 100644 contribs/gnobro/cmd/gnobro/main.go create mode 100644 contribs/gnobro/cmd/gnobro/request.go create mode 100644 contribs/gnobro/go.mod create mode 100644 contribs/gnobro/go.sum diff --git a/contribs/gnobro/cmd/gnobro/call.go b/contribs/gnobro/cmd/gnobro/call.go new file mode 100644 index 00000000000..637a05fd0e9 --- /dev/null +++ b/contribs/gnobro/cmd/gnobro/call.go @@ -0,0 +1,316 @@ +package main + +import ( + "fmt" + + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" + ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/errors" + "github.com/gnolang/gno/tm2/pkg/std" +) + +type callCfg struct { + home string + + gasWanted int64 + gasFee string + memo string + kb keys.Keybase + remote string + + broadcast bool + chainID string + + send string + pkgPath string + funcName string + args commands.StringArr +} + +func execCall(nameOrBech32 string, pass string, cfg callCfg) (*ctypes.ResultBroadcastTxCommit, error) { + if cfg.pkgPath == "" { + return nil, errors.New("pkgpath not specified") + } + if cfg.funcName == "" { + return nil, errors.New("func not specified") + } + + if cfg.gasWanted == 0 { + return nil, errors.New("gas-wanted not specified") + } + if cfg.gasFee == "" { + return nil, errors.New("gas-fee not specified") + } + + // read statement. + fnc := cfg.funcName + + // read account pubkey. + info, err := cfg.kb.GetByNameOrAddress(nameOrBech32) + if err != nil { + return nil, err + } + caller := info.GetAddress() + // info.GetPubKey() + + // Parse send amount. + send, err := std.ParseCoins(cfg.send) + if err != nil { + return nil, fmt.Errorf("parsing send coins: %w", err) + } + + // parse gas wanted & fee. + gaswanted := cfg.gasWanted + gasfee, err := std.ParseCoin(cfg.gasFee) + if err != nil { + return nil, fmt.Errorf("parsing gas fee coin: %w", err) + } + + // construct msg & tx and marshal. + msg := vm.MsgCall{ + Caller: caller, + Send: send, + PkgPath: cfg.pkgPath, + Func: fnc, + Args: cfg.args, + } + tx := std.Tx{ + Msgs: []std.Msg{msg}, + Fee: std.NewFee(gaswanted, gasfee), + Signatures: nil, + Memo: cfg.memo, + } + + res, err := signAndBroadcast(cfg.remote, nameOrBech32, cfg.chainID, pass, cfg.args, tx) + if err != nil { + return nil, fmt.Errorf("unable to sign and broadcast: %w", err) + } + + return res, nil +} + +// func loadKeyBaseName(nameOrBech32 string) error { +// home := gnoenv.HomeDir() +// kb, err := keys.NewKeyBaseFromDir(home) +// if err != nil { +// return err +// } +// info, err := kb.GetByNameOrAddress(nameOrBech32) +// if err != nil { +// return err +// } + +// caller := info.GetAddress() +// return nil +// } + +type queryCfg struct { + data string + height int64 + prove bool + + // internal + path string +} + +func queryHandler(remote string, cfg *queryCfg) (*ctypes.ResultABCIQuery, error) { + if remote == "" || remote == "y" { + return nil, errors.New("missing remote url") + } + + data := []byte(cfg.data) + opts2 := client.ABCIQueryOptions{ + // Height: height, XXX + // Prove: false, XXX + } + cli := client.NewHTTP(remote, "/websocket") + qres, err := cli.ABCIQueryWithOptions( + cfg.path, data, opts2) + if err != nil { + return nil, errors.Wrap(err, "querying") + } + + return qres, nil +} + +type signCfg struct { + txPath string + chainID string + accountNumber uint64 + sequence uint64 + showSignBytes bool + home string + + // internal flags, when called programmatically + nameOrBech32 string + txJSON []byte + pass string +} + +func signAndBroadcast(remote, nameOrBech32, chainid, pass string, args []string, tx std.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + home := gnoenv.HomeDir() + + // query account + kb, err := keys.NewKeyBaseFromDir(home) + if err != nil { + return nil, err + } + info, err := kb.GetByNameOrAddress(nameOrBech32) + if err != nil { + return nil, err + } + accountAddr := info.GetAddress() + + qopts := &queryCfg{ + path: fmt.Sprintf("auth/accounts/%s", accountAddr), + } + qres, err := queryHandler(remote, qopts) + if err != nil { + return nil, fmt.Errorf("query account: %w", err) + } + var qret struct{ BaseAccount std.BaseAccount } + err = amino.UnmarshalJSON(qres.Response.Data, &qret) + if err != nil { + return nil, fmt.Errorf("unmarshall query response: %w", err) + } + + // sign tx + accountNumber := qret.BaseAccount.AccountNumber + sequence := qret.BaseAccount.Sequence + sopts := &signCfg{ + home: home, + sequence: sequence, + accountNumber: accountNumber, + chainID: chainid, + nameOrBech32: nameOrBech32, + txJSON: amino.MustMarshalJSON(tx), + } + + signedTx, err := SignHandler(sopts) + if err != nil { + return nil, fmt.Errorf("sign tx: %w", err) + } + + // broadcast signed tx + bopts := &broadcastCfg{ + tx: signedTx, + } + + bres, err := broadcastHandler(remote, bopts) + if err != nil { + return nil, fmt.Errorf("broadcast: %w", err) + } + + if bres.CheckTx.IsErr() { + req := fmt.Sprintf("remote:%s name:%s chainid:%s pass:%s", remote, nameOrBech32, chainid, pass) + return nil, fmt.Errorf("check trasaction: %w \nreq: %s\nLog:%s", bres.CheckTx.Error, req, bres.CheckTx.Log) + } + + if bres.DeliverTx.IsErr() { + return nil, fmt.Errorf("check trasaction: %w \nLog:%s", bres.DeliverTx.Error, bres.DeliverTx.Log) + } + + return bres, nil +} + +func SignHandler(cfg *signCfg) (*std.Tx, error) { + var err error + var tx std.Tx + + if cfg.txJSON == nil { + return nil, errors.New("invalid tx content") + } + + kb, err := keys.NewKeyBaseFromDir(cfg.home) + if err != nil { + return nil, err + } + + err = amino.UnmarshalJSON(cfg.txJSON, &tx) + if err != nil { + return nil, err + } + + // fill tx signatures. + signers := tx.GetSigners() + if tx.Signatures == nil { + for range signers { + tx.Signatures = append(tx.Signatures, std.Signature{ + PubKey: nil, // zero signature + Signature: nil, // zero signature + }) + } + } + + // validate document to sign. + err = tx.ValidateBasic() + if err != nil { + return nil, err + } + + // derive sign doc bytes. + chainID := cfg.chainID + accountNumber := cfg.accountNumber + sequence := cfg.sequence + signbz := tx.GetSignBytes(chainID, accountNumber, sequence) + if cfg.showSignBytes { + return nil, fmt.Errorf("sign bytes: %X\n", signbz) + } + + sig, pub, err := kb.Sign(cfg.nameOrBech32, cfg.pass, signbz) + if err != nil { + return nil, err + } + addr := pub.Address() + found := false + for i := range tx.Signatures { + // override signature for matching slot. + if signers[i] == addr { + found = true + tx.Signatures[i] = std.Signature{ + PubKey: pub, + Signature: sig, + } + } + } + if !found { + return nil, errors.New( + fmt.Sprintf("addr %v (%s) not in signer set", addr, cfg.nameOrBech32), + ) + } + + return &tx, nil +} + +type broadcastCfg struct { + // internal + tx *std.Tx +} + +func broadcastHandler(remote string, cfg *broadcastCfg) (*ctypes.ResultBroadcastTxCommit, error) { + if cfg.tx == nil { + return nil, errors.New("invalid tx") + } + + if remote == "" || remote == "y" { + return nil, errors.New("missing remote url") + } + + bz, err := amino.Marshal(cfg.tx) + if err != nil { + return nil, errors.Wrap(err, "remarshaling tx binary bytes") + } + + cli := client.NewHTTP(remote, "/websocket") + bres, err := cli.BroadcastTxCommit(bz) + if err != nil { + return nil, errors.Wrap(err, "broadcasting bytes") + } + + return bres, nil +} diff --git a/contribs/gnobro/cmd/gnobro/list.go b/contribs/gnobro/cmd/gnobro/list.go new file mode 100644 index 00000000000..ef521c416f2 --- /dev/null +++ b/contribs/gnobro/cmd/gnobro/list.go @@ -0,0 +1,146 @@ +package main + +import ( + "fmt" + "io" + "sort" + "strings" + + "github.com/charmbracelet/bubbles/list" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + "github.com/sahilm/fuzzy" +) + +var ( + listTitleStyle = lipgloss.NewStyle().MarginLeft(2) + listItemStyle = lipgloss.NewStyle().PaddingLeft(4) + listSelectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170")) + listPaginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4) + listHelpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1) + listQuitTextStyle = lipgloss.NewStyle().Margin(1, 0, 2, 4) +) + +type FuncListModel struct { + list.Model + items []list.Item +} + +func (m *FuncListModel) SetItems(items []list.Item) { + m.Model.SetItems(items) + m.items = items +} + +func (m *FuncListModel) OriginItems() []list.Item { + return m.items +} + +func (m *FuncListModel) Erase() { + m.Model.SetItems([]list.Item{}) +} + +func (m *FuncListModel) Reset() { + m.Model.SetItems(m.items) +} + +func (m *FuncListModel) FilterItems(pattern string) { + i := strings.IndexRune(pattern, '(') + if i > 0 { + pattern = pattern[:i] + } + + data := make([]string, len(m.items)) + for i, item := range m.items { + data[i] = item.FilterValue() + } + + var ranks = fuzzy.Find(pattern, data) + sort.Stable(ranks) + if len(ranks) > 0 && i > 0 { + m.Model.SetItems([]list.Item{m.items[ranks[0].Index]}) + return + } + + items := make([]list.Item, len(ranks)) + for i, r := range ranks { + items[i] = m.items[r.Index] + } + + m.Model.SetItems(items) +} + +type itemFunc vm.FunctionSignature + +func (i itemFunc) Name() string { return i.FuncName } +func (i itemFunc) Title() string { return i.Name() } +func (i itemFunc) Description() string { return i.FuncName } +func (i itemFunc) FilterValue() string { return i.FuncName } + +type itemFuncsDelegate struct{} + +func (d itemFuncsDelegate) Height() int { return 1 } +func (d itemFuncsDelegate) Spacing() int { return 0 } +func (d itemFuncsDelegate) Update(_ tea.Msg, _ *list.Model) tea.Cmd { return nil } +func (d itemFuncsDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { + fun, ok := listItem.(itemFunc) + if !ok { + return + } + + maxw := m.Width() - 10 + + var proto strings.Builder + fmt.Fprintf(&proto, "%s(", fun.FuncName) + for j, param := range fun.Params { + if j != 0 { + fmt.Fprint(&proto, ", ") + } + + fmt.Fprintf(&proto, "%s %s", param.Name, param.Type) + } + fmt.Fprint(&proto, ")") + + switch len(fun.Results) { + case 0: // none + case 1: + fmt.Fprintf(&proto, " %s", fun.Results[0].Type) + default: + fmt.Fprint(&proto, " (") + for j, res := range fun.Results { + if j != 0 { + fmt.Fprint(&proto, ", ") + } + + fmt.Fprint(&proto, res.Type) + } + fmt.Fprint(&proto, ")") + } + + fn := listItemStyle.Render + if index == m.Index() { + fn = func(s ...string) string { + return listSelectedItemStyle.Render("> " + strings.Join(s, " ")) + } + } + + str := proto.String() + if len(str) > maxw { + str = str[:maxw-3] + "..." + } + + fmt.Fprint(w, fn(str)) +} + +func newFuncList() FuncListModel { + l := list.New([]list.Item{}, &itemFuncsDelegate{}, 0, 0) + l.SetShowStatusBar(false) + l.SetFilteringEnabled(true) + l.SetShowHelp(false) + l.SetShowPagination(false) + l.Styles.PaginationStyle = listPaginationStyle + return FuncListModel{ + Model: l, + items: l.Items(), + } +} diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go new file mode 100644 index 00000000000..f0db07b8e0c --- /dev/null +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -0,0 +1,466 @@ +package main + +import ( + "fmt" + "os" + "strings" + + "github.com/charmbracelet/bubbles/list" + "github.com/charmbracelet/bubbles/textinput" + "github.com/charmbracelet/bubbles/viewport" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/glamour" + "github.com/charmbracelet/lipgloss" + zone "github.com/lrstanley/bubblezone" + "golang.org/x/crypto/ssh/terminal" + + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/log" + "github.com/muesli/reflow/wordwrap" +) + +var noopLogger = log.NewNopLogger() + +const gnoPrefix = "gno.land/r/" + +// You generally won't need this unless you're processing stuff with +// complicated ANSI escape sequences. Turn it on if you notice flickering. +// +// Also keep in mind that high performance rendering only works for programs +// that use the full size of the terminal. We're enabling that below with +// tea.EnterAltScreen(). +const useHighPerformanceRenderer = false + +var ( + boxRoundedStyle = func() lipgloss.Style { + b := lipgloss.RoundedBorder() + return lipgloss.NewStyle(). + BorderStyle(b). + Padding(0, 2) + }() + + inputStyleLeft = func() lipgloss.Style { + b := lipgloss.RoundedBorder() + b.Right = "β”œ" + return lipgloss.NewStyle(). + BorderStyle(b). + Padding(0, 2) + }() + + infoStyle = func() lipgloss.Style { + b := lipgloss.RoundedBorder() + b.Left = "─" + return boxRoundedStyle.Copy().BorderStyle(b) + }() +) + +// func (i itemFunc) Description() string { +// var str strings +// return i.Params +// } + +type modelFunc struct { + textInput textinput.Model + err error +} + +type modelInput struct { + textInput textinput.Model + err error +} + +func initURLInput() textinput.Model { + ti := textinput.New() + ti.Placeholder = "demo/foo20" + ti.Focus() + ti.CharLimit = 156 + ti.PromptStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("#FF06B7")) + ti.Prompt = "gno.land/r/" + + return ti +} + +func initCommandInput() textinput.Model { + ti := textinput.New() + ti.Placeholder = "" + ti.CharLimit = 156 + ti.PromptStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("#FF06B7")) + ti.Prompt = "> " + + return ti +} + +type model struct { + name, pass string + kb keys.Keybase + + urlInput textinput.Model + listFuncs FuncListModel + + commandInput textinput.Model + commandFocus bool + zone *zone.Manager + ready bool + viewport viewport.Model + height int + + messageDisplay bool +} + +func (m model) Init() tea.Cmd { + return nil +} + +func (m model) fetchRenderView() (view []byte, err error) { + path := m.urlInput.Value() + rlmpath := gnoPrefix + path + + res, err := makeRender(noopLogger, rlmpath) + if err != nil { + return nil, fmt.Errorf("unable to fetch Render: %w", err) + } + r, err := glamour.NewTermRenderer( + glamour.WithAutoStyle(), + glamour.WithWordWrap(m.viewport.Width), + ) + + if err != nil { + return nil, fmt.Errorf("unable to get render view: %w", err) + } + + view, err = r.RenderBytes(res) + if err != nil { + return nil, fmt.Errorf("uanble to render markdown view: %w", err) + } + + return view, nil +} + +func (m model) fetchFuncsList() (view []list.Item, err error) { + path := m.urlInput.Value() + rlmpath := gnoPrefix + path + + funcs, err := makeFuncs(noopLogger, rlmpath) + if err != nil { + return nil, fmt.Errorf("unable to fetch Render: %w", err) + } + + items := make([]list.Item, 0, len(funcs)) + for _, fun := range funcs { + if fun.FuncName != "Render" { + items = append(items, itemFunc(fun)) + } + } + + return items, nil +} + +func (m model) getError(err error) string { + f := wordwrap.NewWriter(m.viewport.Width) + fmt.Fprintf(f, "error: %s", err) + serr := f.String() + return serr +} + +func (m *model) RenderUpdate() { + var err error + render, err := m.fetchRenderView() + if err != nil { + f := wordwrap.NewWriter(m.viewport.Width) + fmt.Fprintf(f, "error: %s", err) + m.viewport.SetContent(f.String()) + return + } + + m.viewport.SetContent(string(render)) + list, err := m.fetchFuncsList() + if err != nil { + f := wordwrap.NewWriter(m.viewport.Width) + fmt.Fprintf(f, "error: %s", err) + m.viewport.SetContent(f.String()) + return + } + + if len(list) == 0 { + return + } + + m.listFuncs.Title = m.urlInput.Value() + m.listFuncs.SetItems(list) + m.listFuncs.SetSize(m.viewport.Width, 7) +} + +func (m *model) ExtendCommandInput() { + if m.commandInput.Focused() { + if item, ok := m.listFuncs.SelectedItem().(itemFunc); ok { + m.commandInput.SetValue(item.Title()) + } + } +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var ( + cmd tea.Cmd + cmds []tea.Cmd + ) + + switch msg := msg.(type) { + case tea.MouseMsg: + if msg.Type != tea.MouseLeft { + return m, nil + } + + if m.zone.Get("url_input").InBounds(msg) { + m.commandInput.Blur() + cmds = append(cmds, m.urlInput.Focus()) + m.commandFocus = false + } + + if m.zone.Get("command_input").InBounds(msg) { + m.urlInput.Blur() + cmds = append(cmds, m.commandInput.Focus()) + m.commandFocus = true + } + + case tea.KeyMsg: + switch msg.String() { + case "alt+down": + if m.urlInput.Focused() { + m.urlInput.Blur() + cmds = append(cmds, m.commandInput.Focus()) + m.commandFocus = true + } + case "down": + if m.commandFocus { + m.listFuncs.CursorDown() + } + case "up": + if m.commandFocus { + m.listFuncs.CursorUp() + } + case "alt+up": + if m.commandInput.Focused() { + m.commandInput.Blur() + cmds = append(cmds, m.urlInput.Focus()) + m.commandFocus = false + } + case "alt+r": + m.RenderUpdate() + case "enter": + if m.commandInput.Focused() && !m.messageDisplay { + if len(m.listFuncs.Items()) > 1 { + m.ExtendCommandInput() + break + } + + path := m.urlInput.Value() + rlmpath := gnoPrefix + path + res, err := makeCall(noopLogger, makeCallCfg{ + kb: m.kb, + pass: m.pass, + nameOrBech32: m.name, + eval: m.commandInput.Value(), + rlmpath: rlmpath, + }) + + if err != nil { + content := fmt.Sprintf("%s\n\npress [enter] to dismiss", m.getError(err)) + m.viewport.SetContent(content) + } else { + if strings.TrimSpace(string(res)) == "" { + break + } + m.viewport.SetContent(fmt.Sprintf("%s\n\npress [enter] to dismiss", string(res))) + } + + m.listFuncs.Erase() + m.messageDisplay = true + break + } + + if m.messageDisplay || m.urlInput.Focused() { + m.listFuncs.Erase() + m.RenderUpdate() + m.messageDisplay = false + } + + case "tab": + m.ExtendCommandInput() + case "ctrl+c", "esc": + return m, tea.Quit + default: + if m.urlInput.Focused() { + m.urlInput, cmd = m.urlInput.Update(msg) + } else { + m.commandInput, cmd = m.commandInput.Update(msg) + } + // handle url input + + } + + case tea.WindowSizeMsg: + headerHeight := lipgloss.Height(m.headerView()) + footerHeight := lipgloss.Height(m.footerView()) + verticalMarginHeight := headerHeight + footerHeight + + if !m.ready { + // Since this program is using the full size of the viewport we + // need to wait until we've received the window dimensions before + // we can initialize the viewport. The initial dimensions come in + // quickly, though asynchronously, which is why we wait for them + // here. + m.viewport = viewport.New(msg.Width, msg.Height-verticalMarginHeight) + m.viewport.YPosition = headerHeight + m.viewport.HighPerformanceRendering = useHighPerformanceRenderer + m.ready = true + + // This is only necessary for high performance rendering, which in + // most cases you won't need. + // + // Render the viewport one line below the header. + m.viewport.YPosition = headerHeight + 1 + } else { + m.viewport.Width = msg.Width + m.viewport.Height = msg.Height - verticalMarginHeight + } + + m.height = m.viewport.Height + if !m.urlInput.Focused() && len(m.listFuncs.Items()) > 0 { + m.viewport.Height = m.height - lipgloss.Height(m.listFuncsView()) + } + + if useHighPerformanceRenderer { + // Render (or re-render) the whole viewport. Necessary both to + // initialize the viewport and when the window is resized. + // + // This is needed for high-performance rendering only. + cmds = append(cmds, viewport.Sync(m.viewport)) + } + } + + // m.listFuncs, cmd = m.listFuncs.Update(msg) + // cmds = append(cmds, cmd) + + if v := m.commandInput.Value(); v != "" { + m.listFuncs.FilterItems(v) + } else { + m.listFuncs.Reset() + } + + if m.commandFocus && len(m.listFuncs.Items()) > 0 { + m.viewport.Height = m.height - lipgloss.Height(m.listFuncsView()) + } else { + m.viewport.Height = m.height + } + + // Handle keyboard and mouse events in the viewport + if m.urlInput.Focused() { + m.viewport, cmd = m.viewport.Update(msg) + cmds = append(cmds, cmd) + } + + return m, tea.Batch(cmds...) +} + +func (m model) View() string { + if !m.ready { + return "\n Initializing..." + } + + mainView := fmt.Sprintf("%s\n%s\n%s", m.headerView(), m.bodyView(), m.footerView()) + return m.zone.Scan(mainView) +} + +func (m model) listFuncsView() string { + return boxRoundedStyle. + Render(m.listFuncs.View()) +} + +func (m model) bodyView() string { + return m.viewport.View() +} + +func (m model) headerView() string { + return m.zone.Mark("url_input", boxRoundedStyle. + Width(m.viewport.Width-2). + Render(m.urlInput.View())) +} + +func (m model) footerView() string { + info := infoStyle.Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) + command := m.zone.Mark("command_input", inputStyleLeft. + Width(m.viewport.Width-lipgloss.Width(info)-5). + Render(m.commandInput.View())) + line := strings.Repeat("─", 3) + + powerline := lipgloss.JoinHorizontal(lipgloss.Center, command, line, info) + if m.commandFocus && len(m.listFuncs.Items()) > 0 { + suggestions := m.listFuncsView() + return lipgloss.JoinVertical(lipgloss.Left, suggestions, powerline) + } + + return powerline +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func main() { + m := zone.New() + if len(os.Args) != 2 { + fmt.Fprintln(os.Stderr, "no nameOrBench32 given") + return + } + + name := os.Args[1] + + kb, err := keys.NewKeyBaseFromDir(gnoenv.HomeDir()) + if err != nil { + panic("unable to load keybase: " + err.Error()) + } + + if ok, err := kb.HasByNameOrAddress(name); !ok || err != nil { + if err != nil { + panic("invalid name: " + err.Error()) + } + + panic("unknown name/address: " + name) + } + + fmt.Printf("[%s] Enter password: ", name) + + password, err := terminal.ReadPassword(0) + if err != nil { + panic("error while reading password: " + err.Error()) + } + + if _, err := kb.ExportPrivKeyUnsafe(name, string(password)); err != nil { + panic("invalid password: " + err.Error()) + } + + cmd := initCommandInput() + p := tea.NewProgram( + model{ + kb: kb, + name: name, pass: string(password), + listFuncs: newFuncList(), + urlInput: initURLInput(), + commandInput: cmd, + zone: m, + }, + tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" + tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel + ) + + if _, err := p.Run(); err != nil { + fmt.Println("could not run program:", err) + os.Exit(1) + } +} diff --git a/contribs/gnobro/cmd/gnobro/request.go b/contribs/gnobro/cmd/gnobro/request.go new file mode 100644 index 00000000000..42a36107efb --- /dev/null +++ b/contribs/gnobro/cmd/gnobro/request.go @@ -0,0 +1,150 @@ +package main + +import ( + "errors" + "fmt" + "regexp" + "strings" + + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + "github.com/gnolang/gno/tm2/pkg/amino" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/log" +) + +const remoteAddr = "http://localhost:36657" +const qFileStr = "vm/qfile" + +var ( + ErrInternalError = errors.New("internal error") + ErrRenderNotFound = errors.New("render not found") +) + +type Request struct { + RemoteAddr string + QPath string + Data []byte +} + +func makeRequest(log log.Logger, req Request) (res *abci.ResponseQuery, err error) { + opts2 := client.ABCIQueryOptions{ + // Height: height, XXX + // Prove: false, XXX + } + remote := req.RemoteAddr + cli := client.NewHTTP(remote, "/websocket") + qres, err := cli.ABCIQueryWithOptions( + req.QPath, req.Data, opts2) + if err != nil { + log.Error("request error", "path", req.QPath, "error", err) + return nil, fmt.Errorf("unable to query path %q: %w", req.QPath, err) + } + if qres.Response.Error != nil { + log.Error("response error", "path", req.QPath, "log", qres.Response.Log) + return nil, fmt.Errorf("response error: %s\n\n%s\n", qres.Response.Error, qres.Response.Log) + } + return &qres.Response, nil +} + +func makeRender(logger log.Logger, rlmpath string) ([]byte, error) { + var req Request + req.RemoteAddr = remoteAddr + + req.Data = []byte(fmt.Sprintf("%s\n%s", rlmpath, "")) + req.QPath = "vm/qrender" + res, err := makeRequest(logger, req) + if err != nil { + if strings.Contains(err.Error(), "Render not declared") { + return nil, ErrRenderNotFound + } + + return nil, fmt.Errorf("unable to make request on %q: %w", rlmpath, err) + } + + return res.Data, nil +} + +func makeFuncs(logger log.Logger, rlmpath string) (vm.FunctionSignatures, error) { + var req Request + req.RemoteAddr = remoteAddr + + req.QPath = "vm/qfuncs" + req.Data = []byte(rlmpath) + res, err := makeRequest(logger, req) + if err != nil { + return nil, fmt.Errorf("request failed: %w", err) + } + var fsigs vm.FunctionSignatures + amino.MustUnmarshalJSON(res.Data, &fsigs) + // Fill fsigs with query parameters. + // for i := range fsigs { + // fsig := &(fsigs[i]) + // for j := range fsig.Params { + // param := &(fsig.Params[j]) + // value := query.Get(param.Name) + // param.Value = value + // } + // } + return fsigs, nil +} + +type makeCallCfg struct { + kb keys.Keybase + pass string + rlmpath string + eval string + nameOrBech32 string +} + +// gnokey maketx call -pkgpath "gno.land/r/dev/hello" -func "Inc" -gas-fee 1000000ugnot -gas-wanted 2000000 -send "" -broadcast -chainid "tendermint-test" -remote "http://127.0.0.1:36657" g1jg8mtut +func makeCall(logger log.Logger, makecfg makeCallCfg) ([]byte, error) { + var cfg callCfg + + var err error + cfg.funcName, cfg.args, err = parseMethodToArgs(makecfg.eval) + if err != nil { + return nil, err + } + + if len(cfg.args) == 0 { + cfg.args = nil + } + + cfg.gasFee = "1000000ugnot" + cfg.gasWanted = 2000000 + cfg.send = "" + cfg.broadcast = true + cfg.chainID = "tendermint_test" + cfg.remote = remoteAddr + cfg.pkgPath = makecfg.rlmpath + cfg.kb = makecfg.kb + + res, err := execCall(makecfg.nameOrBech32, makecfg.pass, cfg) + if err != nil { + return nil, err + } + + return res.DeliverTx.Data, nil +} + +var reMethod = regexp.MustCompile(`([^(]+)\(([^)]*)\)`) + +func parseMethodToArgs(call string) (method string, args []string, err error) { + matches := reMethod.FindStringSubmatch(call) + if len(matches) == 0 { + err = fmt.Errorf("invalid call: %w", err) + return + } + + method = matches[1] + sargs := matches[2] + + // Splitting arguments by comma + args = strings.Split(sargs, ",") + for i, arg := range args { + args[i] = strings.Trim(strings.TrimSpace(arg), "\"") + } + return +} diff --git a/contribs/gnobro/go.mod b/contribs/gnobro/go.mod new file mode 100644 index 00000000000..be89c51ec9e --- /dev/null +++ b/contribs/gnobro/go.mod @@ -0,0 +1,83 @@ +module github.com/gnolang/gno/contribs/gnobro + +go 1.21 + +replace github.com/gnolang/gno => ../.. + +require ( + github.com/charmbracelet/bubbles v0.18.0 + github.com/charmbracelet/bubbletea v0.26.2 + github.com/charmbracelet/glamour v0.7.0 + github.com/charmbracelet/lipgloss v0.10.0 + github.com/gnolang/gno v0.0.0-00010101000000-000000000000 + github.com/lrstanley/bubblezone v0.0.0-20240125042004-b7bafc493195 + github.com/muesli/reflow v0.3.0 + github.com/sahilm/fuzzy v0.1.1 + golang.org/x/crypto v0.21.0 +) + +require ( + github.com/alecthomas/chroma/v2 v2.8.0 // indirect + github.com/atotto/clipboard v0.1.4 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/aymerick/douceur v0.2.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect + github.com/btcsuite/btcd/btcutil v1.1.5 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cockroachdb/apd/v3 v3.2.1 // indirect + github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/dlclark/regexp2 v1.4.0 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/gorilla/css v1.0.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // 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.15 // indirect + github.com/microcosm-cc/bluemonday v1.0.25 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/peterbourgon/ff/v3 v3.4.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rs/xid v1.5.0 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + github.com/yuin/goldmark v1.5.4 // indirect + github.com/yuin/goldmark-emoji v1.0.2 // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect + go.opentelemetry.io/otel v1.25.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0 // indirect + go.opentelemetry.io/otel/metric v1.25.0 // indirect + go.opentelemetry.io/otel/sdk v1.25.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.25.0 // indirect + go.opentelemetry.io/otel/trace v1.25.0 // indirect + go.opentelemetry.io/proto/otlp v1.1.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect + golang.org/x/mod v0.16.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect + google.golang.org/grpc v1.63.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/contribs/gnobro/go.sum b/contribs/gnobro/go.sum new file mode 100644 index 00000000000..6fec22d41d0 --- /dev/null +++ b/contribs/gnobro/go.sum @@ -0,0 +1,285 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/chroma/v2 v2.8.0 h1:w9WJUjFFmHHB2e8mRpL9jjy3alYDlU0QLDezj1xE264= +github.com/alecthomas/chroma/v2 v2.8.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +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/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7Nzl7WHaOTlTr5hIrR4n1NM4v9n4Kw= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= +github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= +github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= +github.com/charmbracelet/bubbletea v0.26.2 h1:Eeb+n75Om9gQ+I6YpbCXQRKHt5Pn4vMwusQpwLiEgJQ= +github.com/charmbracelet/bubbletea v0.26.2/go.mod h1:6I0nZ3YHUrQj7YHIHlM8RySX4ZIthTliMY+W8X8b+Gs= +github.com/charmbracelet/glamour v0.7.0 h1:2BtKGZ4iVJCDfMF229EzbeR1QRKLWztO9dMtjmqZSng= +github.com/charmbracelet/glamour v0.7.0/go.mod h1:jUMh5MeihljJPQbJ/wf4ldw2+yBP59+ctV36jASy7ps= +github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s= +github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE= +github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= +github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= +github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= +github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk= +github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/lrstanley/bubblezone v0.0.0-20240125042004-b7bafc493195 h1:zcxmFnwisGZSaEzgvkOrs4belfcRlKyIUfa3sOQSttQ= +github.com/lrstanley/bubblezone v0.0.0-20240125042004-b7bafc493195/go.mod h1:v5lEwWaguF1o2MW/ucO0ZIA/IZymdBYJJ+2cMRLE7LU= +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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= +github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +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/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= +github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= +github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= +github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s= +github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= +go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= +go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0 h1:hDKnobznDpcdTlNzO0S/owRB8tyVr1OoeZZhDoqY+Cs= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0/go.mod h1:kUDQaUs1h8iTIHbQTk+iJRiUvSfJYMMKTtMCaiVu7B0= +go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= +go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= +go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= +go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= +go.opentelemetry.io/otel/sdk/metric v1.25.0 h1:7CiHOy08LbrxMAp4vWpbiPcklunUshVpAvGBrdDRlGw= +go.opentelemetry.io/otel/sdk/metric v1.25.0/go.mod h1:LzwoKptdbBBdYfvtGCzGwk6GWMA3aUzBOwtQpR6Nz7o= +go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= +go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8= +google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 2ad1adfe1789a075c3649fbaf4983b126f51469d Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 20 Jun 2024 13:45:47 +0200 Subject: [PATCH 02/64] feat: update model Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/call.go | 316 ---------------- contribs/gnobro/cmd/gnobro/main.go | 507 +++++--------------------- contribs/gnobro/cmd/gnobro/model.go | 397 ++++++++++++++++++++ contribs/gnobro/cmd/gnobro/request.go | 255 +++++++++---- 4 files changed, 667 insertions(+), 808 deletions(-) delete mode 100644 contribs/gnobro/cmd/gnobro/call.go create mode 100644 contribs/gnobro/cmd/gnobro/model.go diff --git a/contribs/gnobro/cmd/gnobro/call.go b/contribs/gnobro/cmd/gnobro/call.go deleted file mode 100644 index 637a05fd0e9..00000000000 --- a/contribs/gnobro/cmd/gnobro/call.go +++ /dev/null @@ -1,316 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - "github.com/gnolang/gno/gnovm/pkg/gnoenv" - "github.com/gnolang/gno/tm2/pkg/amino" - "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" - ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" - "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/crypto/keys" - "github.com/gnolang/gno/tm2/pkg/errors" - "github.com/gnolang/gno/tm2/pkg/std" -) - -type callCfg struct { - home string - - gasWanted int64 - gasFee string - memo string - kb keys.Keybase - remote string - - broadcast bool - chainID string - - send string - pkgPath string - funcName string - args commands.StringArr -} - -func execCall(nameOrBech32 string, pass string, cfg callCfg) (*ctypes.ResultBroadcastTxCommit, error) { - if cfg.pkgPath == "" { - return nil, errors.New("pkgpath not specified") - } - if cfg.funcName == "" { - return nil, errors.New("func not specified") - } - - if cfg.gasWanted == 0 { - return nil, errors.New("gas-wanted not specified") - } - if cfg.gasFee == "" { - return nil, errors.New("gas-fee not specified") - } - - // read statement. - fnc := cfg.funcName - - // read account pubkey. - info, err := cfg.kb.GetByNameOrAddress(nameOrBech32) - if err != nil { - return nil, err - } - caller := info.GetAddress() - // info.GetPubKey() - - // Parse send amount. - send, err := std.ParseCoins(cfg.send) - if err != nil { - return nil, fmt.Errorf("parsing send coins: %w", err) - } - - // parse gas wanted & fee. - gaswanted := cfg.gasWanted - gasfee, err := std.ParseCoin(cfg.gasFee) - if err != nil { - return nil, fmt.Errorf("parsing gas fee coin: %w", err) - } - - // construct msg & tx and marshal. - msg := vm.MsgCall{ - Caller: caller, - Send: send, - PkgPath: cfg.pkgPath, - Func: fnc, - Args: cfg.args, - } - tx := std.Tx{ - Msgs: []std.Msg{msg}, - Fee: std.NewFee(gaswanted, gasfee), - Signatures: nil, - Memo: cfg.memo, - } - - res, err := signAndBroadcast(cfg.remote, nameOrBech32, cfg.chainID, pass, cfg.args, tx) - if err != nil { - return nil, fmt.Errorf("unable to sign and broadcast: %w", err) - } - - return res, nil -} - -// func loadKeyBaseName(nameOrBech32 string) error { -// home := gnoenv.HomeDir() -// kb, err := keys.NewKeyBaseFromDir(home) -// if err != nil { -// return err -// } -// info, err := kb.GetByNameOrAddress(nameOrBech32) -// if err != nil { -// return err -// } - -// caller := info.GetAddress() -// return nil -// } - -type queryCfg struct { - data string - height int64 - prove bool - - // internal - path string -} - -func queryHandler(remote string, cfg *queryCfg) (*ctypes.ResultABCIQuery, error) { - if remote == "" || remote == "y" { - return nil, errors.New("missing remote url") - } - - data := []byte(cfg.data) - opts2 := client.ABCIQueryOptions{ - // Height: height, XXX - // Prove: false, XXX - } - cli := client.NewHTTP(remote, "/websocket") - qres, err := cli.ABCIQueryWithOptions( - cfg.path, data, opts2) - if err != nil { - return nil, errors.Wrap(err, "querying") - } - - return qres, nil -} - -type signCfg struct { - txPath string - chainID string - accountNumber uint64 - sequence uint64 - showSignBytes bool - home string - - // internal flags, when called programmatically - nameOrBech32 string - txJSON []byte - pass string -} - -func signAndBroadcast(remote, nameOrBech32, chainid, pass string, args []string, tx std.Tx) (*ctypes.ResultBroadcastTxCommit, error) { - home := gnoenv.HomeDir() - - // query account - kb, err := keys.NewKeyBaseFromDir(home) - if err != nil { - return nil, err - } - info, err := kb.GetByNameOrAddress(nameOrBech32) - if err != nil { - return nil, err - } - accountAddr := info.GetAddress() - - qopts := &queryCfg{ - path: fmt.Sprintf("auth/accounts/%s", accountAddr), - } - qres, err := queryHandler(remote, qopts) - if err != nil { - return nil, fmt.Errorf("query account: %w", err) - } - var qret struct{ BaseAccount std.BaseAccount } - err = amino.UnmarshalJSON(qres.Response.Data, &qret) - if err != nil { - return nil, fmt.Errorf("unmarshall query response: %w", err) - } - - // sign tx - accountNumber := qret.BaseAccount.AccountNumber - sequence := qret.BaseAccount.Sequence - sopts := &signCfg{ - home: home, - sequence: sequence, - accountNumber: accountNumber, - chainID: chainid, - nameOrBech32: nameOrBech32, - txJSON: amino.MustMarshalJSON(tx), - } - - signedTx, err := SignHandler(sopts) - if err != nil { - return nil, fmt.Errorf("sign tx: %w", err) - } - - // broadcast signed tx - bopts := &broadcastCfg{ - tx: signedTx, - } - - bres, err := broadcastHandler(remote, bopts) - if err != nil { - return nil, fmt.Errorf("broadcast: %w", err) - } - - if bres.CheckTx.IsErr() { - req := fmt.Sprintf("remote:%s name:%s chainid:%s pass:%s", remote, nameOrBech32, chainid, pass) - return nil, fmt.Errorf("check trasaction: %w \nreq: %s\nLog:%s", bres.CheckTx.Error, req, bres.CheckTx.Log) - } - - if bres.DeliverTx.IsErr() { - return nil, fmt.Errorf("check trasaction: %w \nLog:%s", bres.DeliverTx.Error, bres.DeliverTx.Log) - } - - return bres, nil -} - -func SignHandler(cfg *signCfg) (*std.Tx, error) { - var err error - var tx std.Tx - - if cfg.txJSON == nil { - return nil, errors.New("invalid tx content") - } - - kb, err := keys.NewKeyBaseFromDir(cfg.home) - if err != nil { - return nil, err - } - - err = amino.UnmarshalJSON(cfg.txJSON, &tx) - if err != nil { - return nil, err - } - - // fill tx signatures. - signers := tx.GetSigners() - if tx.Signatures == nil { - for range signers { - tx.Signatures = append(tx.Signatures, std.Signature{ - PubKey: nil, // zero signature - Signature: nil, // zero signature - }) - } - } - - // validate document to sign. - err = tx.ValidateBasic() - if err != nil { - return nil, err - } - - // derive sign doc bytes. - chainID := cfg.chainID - accountNumber := cfg.accountNumber - sequence := cfg.sequence - signbz := tx.GetSignBytes(chainID, accountNumber, sequence) - if cfg.showSignBytes { - return nil, fmt.Errorf("sign bytes: %X\n", signbz) - } - - sig, pub, err := kb.Sign(cfg.nameOrBech32, cfg.pass, signbz) - if err != nil { - return nil, err - } - addr := pub.Address() - found := false - for i := range tx.Signatures { - // override signature for matching slot. - if signers[i] == addr { - found = true - tx.Signatures[i] = std.Signature{ - PubKey: pub, - Signature: sig, - } - } - } - if !found { - return nil, errors.New( - fmt.Sprintf("addr %v (%s) not in signer set", addr, cfg.nameOrBech32), - ) - } - - return &tx, nil -} - -type broadcastCfg struct { - // internal - tx *std.Tx -} - -func broadcastHandler(remote string, cfg *broadcastCfg) (*ctypes.ResultBroadcastTxCommit, error) { - if cfg.tx == nil { - return nil, errors.New("invalid tx") - } - - if remote == "" || remote == "y" { - return nil, errors.New("missing remote url") - } - - bz, err := amino.Marshal(cfg.tx) - if err != nil { - return nil, errors.Wrap(err, "remarshaling tx binary bytes") - } - - cli := client.NewHTTP(remote, "/websocket") - bres, err := cli.BroadcastTxCommit(bz) - if err != nil { - return nil, errors.Wrap(err, "broadcasting bytes") - } - - return bres, nil -} diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index f0db07b8e0c..1039bb43b2e 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -1,455 +1,106 @@ package main import ( + "context" + "flag" "fmt" + "net" "os" "strings" - "github.com/charmbracelet/bubbles/list" - "github.com/charmbracelet/bubbles/textinput" - "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/glamour" - "github.com/charmbracelet/lipgloss" + "github.com/gnolang/gno/gno.land/pkg/gnoclient" + "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" + "github.com/gnolang/gno/tm2/pkg/commands" zone "github.com/lrstanley/bubblezone" - "golang.org/x/crypto/ssh/terminal" - - "github.com/gnolang/gno/gnovm/pkg/gnoenv" - "github.com/gnolang/gno/tm2/pkg/crypto/keys" - "github.com/gnolang/gno/tm2/pkg/log" - "github.com/muesli/reflow/wordwrap" -) - -var noopLogger = log.NewNopLogger() - -const gnoPrefix = "gno.land/r/" - -// You generally won't need this unless you're processing stuff with -// complicated ANSI escape sequences. Turn it on if you notice flickering. -// -// Also keep in mind that high performance rendering only works for programs -// that use the full size of the terminal. We're enabling that below with -// tea.EnterAltScreen(). -const useHighPerformanceRenderer = false - -var ( - boxRoundedStyle = func() lipgloss.Style { - b := lipgloss.RoundedBorder() - return lipgloss.NewStyle(). - BorderStyle(b). - Padding(0, 2) - }() - - inputStyleLeft = func() lipgloss.Style { - b := lipgloss.RoundedBorder() - b.Right = "β”œ" - return lipgloss.NewStyle(). - BorderStyle(b). - Padding(0, 2) - }() - - infoStyle = func() lipgloss.Style { - b := lipgloss.RoundedBorder() - b.Left = "─" - return boxRoundedStyle.Copy().BorderStyle(b) - }() ) -// func (i itemFunc) Description() string { -// var str strings -// return i.Params -// } - -type modelFunc struct { - textInput textinput.Model - err error -} - -type modelInput struct { - textInput textinput.Model - err error -} - -func initURLInput() textinput.Model { - ti := textinput.New() - ti.Placeholder = "demo/foo20" - ti.Focus() - ti.CharLimit = 156 - ti.PromptStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FF06B7")) - ti.Prompt = "gno.land/r/" - - return ti +type broCfg struct { + target string + chainID string } -func initCommandInput() textinput.Model { - ti := textinput.New() - ti.Placeholder = "" - ti.CharLimit = 156 - ti.PromptStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FF06B7")) - ti.Prompt = "> " - - return ti +var defaultBroOptions = broCfg{ + target: "127.0.0.1:26657", + chainID: "dev", } -type model struct { - name, pass string - kb keys.Keybase - - urlInput textinput.Model - listFuncs FuncListModel - - commandInput textinput.Model - commandFocus bool - zone *zone.Manager - ready bool - viewport viewport.Model - height int - - messageDisplay bool -} +func main() { + cfg := &broCfg{} + + stdio := commands.NewDefaultIO() + cmd := commands.NewCommand( + commands.Metadata{ + Name: "gnobro", + ShortUsage: "gnobro [flags] [path ...]", + ShortHelp: "runs a cli browser.", + LongHelp: `run a cli browser`, + }, + cfg, + func(_ context.Context, args []string) error { + return execBrowser(cfg, args, stdio) + }) -func (m model) Init() tea.Cmd { - return nil + cmd.Execute(context.Background(), os.Args[1:]) } -func (m model) fetchRenderView() (view []byte, err error) { - path := m.urlInput.Value() - rlmpath := gnoPrefix + path - - res, err := makeRender(noopLogger, rlmpath) - if err != nil { - return nil, fmt.Errorf("unable to fetch Render: %w", err) - } - r, err := glamour.NewTermRenderer( - glamour.WithAutoStyle(), - glamour.WithWordWrap(m.viewport.Width), +func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.target, + "target", + defaultBroOptions.target, + "target gnoland address", ) - if err != nil { - return nil, fmt.Errorf("unable to get render view: %w", err) - } - - view, err = r.RenderBytes(res) - if err != nil { - return nil, fmt.Errorf("uanble to render markdown view: %w", err) - } - - return view, nil -} - -func (m model) fetchFuncsList() (view []list.Item, err error) { - path := m.urlInput.Value() - rlmpath := gnoPrefix + path - - funcs, err := makeFuncs(noopLogger, rlmpath) - if err != nil { - return nil, fmt.Errorf("unable to fetch Render: %w", err) - } - - items := make([]list.Item, 0, len(funcs)) - for _, fun := range funcs { - if fun.FuncName != "Render" { - items = append(items, itemFunc(fun)) - } - } - - return items, nil } -func (m model) getError(err error) string { - f := wordwrap.NewWriter(m.viewport.Width) - fmt.Fprintf(f, "error: %s", err) - serr := f.String() - return serr -} - -func (m *model) RenderUpdate() { - var err error - render, err := m.fetchRenderView() - if err != nil { - f := wordwrap.NewWriter(m.viewport.Width) - fmt.Fprintf(f, "error: %s", err) - m.viewport.SetContent(f.String()) - return - } +func execBrowser(cfg *broCfg, args []string, io commands.IO) error { + m := zone.New() + // if len(os.Args) != 2 { + // fmt.Fprintln(os.Stderr, "no nameOrBench32 given") + // return + // } - m.viewport.SetContent(string(render)) - list, err := m.fetchFuncsList() + remoteAddr := resolveUnixOrTCPAddr(cfg.target) + cl, err := client.NewHTTPClient(remoteAddr) if err != nil { - f := wordwrap.NewWriter(m.viewport.Width) - fmt.Fprintf(f, "error: %s", err) - m.viewport.SetContent(f.String()) - return - } - - if len(list) == 0 { - return + return fmt.Errorf("unable to create http client for %q: %w", remoteAddr, err) } - m.listFuncs.Title = m.urlInput.Value() - m.listFuncs.SetItems(list) - m.listFuncs.SetSize(m.viewport.Width, 7) -} - -func (m *model) ExtendCommandInput() { - if m.commandInput.Focused() { - if item, ok := m.listFuncs.SelectedItem().(itemFunc); ok { - m.commandInput.SetValue(item.Title()) - } + broclient := gnoclient.Client{ + RPCClient: cl, } -} - -func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - var ( - cmd tea.Cmd - cmds []tea.Cmd - ) - - switch msg := msg.(type) { - case tea.MouseMsg: - if msg.Type != tea.MouseLeft { - return m, nil - } - if m.zone.Get("url_input").InBounds(msg) { - m.commandInput.Blur() - cmds = append(cmds, m.urlInput.Focus()) - m.commandFocus = false - } - - if m.zone.Get("command_input").InBounds(msg) { - m.urlInput.Blur() - cmds = append(cmds, m.commandInput.Focus()) - m.commandFocus = true - } - - case tea.KeyMsg: - switch msg.String() { - case "alt+down": - if m.urlInput.Focused() { - m.urlInput.Blur() - cmds = append(cmds, m.commandInput.Focus()) - m.commandFocus = true - } - case "down": - if m.commandFocus { - m.listFuncs.CursorDown() - } - case "up": - if m.commandFocus { - m.listFuncs.CursorUp() - } - case "alt+up": - if m.commandInput.Focused() { - m.commandInput.Blur() - cmds = append(cmds, m.urlInput.Focus()) - m.commandFocus = false - } - case "alt+r": - m.RenderUpdate() - case "enter": - if m.commandInput.Focused() && !m.messageDisplay { - if len(m.listFuncs.Items()) > 1 { - m.ExtendCommandInput() - break - } + // name := os.Args[1] - path := m.urlInput.Value() - rlmpath := gnoPrefix + path - res, err := makeCall(noopLogger, makeCallCfg{ - kb: m.kb, - pass: m.pass, - nameOrBech32: m.name, - eval: m.commandInput.Value(), - rlmpath: rlmpath, - }) + // kb, err := keys.NewKeyBaseFromDir(gnoenv.HomeDir()) + // if err != nil { + // panic("unable to load keybase: " + err.Error()) + // } - if err != nil { - content := fmt.Sprintf("%s\n\npress [enter] to dismiss", m.getError(err)) - m.viewport.SetContent(content) - } else { - if strings.TrimSpace(string(res)) == "" { - break - } - m.viewport.SetContent(fmt.Sprintf("%s\n\npress [enter] to dismiss", string(res))) - } + // if ok, err := kb.HasByNameOrAddress(name); !ok || err != nil { + // if err != nil { + // panic("invalid name: " + err.Error()) + // } - m.listFuncs.Erase() - m.messageDisplay = true - break - } + // panic("unknown name/address: " + name) + // } - if m.messageDisplay || m.urlInput.Focused() { - m.listFuncs.Erase() - m.RenderUpdate() - m.messageDisplay = false - } + // fmt.Printf("[%s] Enter password: ", name) - case "tab": - m.ExtendCommandInput() - case "ctrl+c", "esc": - return m, tea.Quit - default: - if m.urlInput.Focused() { - m.urlInput, cmd = m.urlInput.Update(msg) - } else { - m.commandInput, cmd = m.commandInput.Update(msg) - } - // handle url input + // password, err := terminal.ReadPassword(0) + // if err != nil { + // panic("error while reading password: " + err.Error()) + // } - } - - case tea.WindowSizeMsg: - headerHeight := lipgloss.Height(m.headerView()) - footerHeight := lipgloss.Height(m.footerView()) - verticalMarginHeight := headerHeight + footerHeight - - if !m.ready { - // Since this program is using the full size of the viewport we - // need to wait until we've received the window dimensions before - // we can initialize the viewport. The initial dimensions come in - // quickly, though asynchronously, which is why we wait for them - // here. - m.viewport = viewport.New(msg.Width, msg.Height-verticalMarginHeight) - m.viewport.YPosition = headerHeight - m.viewport.HighPerformanceRendering = useHighPerformanceRenderer - m.ready = true - - // This is only necessary for high performance rendering, which in - // most cases you won't need. - // - // Render the viewport one line below the header. - m.viewport.YPosition = headerHeight + 1 - } else { - m.viewport.Width = msg.Width - m.viewport.Height = msg.Height - verticalMarginHeight - } - - m.height = m.viewport.Height - if !m.urlInput.Focused() && len(m.listFuncs.Items()) > 0 { - m.viewport.Height = m.height - lipgloss.Height(m.listFuncsView()) - } - - if useHighPerformanceRenderer { - // Render (or re-render) the whole viewport. Necessary both to - // initialize the viewport and when the window is resized. - // - // This is needed for high-performance rendering only. - cmds = append(cmds, viewport.Sync(m.viewport)) - } - } - - // m.listFuncs, cmd = m.listFuncs.Update(msg) - // cmds = append(cmds, cmd) - - if v := m.commandInput.Value(); v != "" { - m.listFuncs.FilterItems(v) - } else { - m.listFuncs.Reset() - } - - if m.commandFocus && len(m.listFuncs.Items()) > 0 { - m.viewport.Height = m.height - lipgloss.Height(m.listFuncsView()) - } else { - m.viewport.Height = m.height - } - - // Handle keyboard and mouse events in the viewport - if m.urlInput.Focused() { - m.viewport, cmd = m.viewport.Update(msg) - cmds = append(cmds, cmd) - } - - return m, tea.Batch(cmds...) -} - -func (m model) View() string { - if !m.ready { - return "\n Initializing..." - } - - mainView := fmt.Sprintf("%s\n%s\n%s", m.headerView(), m.bodyView(), m.footerView()) - return m.zone.Scan(mainView) -} - -func (m model) listFuncsView() string { - return boxRoundedStyle. - Render(m.listFuncs.View()) -} - -func (m model) bodyView() string { - return m.viewport.View() -} - -func (m model) headerView() string { - return m.zone.Mark("url_input", boxRoundedStyle. - Width(m.viewport.Width-2). - Render(m.urlInput.View())) -} - -func (m model) footerView() string { - info := infoStyle.Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) - command := m.zone.Mark("command_input", inputStyleLeft. - Width(m.viewport.Width-lipgloss.Width(info)-5). - Render(m.commandInput.View())) - line := strings.Repeat("─", 3) - - powerline := lipgloss.JoinHorizontal(lipgloss.Center, command, line, info) - if m.commandFocus && len(m.listFuncs.Items()) > 0 { - suggestions := m.listFuncsView() - return lipgloss.JoinVertical(lipgloss.Left, suggestions, powerline) - } - - return powerline -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} - -func main() { - m := zone.New() - if len(os.Args) != 2 { - fmt.Fprintln(os.Stderr, "no nameOrBench32 given") - return - } - - name := os.Args[1] - - kb, err := keys.NewKeyBaseFromDir(gnoenv.HomeDir()) - if err != nil { - panic("unable to load keybase: " + err.Error()) - } - - if ok, err := kb.HasByNameOrAddress(name); !ok || err != nil { - if err != nil { - panic("invalid name: " + err.Error()) - } - - panic("unknown name/address: " + name) - } - - fmt.Printf("[%s] Enter password: ", name) - - password, err := terminal.ReadPassword(0) - if err != nil { - panic("error while reading password: " + err.Error()) - } - - if _, err := kb.ExportPrivKeyUnsafe(name, string(password)); err != nil { - panic("invalid password: " + err.Error()) - } + // if _, err := kb.ExportPrivKeyUnsafe(name, string(password)); err != nil { + // panic("invalid password: " + err.Error()) + // } cmd := initCommandInput() p := tea.NewProgram( model{ - kb: kb, - name: name, pass: string(password), + client: &BroClient{client: &broclient}, listFuncs: newFuncList(), urlInput: initURLInput(), commandInput: cmd, @@ -460,7 +111,31 @@ func main() { ) if _, err := p.Run(); err != nil { - fmt.Println("could not run program:", err) - os.Exit(1) + return fmt.Errorf("could not run program: %w", err) } + + return nil +} + +func resolveUnixOrTCPAddr(in string) (out string) { + var err error + var addr net.Addr + + if strings.HasPrefix(in, "unix://") { + in = strings.TrimPrefix(in, "unix://") + if addr, err := net.ResolveUnixAddr("unix", in); err == nil { + return fmt.Sprintf("%s://%s", addr.Network(), addr.String()) + } + + err = fmt.Errorf("unable to resolve unix address `unix://%s`: %w", in, err) + } else { // don't bother to checking prefix + in = strings.TrimPrefix(in, "tcp://") + if addr, err = net.ResolveTCPAddr("tcp", in); err == nil { + return fmt.Sprintf("%s://%s", addr.Network(), addr.String()) + } + + err = fmt.Errorf("unable to resolve tcp address `tcp://%s`: %w", in, err) + } + + panic(err) } diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go new file mode 100644 index 00000000000..3f365c598dd --- /dev/null +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -0,0 +1,397 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/charmbracelet/bubbles/list" + "github.com/charmbracelet/bubbles/textinput" + "github.com/charmbracelet/bubbles/viewport" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/glamour" + "github.com/charmbracelet/lipgloss" + zone "github.com/lrstanley/bubblezone" + + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/muesli/reflow/wordwrap" +) + +// var noopLogger = log.NewNopLogger() + +const gnoPrefix = "gno.land/r/" + +// You generally won't need this unless you're processing stuff with +// complicated ANSI escape sequences. Turn it on if you notice flickering. +// +// Also keep in mind that high performance rendering only works for programs +// that use the full size of the terminal. We're enabling that below with +// tea.EnterAltScreen(). +const useHighPerformanceRenderer = false + +var ( + boxRoundedStyle = func() lipgloss.Style { + b := lipgloss.RoundedBorder() + return lipgloss.NewStyle(). + BorderStyle(b). + Padding(0, 2) + }() + + inputStyleLeft = func() lipgloss.Style { + b := lipgloss.RoundedBorder() + b.Right = "β”œ" + return lipgloss.NewStyle(). + BorderStyle(b). + Padding(0, 2) + }() + + infoStyle = func() lipgloss.Style { + b := lipgloss.RoundedBorder() + b.Left = "─" + return boxRoundedStyle.Copy().BorderStyle(b) + }() +) + +// func (i itemFunc) Description() string { +// var str strings +// return i.Params +// } + +type modelFunc struct { + textInput textinput.Model + err error +} + +type modelInput struct { + textInput textinput.Model + err error +} + +func initURLInput() textinput.Model { + ti := textinput.New() + ti.Placeholder = "demo/foo20" + ti.Focus() + ti.CharLimit = 156 + ti.PromptStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("#FF06B7")) + ti.Prompt = "gno.land/r/" + + return ti +} + +func initCommandInput() textinput.Model { + ti := textinput.New() + ti.Placeholder = "" + ti.CharLimit = 156 + ti.PromptStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("#FF06B7")) + ti.Prompt = "> " + + return ti +} + +type model struct { + name, pass string + kb keys.Keybase + + client *BroClient + + urlInput textinput.Model + listFuncs FuncListModel + + commandInput textinput.Model + commandFocus bool + zone *zone.Manager + ready bool + viewport viewport.Model + height int + + messageDisplay bool +} + +func (m model) Init() tea.Cmd { + return nil +} + +func (m model) fetchRenderView() (view []byte, err error) { + path := m.urlInput.Value() + rlmpath := gnoPrefix + path + + rlmpath, args, _ := strings.Cut(rlmpath, ":") + res, err := m.client.Render(rlmpath, args) + if err != nil { + return nil, fmt.Errorf("unable to fetch Render: %w", err) + } + r, err := glamour.NewTermRenderer( + glamour.WithAutoStyle(), + glamour.WithWordWrap(m.viewport.Width), + ) + + if err != nil { + return nil, fmt.Errorf("unable to get render view: %w", err) + } + + view, err = r.RenderBytes(res) + if err != nil { + return nil, fmt.Errorf("uanble to render markdown view: %w", err) + } + + return view, nil +} + +func (m model) fetchFuncsList() (view []list.Item, err error) { + path := m.urlInput.Value() + rlmpath := gnoPrefix + path + + funcs, err := m.client.Funcs(rlmpath) + if err != nil { + return nil, fmt.Errorf("unable to fetch Render: %w", err) + } + + items := make([]list.Item, 0, len(funcs)) + for _, fun := range funcs { + if fun.FuncName != "Render" { + items = append(items, itemFunc(fun)) + } + } + + return items, nil +} + +func (m model) getError(err error) string { + f := wordwrap.NewWriter(m.viewport.Width) + fmt.Fprintf(f, "error: %s", err) + serr := f.String() + return serr +} + +func (m *model) RenderUpdate() { + var err error + render, err := m.fetchRenderView() + if err != nil { + f := wordwrap.NewWriter(m.viewport.Width) + fmt.Fprintf(f, "error: %s", err) + m.viewport.SetContent(f.String()) + return + } + + m.viewport.SetContent(string(render)) + list, err := m.fetchFuncsList() + if err != nil { + f := wordwrap.NewWriter(m.viewport.Width) + fmt.Fprintf(f, "error: %s", err) + m.viewport.SetContent(f.String()) + return + } + + if len(list) == 0 { + return + } + + m.listFuncs.Title = m.urlInput.Value() + m.listFuncs.SetItems(list) + m.listFuncs.SetSize(m.viewport.Width, 7) +} + +func (m *model) ExtendCommandInput() { + if m.commandInput.Focused() { + if item, ok := m.listFuncs.SelectedItem().(itemFunc); ok { + m.commandInput.SetValue(item.Title()) + } + } +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var ( + cmd tea.Cmd + cmds []tea.Cmd + ) + + switch msg := msg.(type) { + case tea.MouseMsg: + if msg.Type != tea.MouseLeft { + return m, nil + } + + if m.zone.Get("url_input").InBounds(msg) { + m.commandInput.Blur() + cmds = append(cmds, m.urlInput.Focus()) + m.commandFocus = false + } + + if m.zone.Get("command_input").InBounds(msg) { + m.urlInput.Blur() + cmds = append(cmds, m.commandInput.Focus()) + m.commandFocus = true + } + + case tea.KeyMsg: + switch msg.String() { + case "alt+down": + if m.urlInput.Focused() { + m.urlInput.Blur() + cmds = append(cmds, m.commandInput.Focus()) + m.commandFocus = true + } + case "down", "tab": + if m.commandFocus { + m.listFuncs.CursorDown() + } + case "up": + if m.commandFocus { + m.listFuncs.CursorUp() + } + case "alt+up": + if m.commandInput.Focused() { + m.commandInput.Blur() + cmds = append(cmds, m.urlInput.Focus()) + m.commandFocus = false + } + case "alt+r": + m.RenderUpdate() + case "enter": + if m.commandInput.Focused() && !m.messageDisplay { + if len(m.listFuncs.Items()) > 1 { + m.ExtendCommandInput() + break + } + + path := m.urlInput.Value() + rlmpath := gnoPrefix + path + res, err := m.client.Call(rlmpath, m.commandInput.Value()) + + if err != nil { + content := fmt.Sprintf("%s\n\npress [enter] to dismiss", m.getError(err)) + m.viewport.SetContent(content) + } else { + if strings.TrimSpace(string(res)) == "" { + break + } + m.viewport.SetContent(fmt.Sprintf("%s\n\npress [enter] to dismiss", string(res))) + } + + m.listFuncs.Erase() + m.messageDisplay = true + break + } + + if m.messageDisplay || m.urlInput.Focused() { + m.listFuncs.Erase() + m.RenderUpdate() + m.messageDisplay = false + } + + case "ctrl+c", "esc": + return m, tea.Quit + default: + if m.urlInput.Focused() { + m.urlInput, cmd = m.urlInput.Update(msg) + } else { + m.commandInput, cmd = m.commandInput.Update(msg) + } + // handle url input + + } + + case tea.WindowSizeMsg: + headerHeight := lipgloss.Height(m.headerView()) + footerHeight := lipgloss.Height(m.footerView()) + verticalMarginHeight := headerHeight + footerHeight + + if !m.ready { + // Since this program is using the full size of the viewport we + // need to wait until we've received the window dimensions before + // we can initialize the viewport. The initial dimensions come in + // quickly, though asynchronously, which is why we wait for them + // here. + m.viewport = viewport.New(msg.Width, msg.Height-verticalMarginHeight) + m.viewport.YPosition = headerHeight + m.viewport.HighPerformanceRendering = useHighPerformanceRenderer + m.ready = true + + // This is only necessary for high performance rendering, which in + // most cases you won't need. + // + // Render the viewport one line below the header. + m.viewport.YPosition = headerHeight + 1 + } else { + m.viewport.Width = msg.Width + m.viewport.Height = msg.Height - verticalMarginHeight + } + + m.height = m.viewport.Height + if !m.urlInput.Focused() && len(m.listFuncs.Items()) > 0 { + m.viewport.Height = m.height - lipgloss.Height(m.listFuncsView()) + } + + if useHighPerformanceRenderer { + // Render (or re-render) the whole viewport. Necessary both to + // initialize the viewport and when the window is resized. + // + // This is needed for high-performance rendering only. + cmds = append(cmds, viewport.Sync(m.viewport)) + } + } + + // m.listFuncs, cmd = m.listFuncs.Update(msg) + // cmds = append(cmds, cmd) + + if v := m.commandInput.Value(); v != "" { + m.listFuncs.FilterItems(v) + } else { + m.listFuncs.Reset() + } + + if m.commandFocus && len(m.listFuncs.Items()) > 0 { + m.viewport.Height = m.height - lipgloss.Height(m.listFuncsView()) + } else { + m.viewport.Height = m.height + } + + // Handle keyboard and mouse events in the viewport + if m.urlInput.Focused() { + m.viewport, cmd = m.viewport.Update(msg) + cmds = append(cmds, cmd) + } + + return m, tea.Batch(cmds...) +} + +func (m model) View() string { + if !m.ready { + return "\n Initializing..." + } + + mainView := fmt.Sprintf("%s\n%s\n%s", m.headerView(), m.bodyView(), m.footerView()) + return m.zone.Scan(mainView) +} + +func (m model) listFuncsView() string { + return boxRoundedStyle. + Render(m.listFuncs.View()) +} + +func (m model) bodyView() string { + return m.viewport.View() +} + +func (m model) headerView() string { + return m.zone.Mark("url_input", boxRoundedStyle. + Width(m.viewport.Width-2). + Render(m.urlInput.View())) +} + +func (m model) footerView() string { + info := infoStyle.Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) + command := m.zone.Mark("command_input", inputStyleLeft. + Width(m.viewport.Width-lipgloss.Width(info)-5). + Render(m.commandInput.View())) + line := strings.Repeat("─", 3) + + powerline := lipgloss.JoinHorizontal(lipgloss.Center, command, line, info) + if m.commandFocus && len(m.listFuncs.Items()) > 0 { + suggestions := m.listFuncsView() + return lipgloss.JoinVertical(lipgloss.Left, suggestions, powerline) + } + + return powerline +} diff --git a/contribs/gnobro/cmd/gnobro/request.go b/contribs/gnobro/cmd/gnobro/request.go index 42a36107efb..c934dd4bcdc 100644 --- a/contribs/gnobro/cmd/gnobro/request.go +++ b/contribs/gnobro/cmd/gnobro/request.go @@ -3,15 +3,13 @@ package main import ( "errors" "fmt" + "log/slog" "regexp" "strings" + "github.com/gnolang/gno/gno.land/pkg/gnoclient" "github.com/gnolang/gno/gno.land/pkg/sdk/vm" "github.com/gnolang/gno/tm2/pkg/amino" - abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" - "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" - "github.com/gnolang/gno/tm2/pkg/crypto/keys" - "github.com/gnolang/gno/tm2/pkg/log" ) const remoteAddr = "http://localhost:36657" @@ -22,62 +20,87 @@ var ( ErrRenderNotFound = errors.New("render not found") ) -type Request struct { - RemoteAddr string - QPath string - Data []byte +type BroClient struct { + client *gnoclient.Client + log *slog.Logger } -func makeRequest(log log.Logger, req Request) (res *abci.ResponseQuery, err error) { - opts2 := client.ABCIQueryOptions{ - // Height: height, XXX - // Prove: false, XXX - } - remote := req.RemoteAddr - cli := client.NewHTTP(remote, "/websocket") - qres, err := cli.ABCIQueryWithOptions( - req.QPath, req.Data, opts2) +type CallCfg struct { + rlmpath string + eval string + nameOrBech32 string +} + +// gnokey maketx call -pkgpath "gno.land/r/dev/hello" -func "Inc" -gas-fee 1000000ugnot -gas-wanted 2000000 -send "" -broadcast -chainid "tendermint-test" -remote "http://127.0.0.1:36657" g1jg8mtut +func (bl *BroClient) Call(path, call string) ([]byte, error) { + method, args, err := parseMethodToArgs(call) if err != nil { - log.Error("request error", "path", req.QPath, "error", err) - return nil, fmt.Errorf("unable to query path %q: %w", req.QPath, err) - } - if qres.Response.Error != nil { - log.Error("response error", "path", req.QPath, "log", qres.Response.Log) - return nil, fmt.Errorf("response error: %s\n\n%s\n", qres.Response.Error, qres.Response.Log) + return nil, fmt.Errorf("unable to parse method/args: %w", err) } - return &qres.Response, nil -} -func makeRender(logger log.Logger, rlmpath string) ([]byte, error) { - var req Request - req.RemoteAddr = remoteAddr + base := gnoclient.BaseTxCfg{} + cm, err := bl.client.Call(base, gnoclient.MsgCall{ + PkgPath: path, + FuncName: method, + Args: args, + }) - req.Data = []byte(fmt.Sprintf("%s\n%s", rlmpath, "")) - req.QPath = "vm/qrender" - res, err := makeRequest(logger, req) if err != nil { - if strings.Contains(err.Error(), "Render not declared") { - return nil, ErrRenderNotFound - } + return nil, err + } - return nil, fmt.Errorf("unable to make request on %q: %w", rlmpath, err) + if cm.CheckTx.Error != nil { + return nil, fmt.Errorf("check error: %w", err) } - return res.Data, nil -} + if cm.DeliverTx.Error != nil { + return nil, fmt.Errorf("delivry error: %w", err) + } + + return cm.DeliverTx.Data, nil + + // var err error + // cfg.funcName, cfg.args, err = parseMethodToArgs(makecfg.eval) + // if err != nil { + // return nil, err + // } -func makeFuncs(logger log.Logger, rlmpath string) (vm.FunctionSignatures, error) { - var req Request - req.RemoteAddr = remoteAddr + // if len(cfg.args) == 0 { + // cfg.args = nil + // } - req.QPath = "vm/qfuncs" - req.Data = []byte(rlmpath) - res, err := makeRequest(logger, req) + // cfg.gasFee = "1000000ugnot" + // cfg.gasWanted = 2000000 + // cfg.send = "" + // cfg.broadcast = true + // cfg.chainID = "tendermint_test" + // cfg.remote = remoteAddr + // cfg.pkgPath = makecfg.rlmpath + // cfg.kb = makecfg.kb + + // res, err := execCall(makecfg.nameOrBech32, makecfg.pass, cfg) + // if err != nil { + // return nil, err + // } + + // return res.DeliverTx.Data, nil +} + +func (bl *BroClient) Funcs(path string) (vm.FunctionSignatures, error) { + res, err := bl.client.Query(gnoclient.QueryCfg{ + Path: "vm/qfuncs", + Data: []byte(path), + }) if err != nil { - return nil, fmt.Errorf("request failed: %w", err) + return nil, err + } + + if err := res.Response.Error; err != nil { + return nil, err } + var fsigs vm.FunctionSignatures - amino.MustUnmarshalJSON(res.Data, &fsigs) + amino.MustUnmarshalJSON(res.Response.Data, &fsigs) // Fill fsigs with query parameters. // for i := range fsigs { // fsig := &(fsigs[i]) @@ -90,43 +113,16 @@ func makeFuncs(logger log.Logger, rlmpath string) (vm.FunctionSignatures, error) return fsigs, nil } -type makeCallCfg struct { - kb keys.Keybase - pass string - rlmpath string - eval string - nameOrBech32 string -} - -// gnokey maketx call -pkgpath "gno.land/r/dev/hello" -func "Inc" -gas-fee 1000000ugnot -gas-wanted 2000000 -send "" -broadcast -chainid "tendermint-test" -remote "http://127.0.0.1:36657" g1jg8mtut -func makeCall(logger log.Logger, makecfg makeCallCfg) ([]byte, error) { - var cfg callCfg - - var err error - cfg.funcName, cfg.args, err = parseMethodToArgs(makecfg.eval) +func (bl *BroClient) Render(path, args string) ([]byte, error) { + data, res, err := bl.client.Render(path, args) if err != nil { return nil, err } - - if len(cfg.args) == 0 { - cfg.args = nil - } - - cfg.gasFee = "1000000ugnot" - cfg.gasWanted = 2000000 - cfg.send = "" - cfg.broadcast = true - cfg.chainID = "tendermint_test" - cfg.remote = remoteAddr - cfg.pkgPath = makecfg.rlmpath - cfg.kb = makecfg.kb - - res, err := execCall(makecfg.nameOrBech32, makecfg.pass, cfg) - if err != nil { + if err := res.Response.Error; err != nil { return nil, err } - return res.DeliverTx.Data, nil + return []byte(data), nil } var reMethod = regexp.MustCompile(`([^(]+)\(([^)]*)\)`) @@ -148,3 +144,110 @@ func parseMethodToArgs(call string) (method string, args []string, err error) { } return } + +// type Request struct { +// RemoteAddr string +// QPath string +// Data []byte +// } + +// func makeRequest(log log.Logger, req Request) (res *abci.ResponseQuery, err error) { +// opts2 := client.ABCIQueryOptions{ +// // Height: height, XXX +// // Prove: false, XXX +// } +// remote := req.RemoteAddr +// cli := client.NewHTTP(remote, "/websocket") +// qres, err := cli.ABCIQueryWithOptions( +// req.QPath, req.Data, opts2) +// if err != nil { +// log.Error("request error", "path", req.QPath, "error", err) +// return nil, fmt.Errorf("unable to query path %q: %w", req.QPath, err) +// } +// if qres.Response.Error != nil { +// log.Error("response error", "path", req.QPath, "log", qres.Response.Log) +// return nil, fmt.Errorf("response error: %s\n\n%s\n", qres.Response.Error, qres.Response.Log) +// } +// return &qres.Response, nil +// } + +// func makeRender(logger log.Logger, rlmpath string) ([]byte, error) { +// var req Request +// req.RemoteAddr = remoteAddr + +// req.Data = []byte(fmt.Sprintf("%s\n%s", rlmpath, "")) +// req.QPath = "vm/qrender" +// res, err := makeRequest(logger, req) +// if err != nil { +// if strings.Contains(err.Error(), "Render not declared") { +// return nil, ErrRenderNotFound +// } + +// return nil, fmt.Errorf("unable to make request on %q: %w", rlmpath, err) +// } + +// return res.Data, nil +// } + +// func makeFuncs(logger log.Logger, rlmpath string) (vm.FunctionSignatures, error) { +// var req Request +// req.RemoteAddr = remoteAddr + +// req.QPath = "vm/qfuncs" +// req.Data = []byte(rlmpath) +// res, err := makeRequest(logger, req) +// if err != nil { +// return nil, fmt.Errorf("request failed: %w", err) +// } +// var fsigs vm.FunctionSignatures +// amino.MustUnmarshalJSON(res.Data, &fsigs) +// // Fill fsigs with query parameters. +// // for i := range fsigs { +// // fsig := &(fsigs[i]) +// // for j := range fsig.Params { +// // param := &(fsig.Params[j]) +// // value := query.Get(param.Name) +// // param.Value = value +// // } +// // } +// return fsigs, nil +// } + +// type makeCallCfg struct { +// kb keys.Keybase +// pass string +// rlmpath string +// eval string +// nameOrBech32 string +// } + +// // gnokey maketx call -pkgpath "gno.land/r/dev/hello" -func "Inc" -gas-fee 1000000ugnot -gas-wanted 2000000 -send "" -broadcast -chainid "tendermint-test" -remote "http://127.0.0.1:36657" g1jg8mtut +// func makeCall(logger log.Logger, makecfg makeCallCfg) ([]byte, error) { +// var cfg callCfg + +// var err error +// cfg.funcName, cfg.args, err = parseMethodToArgs(makecfg.eval) +// if err != nil { +// return nil, err +// } + +// if len(cfg.args) == 0 { +// cfg.args = nil +// } + +// cfg.gasFee = "1000000ugnot" +// cfg.gasWanted = 2000000 +// cfg.send = "" +// cfg.broadcast = true +// cfg.chainID = "tendermint_test" +// cfg.remote = remoteAddr +// cfg.pkgPath = makecfg.rlmpath +// cfg.kb = makecfg.kb + +// res, err := execCall(makecfg.nameOrBech32, makecfg.pass, cfg) +// if err != nil { +// return nil, err +// } + +// return res.DeliverTx.Data, nil +// } From 91ebebaa7169543bdd76029a1a542ae76c860e8a Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:33:34 +0200 Subject: [PATCH 03/64] wip: gnobro Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/main.go | 126 +++++++++++++++++++------- contribs/gnobro/cmd/gnobro/model.go | 40 +++++--- contribs/gnobro/cmd/gnobro/request.go | 14 ++- contribs/gnobro/go.mod | 10 +- contribs/gnobro/go.sum | 10 ++ 5 files changed, 147 insertions(+), 53 deletions(-) diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index 1039bb43b2e..fca162acc01 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -10,8 +10,11 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/gnolang/gno/gno.land/pkg/gnoclient" + "github.com/gnolang/gno/gno.land/pkg/integration" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" zone "github.com/lrstanley/bubblezone" ) @@ -56,10 +59,29 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { func execBrowser(cfg *broCfg, args []string, io commands.IO) error { m := zone.New() - // if len(os.Args) != 2 { - // fmt.Fprintln(os.Stderr, "no nameOrBench32 given") - // return - // } + home := gnoenv.HomeDir() + + var address string + var kb keys.Keybase + if len(args) > 0 && args[0] != "" { + address = args[0] + + var err error + kb, err = keys.NewKeyBaseFromDir(home) + if err != nil { + return fmt.Errorf("unable to load keybase: %w", err) + } + } else { + // create a inmemory keybase + kb = keys.NewInMemory() + kb.CreateAccount(integration.DefaultAccount_Name, integration.DefaultAccount_Seed, "", "", 0, 0) + address = integration.DefaultAccount_Name + } + + signer, err := getSignerForAccount(io, address, kb, cfg) + if err != nil { + return fmt.Errorf("unable to get signer for account %q: %w", address, err) + } remoteAddr := resolveUnixOrTCPAddr(cfg.target) cl, err := client.NewHTTPClient(remoteAddr) @@ -67,40 +89,32 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { return fmt.Errorf("unable to create http client for %q: %w", remoteAddr, err) } - broclient := gnoclient.Client{ - RPCClient: cl, + broclient := &BroClient{ + client: &gnoclient.Client{ + RPCClient: cl, + Signer: signer, + }, + base: gnoclient.BaseTxCfg{ + GasFee: "1000000ugnot", + GasWanted: 2000000, + AccountNumber: 1, + SequenceNumber: 1, + }, } - // name := os.Args[1] - - // kb, err := keys.NewKeyBaseFromDir(gnoenv.HomeDir()) - // if err != nil { - // panic("unable to load keybase: " + err.Error()) - // } - - // if ok, err := kb.HasByNameOrAddress(name); !ok || err != nil { - // if err != nil { - // panic("invalid name: " + err.Error()) - // } - - // panic("unknown name/address: " + name) - // } - - // fmt.Printf("[%s] Enter password: ", name) - - // password, err := terminal.ReadPassword(0) - // if err != nil { - // panic("error while reading password: " + err.Error()) - // } - - // if _, err := kb.ExportPrivKeyUnsafe(name, string(password)); err != nil { - // panic("invalid password: " + err.Error()) - // } + res, err := broclient.Call("gno.land/r/dev/counter", "Inc()") + if err != nil { + return fmt.Errorf("cal error: %+v", err) + } + fmt.Println(res) + if res != nil { + os.Exit(1) + } cmd := initCommandInput() p := tea.NewProgram( model{ - client: &BroClient{client: &broclient}, + client: broclient, listFuncs: newFuncList(), urlInput: initURLInput(), commandInput: cmd, @@ -117,6 +131,54 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { return nil } +// func execApi(cfg *apiCfg, args []string, io commands.IO) error { +// logger := log.ZapLoggerToSlog(log.NewZapConsoleLogger(io.Out(), zapcore.DebugLevel)) + +// var server http.Server +// server.ReadHeaderTimeout = 60 * time.Second +// server.Handler = proxycl + +// l, err := net.Listen("tcp", cfg.listener) +// if err != nil { +// return fmt.Errorf("unable to listen on %q: %w", cfg.listener, err) +// } +// logger.Info("api listening", "addr", l.Addr()) + +// return server.Serve(l) +// } + +func getSignerForAccount(io commands.IO, address string, kb keys.Keybase, cfg *broCfg) (gnoclient.Signer, error) { + var signer gnoclient.SignerFromKeybase + + signer.Keybase = kb + signer.Account = address + signer.ChainID = cfg.chainID // XXX: override this + // ChainID: chainid, // Chain ID for transaction signing + + if ok, err := kb.HasByNameOrAddress(address); !ok || err != nil { + if err != nil { + return nil, fmt.Errorf("invalid name: %w", err) + } + + return nil, fmt.Errorf("unknown name/address: %q", address) + } + + // try empty password first + if _, err := kb.ExportPrivKeyUnsafe(address, ""); err != nil { + prompt := fmt.Sprintf("[%.10s] Enter password:", address) + signer.Password, err = io.GetPassword(prompt, true) + if err != nil { + return nil, fmt.Errorf("error while reading password: %w", err) + } + + if _, err := kb.ExportPrivKeyUnsafe(address, string(signer.Password)); err != nil { + return nil, fmt.Errorf("invalid password: %w", err) + } + } + + return signer, nil +} + func resolveUnixOrTCPAddr(in string) (out string) { var err error var addr net.Addr diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index 3f365c598dd..2f6ae419cff 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -13,6 +13,7 @@ import ( zone "github.com/lrstanley/bubblezone" "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/errors" "github.com/muesli/reflow/wordwrap" ) @@ -192,12 +193,26 @@ func (m *model) RenderUpdate() { m.listFuncs.SetSize(m.viewport.Width, 7) } -func (m *model) ExtendCommandInput() { +func (m *model) ExtendCommandInput() bool { if m.commandInput.Focused() { if item, ok := m.listFuncs.SelectedItem().(itemFunc); ok { - m.commandInput.SetValue(item.Title()) + var value string + if len(item.Params) > 0 { + value = item.Title() + "(" + } else { + value = item.Title() + "()" + } + + currentValue := m.commandInput.Value() + if len(value) > len(currentValue) && strings.HasPrefix(value, currentValue) { + m.commandInput.SetValue(value) + return true + } + } } + + return false } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { @@ -226,13 +241,21 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: switch msg.String() { - case "alt+down": + case "alt+down", "alt+up", "tab": if m.urlInput.Focused() { m.urlInput.Blur() cmds = append(cmds, m.commandInput.Focus()) m.commandFocus = true + } else if m.commandInput.Focused() { + if msg.String() == "tab" && m.ExtendCommandInput() { + break + } else { + m.commandInput.Blur() + cmds = append(cmds, m.urlInput.Focus()) + m.commandFocus = false + } } - case "down", "tab": + case "down": if m.commandFocus { m.listFuncs.CursorDown() } @@ -240,12 +263,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.commandFocus { m.listFuncs.CursorUp() } - case "alt+up": - if m.commandInput.Focused() { - m.commandInput.Blur() - cmds = append(cmds, m.urlInput.Focus()) - m.commandFocus = false - } case "alt+r": m.RenderUpdate() case "enter": @@ -258,9 +275,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { path := m.urlInput.Value() rlmpath := gnoPrefix + path res, err := m.client.Call(rlmpath, m.commandInput.Value()) - if err != nil { - content := fmt.Sprintf("%s\n\npress [enter] to dismiss", m.getError(err)) + content := fmt.Sprintf("%s\n\npress [enter] to dismiss", m.getError(errors.Cause(err))) m.viewport.SetContent(content) } else { if strings.TrimSpace(string(res)) == "" { diff --git a/contribs/gnobro/cmd/gnobro/request.go b/contribs/gnobro/cmd/gnobro/request.go index c934dd4bcdc..74c05eb433e 100644 --- a/contribs/gnobro/cmd/gnobro/request.go +++ b/contribs/gnobro/cmd/gnobro/request.go @@ -21,16 +21,11 @@ var ( ) type BroClient struct { + base gnoclient.BaseTxCfg client *gnoclient.Client log *slog.Logger } -type CallCfg struct { - rlmpath string - eval string - nameOrBech32 string -} - // gnokey maketx call -pkgpath "gno.land/r/dev/hello" -func "Inc" -gas-fee 1000000ugnot -gas-wanted 2000000 -send "" -broadcast -chainid "tendermint-test" -remote "http://127.0.0.1:36657" g1jg8mtut func (bl *BroClient) Call(path, call string) ([]byte, error) { method, args, err := parseMethodToArgs(call) @@ -38,8 +33,11 @@ func (bl *BroClient) Call(path, call string) ([]byte, error) { return nil, fmt.Errorf("unable to parse method/args: %w", err) } - base := gnoclient.BaseTxCfg{} - cm, err := bl.client.Call(base, gnoclient.MsgCall{ + if len(args) == 0 { + args = nil + } + + cm, err := bl.client.Call(bl.base, gnoclient.MsgCall{ PkgPath: path, FuncName: method, Args: args, diff --git a/contribs/gnobro/go.mod b/contribs/gnobro/go.mod index be89c51ec9e..3acbc7e8193 100644 --- a/contribs/gnobro/go.mod +++ b/contribs/gnobro/go.mod @@ -13,10 +13,10 @@ require ( github.com/lrstanley/bubblezone v0.0.0-20240125042004-b7bafc493195 github.com/muesli/reflow v0.3.0 github.com/sahilm/fuzzy v0.1.1 - golang.org/x/crypto v0.21.0 ) require ( + dario.cat/mergo v1.0.0 // indirect github.com/alecthomas/chroma/v2 v2.8.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect @@ -49,10 +49,13 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.15.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect github.com/peterbourgon/ff/v3 v3.4.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/cors v1.10.1 // indirect github.com/rs/xid v1.5.0 // indirect github.com/stretchr/testify v1.9.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect @@ -60,6 +63,7 @@ require ( github.com/yuin/goldmark-emoji v1.0.2 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect + go.etcd.io/bbolt v1.3.9 // indirect go.opentelemetry.io/otel v1.25.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0 // indirect go.opentelemetry.io/otel/metric v1.25.0 // indirect @@ -68,6 +72,9 @@ require ( go.opentelemetry.io/otel/trace v1.25.0 // indirect go.opentelemetry.io/proto/otlp v1.1.0 // indirect go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap/exp v0.2.0 // indirect + golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect golang.org/x/mod v0.16.0 // indirect golang.org/x/net v0.23.0 // indirect @@ -75,6 +82,7 @@ require ( golang.org/x/sys v0.20.0 // indirect golang.org/x/term v0.20.0 // indirect golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.19.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect google.golang.org/grpc v1.63.0 // indirect diff --git a/contribs/gnobro/go.sum b/contribs/gnobro/go.sum index 6fec22d41d0..78d1ec3c8be 100644 --- a/contribs/gnobro/go.sum +++ b/contribs/gnobro/go.sum @@ -173,6 +173,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= @@ -208,8 +210,14 @@ go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1 go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs= +go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -250,6 +258,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From c24fca4f2dadbf766c072e6eb533bec7365752a1 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:57:03 +0200 Subject: [PATCH 04/64] feat: make gno link clickable Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/main.go | 71 +++++------- contribs/gnobro/cmd/gnobro/model.go | 84 +++++++++++--- contribs/gnobro/cmd/gnobro/request.go | 155 ++------------------------ 3 files changed, 108 insertions(+), 202 deletions(-) diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index fca162acc01..e152bcfb022 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -15,6 +15,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/log" zone "github.com/lrstanley/bubblezone" ) @@ -55,12 +56,19 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { "target gnoland address", ) + fs.StringVar( + &c.chainID, + "chainid", + defaultBroOptions.chainID, + "chainid", + ) + } func execBrowser(cfg *broCfg, args []string, io commands.IO) error { - m := zone.New() home := gnoenv.HomeDir() + logger := log.NewNoopLogger() var address string var kb keys.Keybase if len(args) > 0 && args[0] != "" { @@ -89,37 +97,28 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { return fmt.Errorf("unable to create http client for %q: %w", remoteAddr, err) } - broclient := &BroClient{ - client: &gnoclient.Client{ - RPCClient: cl, - Signer: signer, - }, - base: gnoclient.BaseTxCfg{ - GasFee: "1000000ugnot", - GasWanted: 2000000, - AccountNumber: 1, - SequenceNumber: 1, - }, - } - - res, err := broclient.Call("gno.land/r/dev/counter", "Inc()") - if err != nil { - return fmt.Errorf("cal error: %+v", err) + gnocl := &gnoclient.Client{ + RPCClient: cl, + Signer: signer, } - fmt.Println(res) - if res != nil { - os.Exit(1) + base := gnoclient.BaseTxCfg{ + GasFee: "1000000ugnot", + GasWanted: 2000000, } + broclient := NewBroClient(logger, base, gnocl) + cmd := initCommandInput() - p := tea.NewProgram( - model{ - client: broclient, - listFuncs: newFuncList(), - urlInput: initURLInput(), - commandInput: cmd, - zone: m, - }, + mod := model{ + client: broclient, + listFuncs: newFuncList(), + urlInput: initURLInput(), + commandInput: cmd, + zone: zone.New(), + pageurls: map[string]string{}, + } + + p := tea.NewProgram(mod, tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel ) @@ -131,22 +130,6 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { return nil } -// func execApi(cfg *apiCfg, args []string, io commands.IO) error { -// logger := log.ZapLoggerToSlog(log.NewZapConsoleLogger(io.Out(), zapcore.DebugLevel)) - -// var server http.Server -// server.ReadHeaderTimeout = 60 * time.Second -// server.Handler = proxycl - -// l, err := net.Listen("tcp", cfg.listener) -// if err != nil { -// return fmt.Errorf("unable to listen on %q: %w", cfg.listener, err) -// } -// logger.Info("api listening", "addr", l.Addr()) - -// return server.Serve(l) -// } - func getSignerForAccount(io commands.IO, address string, kb keys.Keybase, cfg *broCfg) (gnoclient.Signer, error) { var signer gnoclient.SignerFromKeybase diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index 2f6ae419cff..959d57eb8d5 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -1,7 +1,9 @@ package main import ( + "bytes" "fmt" + "regexp" "strings" "github.com/charmbracelet/bubbles/list" @@ -13,13 +15,12 @@ import ( zone "github.com/lrstanley/bubblezone" "github.com/gnolang/gno/tm2/pkg/crypto/keys" - "github.com/gnolang/gno/tm2/pkg/errors" "github.com/muesli/reflow/wordwrap" ) // var noopLogger = log.NewNopLogger() -const gnoPrefix = "gno.land/r/" +const gnoPrefix = "gno.land/" // You generally won't need this unless you're processing stuff with // complicated ANSI escape sequences. Turn it on if you notice flickering. @@ -74,7 +75,7 @@ func initURLInput() textinput.Model { ti.CharLimit = 156 ti.PromptStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("#FF06B7")) - ti.Prompt = "gno.land/r/" + ti.Prompt = gnoPrefix return ti } @@ -106,6 +107,7 @@ type model struct { viewport viewport.Model height int + pageurls map[string]string messageDisplay bool } @@ -113,6 +115,37 @@ func (m model) Init() tea.Cmd { return nil } +var urlPattern = regexp.MustCompile(`(?m)/(?:p|r)/[^[;\s]+`) + +func (m model) FindAndMarkURLs(body []byte) []byte { + var buf bytes.Buffer + lastIndex := 0 + + indexes := urlPattern.FindAllIndex(body, -1) + for i, loc := range indexes { + markid := fmt.Sprintf("url_%d", i) + + // Write bytes before match + buf.Write(body[lastIndex:loc[0]]) + + // Write quoted URL + u := string(body[loc[0]:loc[1]]) + buf.WriteString(m.zone.Mark(markid, u)) + m.pageurls[markid] = u + lastIndex = loc[1] + } + // Write remaining bytes + buf.Write(body[lastIndex:]) + + // Cleanup previous urls + for i := len(indexes); i < len(m.pageurls); i++ { + markid := fmt.Sprintf("url_%d", i) + delete(m.pageurls, markid) + } + + return buf.Bytes() +} + func (m model) fetchRenderView() (view []byte, err error) { path := m.urlInput.Value() rlmpath := gnoPrefix + path @@ -122,6 +155,7 @@ func (m model) fetchRenderView() (view []byte, err error) { if err != nil { return nil, fmt.Errorf("unable to fetch Render: %w", err) } + r, err := glamour.NewTermRenderer( glamour.WithAutoStyle(), glamour.WithWordWrap(m.viewport.Width), @@ -136,13 +170,14 @@ func (m model) fetchRenderView() (view []byte, err error) { return nil, fmt.Errorf("uanble to render markdown view: %w", err) } - return view, nil + return m.FindAndMarkURLs(view), nil } func (m model) fetchFuncsList() (view []list.Item, err error) { path := m.urlInput.Value() rlmpath := gnoPrefix + path + rlmpath, _, _ = strings.Cut(rlmpath, ":") funcs, err := m.client.Funcs(rlmpath) if err != nil { return nil, fmt.Errorf("unable to fetch Render: %w", err) @@ -160,7 +195,7 @@ func (m model) fetchFuncsList() (view []list.Item, err error) { func (m model) getError(err error) string { f := wordwrap.NewWriter(m.viewport.Width) - fmt.Fprintf(f, "error: %s", err) + fmt.Fprintf(f, "error: %v", err) serr := f.String() return serr } @@ -223,22 +258,36 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.MouseMsg: - if msg.Type != tea.MouseLeft { - return m, nil + if msg.Button == tea.MouseButtonWheelUp || msg.Button == tea.MouseButtonWheelDown || + msg.Button == tea.MouseButtonWheelLeft || msg.Button == tea.MouseButtonWheelRight { + m.viewport, cmd = m.viewport.Update(msg) + return m, cmd // stop here to avoid update input view } if m.zone.Get("url_input").InBounds(msg) { m.commandInput.Blur() cmds = append(cmds, m.urlInput.Focus()) m.commandFocus = false - } - - if m.zone.Get("command_input").InBounds(msg) { + } else if m.zone.Get("command_input").InBounds(msg) { m.urlInput.Blur() cmds = append(cmds, m.commandInput.Focus()) m.commandFocus = true - } + } else { + for mark := range m.pageurls { + if !m.zone.Get(mark).InBounds(msg) { + continue + } + if uri := m.pageurls[mark]; uri != "" { + uri = strings.TrimPrefix(uri, "/") + m.urlInput.SetValue(uri) + m.urlInput.CursorEnd() + m.RenderUpdate() + } + + break + } + } case tea.KeyMsg: switch msg.String() { case "alt+down", "alt+up", "tab": @@ -276,17 +325,20 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { rlmpath := gnoPrefix + path res, err := m.client.Call(rlmpath, m.commandInput.Value()) if err != nil { - content := fmt.Sprintf("%s\n\npress [enter] to dismiss", m.getError(errors.Cause(err))) + content := fmt.Sprintf("%s\n\npress [enter] to dismiss", m.getError(err)) m.viewport.SetContent(content) + m.messageDisplay = true } else { if strings.TrimSpace(string(res)) == "" { - break + m.RenderUpdate() + m.messageDisplay = false + } else { + m.viewport.SetContent(fmt.Sprintf("%s\n\npress [enter] to dismiss", string(res))) + m.messageDisplay = true } - m.viewport.SetContent(fmt.Sprintf("%s\n\npress [enter] to dismiss", string(res))) } m.listFuncs.Erase() - m.messageDisplay = true break } @@ -321,6 +373,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // here. m.viewport = viewport.New(msg.Width, msg.Height-verticalMarginHeight) m.viewport.YPosition = headerHeight + m.viewport.MouseWheelEnabled = true + m.viewport.MouseWheelDelta = 1 m.viewport.HighPerformanceRendering = useHighPerformanceRenderer m.ready = true diff --git a/contribs/gnobro/cmd/gnobro/request.go b/contribs/gnobro/cmd/gnobro/request.go index 74c05eb433e..693caffcd2e 100644 --- a/contribs/gnobro/cmd/gnobro/request.go +++ b/contribs/gnobro/cmd/gnobro/request.go @@ -23,7 +23,15 @@ var ( type BroClient struct { base gnoclient.BaseTxCfg client *gnoclient.Client - log *slog.Logger + logger *slog.Logger +} + +func NewBroClient(logger *slog.Logger, base gnoclient.BaseTxCfg, client *gnoclient.Client) *BroClient { + return &BroClient{ + base: base, + client: client, + logger: logger, + } } // gnokey maketx call -pkgpath "gno.land/r/dev/hello" -func "Inc" -gas-fee 1000000ugnot -gas-wanted 2000000 -send "" -broadcast -chainid "tendermint-test" -remote "http://127.0.0.1:36657" g1jg8mtut @@ -56,32 +64,6 @@ func (bl *BroClient) Call(path, call string) ([]byte, error) { } return cm.DeliverTx.Data, nil - - // var err error - // cfg.funcName, cfg.args, err = parseMethodToArgs(makecfg.eval) - // if err != nil { - // return nil, err - // } - - // if len(cfg.args) == 0 { - // cfg.args = nil - // } - - // cfg.gasFee = "1000000ugnot" - // cfg.gasWanted = 2000000 - // cfg.send = "" - // cfg.broadcast = true - // cfg.chainID = "tendermint_test" - // cfg.remote = remoteAddr - // cfg.pkgPath = makecfg.rlmpath - // cfg.kb = makecfg.kb - - // res, err := execCall(makecfg.nameOrBech32, makecfg.pass, cfg) - // if err != nil { - // return nil, err - // } - - // return res.DeliverTx.Data, nil } func (bl *BroClient) Funcs(path string) (vm.FunctionSignatures, error) { @@ -99,15 +81,6 @@ func (bl *BroClient) Funcs(path string) (vm.FunctionSignatures, error) { var fsigs vm.FunctionSignatures amino.MustUnmarshalJSON(res.Response.Data, &fsigs) - // Fill fsigs with query parameters. - // for i := range fsigs { - // fsig := &(fsigs[i]) - // for j := range fsig.Params { - // param := &(fsig.Params[j]) - // value := query.Get(param.Name) - // param.Value = value - // } - // } return fsigs, nil } @@ -134,6 +107,9 @@ func parseMethodToArgs(call string) (method string, args []string, err error) { method = matches[1] sargs := matches[2] + if sargs == "" { + return + } // Splitting arguments by comma args = strings.Split(sargs, ",") @@ -142,110 +118,3 @@ func parseMethodToArgs(call string) (method string, args []string, err error) { } return } - -// type Request struct { -// RemoteAddr string -// QPath string -// Data []byte -// } - -// func makeRequest(log log.Logger, req Request) (res *abci.ResponseQuery, err error) { -// opts2 := client.ABCIQueryOptions{ -// // Height: height, XXX -// // Prove: false, XXX -// } -// remote := req.RemoteAddr -// cli := client.NewHTTP(remote, "/websocket") -// qres, err := cli.ABCIQueryWithOptions( -// req.QPath, req.Data, opts2) -// if err != nil { -// log.Error("request error", "path", req.QPath, "error", err) -// return nil, fmt.Errorf("unable to query path %q: %w", req.QPath, err) -// } -// if qres.Response.Error != nil { -// log.Error("response error", "path", req.QPath, "log", qres.Response.Log) -// return nil, fmt.Errorf("response error: %s\n\n%s\n", qres.Response.Error, qres.Response.Log) -// } -// return &qres.Response, nil -// } - -// func makeRender(logger log.Logger, rlmpath string) ([]byte, error) { -// var req Request -// req.RemoteAddr = remoteAddr - -// req.Data = []byte(fmt.Sprintf("%s\n%s", rlmpath, "")) -// req.QPath = "vm/qrender" -// res, err := makeRequest(logger, req) -// if err != nil { -// if strings.Contains(err.Error(), "Render not declared") { -// return nil, ErrRenderNotFound -// } - -// return nil, fmt.Errorf("unable to make request on %q: %w", rlmpath, err) -// } - -// return res.Data, nil -// } - -// func makeFuncs(logger log.Logger, rlmpath string) (vm.FunctionSignatures, error) { -// var req Request -// req.RemoteAddr = remoteAddr - -// req.QPath = "vm/qfuncs" -// req.Data = []byte(rlmpath) -// res, err := makeRequest(logger, req) -// if err != nil { -// return nil, fmt.Errorf("request failed: %w", err) -// } -// var fsigs vm.FunctionSignatures -// amino.MustUnmarshalJSON(res.Data, &fsigs) -// // Fill fsigs with query parameters. -// // for i := range fsigs { -// // fsig := &(fsigs[i]) -// // for j := range fsig.Params { -// // param := &(fsig.Params[j]) -// // value := query.Get(param.Name) -// // param.Value = value -// // } -// // } -// return fsigs, nil -// } - -// type makeCallCfg struct { -// kb keys.Keybase -// pass string -// rlmpath string -// eval string -// nameOrBech32 string -// } - -// // gnokey maketx call -pkgpath "gno.land/r/dev/hello" -func "Inc" -gas-fee 1000000ugnot -gas-wanted 2000000 -send "" -broadcast -chainid "tendermint-test" -remote "http://127.0.0.1:36657" g1jg8mtut -// func makeCall(logger log.Logger, makecfg makeCallCfg) ([]byte, error) { -// var cfg callCfg - -// var err error -// cfg.funcName, cfg.args, err = parseMethodToArgs(makecfg.eval) -// if err != nil { -// return nil, err -// } - -// if len(cfg.args) == 0 { -// cfg.args = nil -// } - -// cfg.gasFee = "1000000ugnot" -// cfg.gasWanted = 2000000 -// cfg.send = "" -// cfg.broadcast = true -// cfg.chainID = "tendermint_test" -// cfg.remote = remoteAddr -// cfg.pkgPath = makecfg.rlmpath -// cfg.kb = makecfg.kb - -// res, err := execCall(makecfg.nameOrBech32, makecfg.pass, cfg) -// if err != nil { -// return nil, err -// } - -// return res.DeliverTx.Data, nil -// } From 390f687ff0adb8148c65740ae92413cdebcd9e43 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 20 Jun 2024 17:51:48 +0200 Subject: [PATCH 05/64] feat: add ssh support Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/main.go | 99 +++++++++++++++++++++++++---- contribs/gnobro/cmd/gnobro/model.go | 41 ++++++------ contribs/gnobro/go.mod | 9 +++ contribs/gnobro/go.sum | 18 ++++++ 4 files changed, 131 insertions(+), 36 deletions(-) diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index e152bcfb022..b63054a62cd 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -2,31 +2,45 @@ package main import ( "context" + "errors" "flag" "fmt" + "log/slog" "net" "os" + "os/signal" "strings" + "syscall" + "time" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + charmlog "github.com/charmbracelet/log" + "github.com/charmbracelet/ssh" + "github.com/charmbracelet/wish" + "github.com/charmbracelet/wish/activeterm" + "github.com/charmbracelet/wish/bubbletea" + "github.com/charmbracelet/wish/logging" "github.com/gnolang/gno/gno.land/pkg/gnoclient" "github.com/gnolang/gno/gno.land/pkg/integration" "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys" - "github.com/gnolang/gno/tm2/pkg/log" + tmlog "github.com/gnolang/gno/tm2/pkg/log" zone "github.com/lrstanley/bubblezone" ) type broCfg struct { - target string - chainID string + target string + chainID string + sshListener string } var defaultBroOptions = broCfg{ - target: "127.0.0.1:26657", - chainID: "dev", + target: "127.0.0.1:26657", + sshListener: "", + chainID: "dev", } func main() { @@ -36,7 +50,7 @@ func main() { cmd := commands.NewCommand( commands.Metadata{ Name: "gnobro", - ShortUsage: "gnobro [flags] [path ...]", + ShortUsage: "gnobro [flags]", ShortHelp: "runs a cli browser.", LongHelp: `run a cli browser`, }, @@ -63,12 +77,19 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { "chainid", ) + fs.StringVar( + &c.sshListener, + "ssh", + defaultBroOptions.sshListener, + "ssh server listener address", + ) + } func execBrowser(cfg *broCfg, args []string, io commands.IO) error { home := gnoenv.HomeDir() - logger := log.NewNoopLogger() + logger := tmlog.NewNoopLogger() var address string var kb keys.Keybase if len(args) > 0 && args[0] != "" { @@ -108,23 +129,73 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { broclient := NewBroClient(logger, base, gnocl) - cmd := initCommandInput() + render := lipgloss.DefaultRenderer() + + cmd := initCommandInput(render) mod := model{ + render: render, client: broclient, listFuncs: newFuncList(), - urlInput: initURLInput(), + urlInput: initURLInput(render), commandInput: cmd, zone: zone.New(), pageurls: map[string]string{}, } - p := tea.NewProgram(mod, - tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" - tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel + if cfg.sshListener == "" { + p := tea.NewProgram(mod, + tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" + tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel + ) + + if _, err := p.Run(); err != nil { + return fmt.Errorf("could not run program: %w", err) + } + + return nil + } + + teaHandler := func(s ssh.Session) (tea.Model, []tea.ProgramOption) { + model := mod + model.render = bubbletea.MakeRenderer(s) + return model, []tea.ProgramOption{ + tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" + tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel + } + } + + sshaddr, err := net.ResolveTCPAddr("", cfg.sshListener) + if err != nil { + return fmt.Errorf("unable to resolve address: %w", err) + } + + logger = slog.New(charmlog.New(io.Out())) + s, err := wish.NewServer( + wish.WithAddress(sshaddr.String()), + wish.WithHostKeyPath(".ssh/id_ed25519"), + wish.WithMiddleware( + bubbletea.Middleware(teaHandler), + activeterm.Middleware(), // Bubble Tea apps usually require a PTY. + logging.Middleware(), + ), ) - if _, err := p.Run(); err != nil { - return fmt.Errorf("could not run program: %w", err) + done := make(chan os.Signal, 1) + signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + logger.Info("Starting SSH server", "addr", sshaddr.String()) + go func() { + if err = s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) { + logger.Error("Could not start server", "error", err) + done <- nil + } + }() + + <-done + logger.Info("Stopping SSH server") + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer func() { cancel() }() + if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) { + logger.Error("Could not stop server", "error", err) } return nil diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index 959d57eb8d5..b858158de29 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -31,33 +31,28 @@ const gnoPrefix = "gno.land/" const useHighPerformanceRenderer = false var ( - boxRoundedStyle = func() lipgloss.Style { + boxRoundedStyle = func(r *lipgloss.Renderer) lipgloss.Style { b := lipgloss.RoundedBorder() - return lipgloss.NewStyle(). + return r.NewStyle(). BorderStyle(b). Padding(0, 2) - }() + } - inputStyleLeft = func() lipgloss.Style { + inputStyleLeft = func(r *lipgloss.Renderer) lipgloss.Style { b := lipgloss.RoundedBorder() b.Right = "β”œ" - return lipgloss.NewStyle(). + return r.NewStyle(). BorderStyle(b). Padding(0, 2) - }() + } - infoStyle = func() lipgloss.Style { + infoStyle = func(r *lipgloss.Renderer) lipgloss.Style { b := lipgloss.RoundedBorder() b.Left = "─" - return boxRoundedStyle.Copy().BorderStyle(b) - }() + return boxRoundedStyle(r).Copy().BorderStyle(b) + } ) -// func (i itemFunc) Description() string { -// var str strings -// return i.Params -// } - type modelFunc struct { textInput textinput.Model err error @@ -68,23 +63,23 @@ type modelInput struct { err error } -func initURLInput() textinput.Model { +func initURLInput(r *lipgloss.Renderer) textinput.Model { ti := textinput.New() ti.Placeholder = "demo/foo20" ti.Focus() ti.CharLimit = 156 - ti.PromptStyle = lipgloss.NewStyle(). + ti.PromptStyle = r.NewStyle(). Foreground(lipgloss.Color("#FF06B7")) ti.Prompt = gnoPrefix return ti } -func initCommandInput() textinput.Model { +func initCommandInput(r *lipgloss.Renderer) textinput.Model { ti := textinput.New() ti.Placeholder = "" ti.CharLimit = 156 - ti.PromptStyle = lipgloss.NewStyle(). + ti.PromptStyle = r.NewStyle(). Foreground(lipgloss.Color("#FF06B7")) ti.Prompt = "> " @@ -94,6 +89,7 @@ func initCommandInput() textinput.Model { type model struct { name, pass string kb keys.Keybase + render *lipgloss.Renderer client *BroClient @@ -250,6 +246,7 @@ func (m *model) ExtendCommandInput() bool { return false } +// XXX: it's bit messy here, need some rework func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var ( cmd tea.Cmd @@ -436,7 +433,7 @@ func (m model) View() string { } func (m model) listFuncsView() string { - return boxRoundedStyle. + return boxRoundedStyle(m.render). Render(m.listFuncs.View()) } @@ -445,14 +442,14 @@ func (m model) bodyView() string { } func (m model) headerView() string { - return m.zone.Mark("url_input", boxRoundedStyle. + return m.zone.Mark("url_input", boxRoundedStyle(m.render). Width(m.viewport.Width-2). Render(m.urlInput.View())) } func (m model) footerView() string { - info := infoStyle.Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) - command := m.zone.Mark("command_input", inputStyleLeft. + info := infoStyle(m.render).Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) + command := m.zone.Mark("command_input", inputStyleLeft(m.render). Width(m.viewport.Width-lipgloss.Width(info)-5). Render(m.commandInput.View())) line := strings.Repeat("─", 3) diff --git a/contribs/gnobro/go.mod b/contribs/gnobro/go.mod index 3acbc7e8193..36890689ab9 100644 --- a/contribs/gnobro/go.mod +++ b/contribs/gnobro/go.mod @@ -9,6 +9,7 @@ require ( github.com/charmbracelet/bubbletea v0.26.2 github.com/charmbracelet/glamour v0.7.0 github.com/charmbracelet/lipgloss v0.10.0 + github.com/charmbracelet/wish v1.4.0 github.com/gnolang/gno v0.0.0-00010101000000-000000000000 github.com/lrstanley/bubblezone v0.0.0-20240125042004-b7bafc493195 github.com/muesli/reflow v0.3.0 @@ -18,20 +19,28 @@ require ( require ( dario.cat/mergo v1.0.0 // indirect github.com/alecthomas/chroma/v2 v2.8.0 // indirect + github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect github.com/btcsuite/btcd/btcutil v1.1.5 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/charmbracelet/keygen v0.5.0 // indirect + github.com/charmbracelet/log v0.4.0 // indirect + github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917 // indirect + github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 // indirect + github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/creack/pty v1.1.21 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.4 // indirect diff --git a/contribs/gnobro/go.sum b/contribs/gnobro/go.sum index 78d1ec3c8be..f4d4f9042a8 100644 --- a/contribs/gnobro/go.sum +++ b/contribs/gnobro/go.sum @@ -7,6 +7,8 @@ github.com/alecthomas/chroma/v2 v2.8.0 h1:w9WJUjFFmHHB2e8mRpL9jjy3alYDlU0QLDezj1 github.com/alecthomas/chroma/v2 v2.8.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw= github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -46,12 +48,26 @@ github.com/charmbracelet/bubbletea v0.26.2 h1:Eeb+n75Om9gQ+I6YpbCXQRKHt5Pn4vMwus github.com/charmbracelet/bubbletea v0.26.2/go.mod h1:6I0nZ3YHUrQj7YHIHlM8RySX4ZIthTliMY+W8X8b+Gs= github.com/charmbracelet/glamour v0.7.0 h1:2BtKGZ4iVJCDfMF229EzbeR1QRKLWztO9dMtjmqZSng= github.com/charmbracelet/glamour v0.7.0/go.mod h1:jUMh5MeihljJPQbJ/wf4ldw2+yBP59+ctV36jASy7ps= +github.com/charmbracelet/keygen v0.5.0 h1:XY0fsoYiCSM9axkrU+2ziE6u6YjJulo/b9Dghnw6MZc= +github.com/charmbracelet/keygen v0.5.0/go.mod h1:DfvCgLHxZ9rJxdK0DGw3C/LkV4SgdGbnliHcObV3L+8= github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s= github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE= +github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= +github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= +github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917 h1:NZKjJ7d/pzk/AfcJYEzmF8M48JlIrrY00RR5JdDc3io= +github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917/go.mod h1:8/Ve8iGRRIGFM1kepYfRF2pEOF5Y3TEZYoJaA54228U= +github.com/charmbracelet/wish v1.4.0 h1:pL1uVP/YuYgJheHEj98teZ/n6pMYnmlZq/fcHvomrfc= +github.com/charmbracelet/wish v1.4.0/go.mod h1:ew4/MjJVfW/akEO9KmrQHQv1F7bQRGscRMrA+KtovTk= +github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 h1:3RXpZWGWTOeVXCTv0Dnzxdv/MhNUkBfEcbaTY0zrTQI= +github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= +github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd h1:HqBjkSFXXfW4IgX3TMKipWoPEN08T3Pi4SA/3DLss/U= +github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd/go.mod h1:6GZ13FjIP6eOCqWU4lqgveGnYxQo9c3qBzHPeFu4HBE= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= +github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= +github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -75,6 +91,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk= github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= From 602c8675f8c7dfab37ceb13dd440bb7346762993 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 20 Jun 2024 19:22:22 +0200 Subject: [PATCH 06/64] feat: add banner + some fixup Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/banner.txt | 24 ++++++++++++++++++ contribs/gnobro/cmd/gnobro/main.go | 8 ++++-- contribs/gnobro/cmd/gnobro/model.go | 36 +++++++++++++++++++-------- 3 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 contribs/gnobro/cmd/gnobro/banner.txt diff --git a/contribs/gnobro/cmd/gnobro/banner.txt b/contribs/gnobro/cmd/gnobro/banner.txt new file mode 100644 index 00000000000..915a4a8d01c --- /dev/null +++ b/contribs/gnobro/cmd/gnobro/banner.txt @@ -0,0 +1,24 @@ + + + + + Hello %s, welcome to + + + + + + :::::::: :::: ::: :::::::: + + :+. . :+: + :+:+: :+: :+: :+: + + +:+ :+:+:+ +:+ +:+ +:+ + :#: + +#+ +:+ +#+ + +#+ +:+ + + +#+ +#+# +#+ +#+#+# +#+ +#+ + #+# #+# #+# #+#+# #+# #+# + + ######## ### #### ######## + + + + + + ::: ::: :::: ::: ::::::::: + + :+: :+: :+: :+:+: :+: :+: :+: + +:+ +:+ +:+ :+:+:+ +:+ + +:+ +:+ + +#+ +#++:++#++: +#+ +:+ +#+ +#+ +:+ + + +#+ + +#+ +#+ +#+ +#+#+# +#+ +#+ + + #+# #+# #+# #+# #+#+# + #+# #+# + ########## ### ### ### #### ######### + + + + + +////////////////////////// press to continue ////////////////////////// diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index b63054a62cd..6ad7a896c08 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -2,6 +2,7 @@ package main import ( "context" + _ "embed" "errors" "flag" "fmt" @@ -43,6 +44,9 @@ var defaultBroOptions = broCfg{ chainID: "dev", } +//go:embed banner.txt +var banner string + func main() { cfg := &broCfg{} @@ -157,6 +161,7 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { teaHandler := func(s ssh.Session) (tea.Model, []tea.ProgramOption) { model := mod + model.banner = fmt.Sprintf(banner, s.User()) model.render = bubbletea.MakeRenderer(s) return model, []tea.ProgramOption{ tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" @@ -192,7 +197,7 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { <-done logger.Info("Stopping SSH server") - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer func() { cancel() }() if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) { logger.Error("Could not stop server", "error", err) @@ -207,7 +212,6 @@ func getSignerForAccount(io commands.IO, address string, kb keys.Keybase, cfg *b signer.Keybase = kb signer.Account = address signer.ChainID = cfg.chainID // XXX: override this - // ChainID: chainid, // Chain ID for transaction signing if ok, err := kb.HasByNameOrAddress(address); !ok || err != nil { if err != nil { diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index b858158de29..79a191e20d9 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -65,7 +65,7 @@ type modelInput struct { func initURLInput(r *lipgloss.Renderer) textinput.Model { ti := textinput.New() - ti.Placeholder = "demo/foo20" + ti.Placeholder = "r/gnoland/blog" ti.Focus() ti.CharLimit = 156 ti.PromptStyle = r.NewStyle(). @@ -87,21 +87,21 @@ func initCommandInput(r *lipgloss.Renderer) textinput.Model { } type model struct { - name, pass string - kb keys.Keybase - render *lipgloss.Renderer + kb keys.Keybase + render *lipgloss.Renderer + banner string client *BroClient urlInput textinput.Model listFuncs FuncListModel - commandInput textinput.Model - commandFocus bool - zone *zone.Manager - ready bool - viewport viewport.Model - height int + commandInput textinput.Model + commandFocus bool + zone *zone.Manager + ready bool + viewport viewport.Model + height, width int pageurls map[string]string messageDisplay bool @@ -253,6 +253,13 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmds []tea.Cmd ) + if m.banner != "" { + if _, ok := msg.(tea.KeyMsg); ok { + m.banner = "" + return m, nil + } + } + switch msg := msg.(type) { case tea.MouseMsg: if msg.Button == tea.MouseButtonWheelUp || msg.Button == tea.MouseButtonWheelDown || @@ -358,6 +365,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case tea.WindowSizeMsg: + m.width = msg.Width + headerHeight := lipgloss.Height(m.headerView()) footerHeight := lipgloss.Height(m.footerView()) verticalMarginHeight := headerHeight + footerHeight @@ -428,6 +437,13 @@ func (m model) View() string { return "\n Initializing..." } + if m.banner != "" { + banner := m.render.NewStyle(). + Border(lipgloss.HiddenBorder()). + Render(m.banner) + return lipgloss.PlaceHorizontal(m.width, lipgloss.Center, banner) + } + mainView := fmt.Sprintf("%s\n%s\n%s", m.headerView(), m.bodyView(), m.footerView()) return m.zone.Scan(mainView) } From 14890fc1df553bd54c039f12e08a46155c53ac2f Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 20 Jun 2024 23:52:12 +0200 Subject: [PATCH 07/64] feat: new banner Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/banner2.txt | 19 +++++++++++++++++++ contribs/gnobro/cmd/gnobro/main.go | 22 ++++++++++++++-------- contribs/gnobro/cmd/gnobro/model.go | 4 ++-- 3 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 contribs/gnobro/cmd/gnobro/banner2.txt diff --git a/contribs/gnobro/cmd/gnobro/banner2.txt b/contribs/gnobro/cmd/gnobro/banner2.txt new file mode 100644 index 00000000000..bb1976cff2e --- /dev/null +++ b/contribs/gnobro/cmd/gnobro/banner2.txt @@ -0,0 +1,19 @@ + . + + + . Hello %s, Welcome to + . ++ .,-:::::/ :::. :::. ... . + . ,;;-'````' `;;;;, `;;; .;;;;;;;. + [[[ [[[[[[/ [[[[[. '[[ ,[[ \[[, + "$$c. "$$ + $$$ "Y$c$$ $$$, $$$ + `Y8bo,,,o88o 888 Y88 "888,_ _,88P + . `'YMUP"YMM MMM . YM "YMMMMMP" + + . . + ::: + :::. :::. :::. :::::::-. + ;;; ;;`;; `;;;;, `;;; ;;, `';, + + [[[ ,[[ '[[, + [[[[[. '[[ `[[ [[ + $$' c$$$cc$$$c $$$ "Y$c$$ $$, $$ + o88oo,.__ 888 888, 888 Y88 888_,o8P' + """"YUMMM YMM ""` MMM + YM MMMMP"` + + . + + + press to continue diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index 6ad7a896c08..165a4a62a96 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -44,7 +44,7 @@ var defaultBroOptions = broCfg{ chainID: "dev", } -//go:embed banner.txt +//go:embed banner2.txt var banner string func main() { @@ -87,7 +87,6 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { defaultBroOptions.sshListener, "ssh server listener address", ) - } func execBrowser(cfg *broCfg, args []string, io commands.IO) error { @@ -116,8 +115,8 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { return fmt.Errorf("unable to get signer for account %q: %w", address, err) } - remoteAddr := resolveUnixOrTCPAddr(cfg.target) - cl, err := client.NewHTTPClient(remoteAddr) + // remoteAddr := resolveUnixOrTCPAddr(cfg.target) + cl, err := client.NewHTTPClient(cfg.target) if err != nil { return fmt.Errorf("unable to create http client for %q: %w", remoteAddr, err) } @@ -132,15 +131,22 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { } broclient := NewBroClient(logger, base, gnocl) + // res, err := broclient.Render("gno.land/r/gnoland/blogss", "") + // if err != nil { + // io.ErrPrintfln("error: %+v", err) + // os.Exit(1) + // } + // fmt.Println(res) - render := lipgloss.DefaultRenderer() + renderer := lipgloss.DefaultRenderer() - cmd := initCommandInput(render) + cmd := initCommandInput(renderer) mod := model{ - render: render, + // banner: banner, + render: renderer, client: broclient, listFuncs: newFuncList(), - urlInput: initURLInput(render), + urlInput: initURLInput(renderer), commandInput: cmd, zone: zone.New(), pageurls: map[string]string{}, diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index 79a191e20d9..f983239efc9 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -438,8 +438,8 @@ func (m model) View() string { } if m.banner != "" { - banner := m.render.NewStyle(). - Border(lipgloss.HiddenBorder()). + banner := m.render.NewStyle().Padding(1, 5). + Border(lipgloss.DoubleBorder(), true, false, true). Render(m.banner) return lipgloss.PlaceHorizontal(m.width, lipgloss.Center, banner) } From 253418cb92ad2c0c61f53b6495631d43c1a29b4b Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 21 Jun 2024 18:13:44 +0200 Subject: [PATCH 08/64] fix: banner offset Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/banner2.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/gnobro/cmd/gnobro/banner2.txt b/contribs/gnobro/cmd/gnobro/banner2.txt index bb1976cff2e..9a5931625ca 100644 --- a/contribs/gnobro/cmd/gnobro/banner2.txt +++ b/contribs/gnobro/cmd/gnobro/banner2.txt @@ -1,7 +1,7 @@ . + + . Hello %s, Welcome to . -+ .,-:::::/ :::. :::. ... . ++ .,-:::::/ :::. :::. ... . . ,;;-'````' `;;;;, `;;; .;;;;;;;. [[[ [[[[[[/ [[[[[. '[[ ,[[ \[[, "$$c. "$$ + $$$ "Y$c$$ $$$, $$$ From 4b415dfb781b495efaccde2c5030b13f7dc6a06d Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 21 Jun 2024 18:54:22 +0200 Subject: [PATCH 09/64] feat: add default realm Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/main.go | 29 ++++++++++++++++++++++------- contribs/gnobro/cmd/gnobro/model.go | 8 ++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index 165a4a62a96..a1f8fe1ced3 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -33,15 +33,17 @@ import ( ) type broCfg struct { - target string - chainID string - sshListener string + target string + chainID string + defaultRealm string + sshListener string } var defaultBroOptions = broCfg{ - target: "127.0.0.1:26657", - sshListener: "", - chainID: "dev", + target: "127.0.0.1:26657", + sshListener: "", + defaultRealm: "/r/gnoland/blog", + chainID: "dev", } //go:embed banner2.txt @@ -81,6 +83,13 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { "chainid", ) + fs.StringVar( + &c.defaultRealm, + "default-realm", + defaultBroOptions.defaultRealm, + "default realm to display when gnobro start", + ) + fs.StringVar( &c.sshListener, "ssh", @@ -139,6 +148,12 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { // fmt.Println(res) renderer := lipgloss.DefaultRenderer() + input := initURLInput(renderer) + if cfg.defaultRealm != "" { + path := strings.TrimLeft(cfg.defaultRealm, "gno.land") + path = strings.TrimLeft(cfg.defaultRealm, "/") + input.SetValue(path) + } cmd := initCommandInput(renderer) mod := model{ @@ -146,7 +161,7 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { render: renderer, client: broclient, listFuncs: newFuncList(), - urlInput: initURLInput(renderer), + urlInput: input, commandInput: cmd, zone: zone.New(), pageurls: map[string]string{}, diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index f983239efc9..d6db5208088 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -246,6 +246,10 @@ func (m *model) ExtendCommandInput() bool { return false } +type updateRenderMsg struct { + realmPath string +} + // XXX: it's bit messy here, need some rework func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var ( @@ -389,6 +393,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // // Render the viewport one line below the header. m.viewport.YPosition = headerHeight + 1 + + if value := m.urlInput.Value(); value != "" { + m.RenderUpdate() + } } else { m.viewport.Width = msg.Width m.viewport.Height = msg.Height - verticalMarginHeight From e6105f4bce6bf92f1eaa5d07fb1f759dd4873e5c Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 24 Jun 2024 00:36:32 +0200 Subject: [PATCH 10/64] feat: add history Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/main.go | 8 +- contribs/gnobro/cmd/gnobro/model.go | 194 ++++++++++++++++++++++++---- contribs/gnobro/go.mod | 37 +++--- contribs/gnobro/go.sum | 70 +++++----- gno.land/pkg/gnoweb/gnoweb.go | 29 +---- 5 files changed, 236 insertions(+), 102 deletions(-) diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index a1f8fe1ced3..6b7845c9043 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -1,6 +1,7 @@ package main import ( + "container/list" "context" _ "embed" "errors" @@ -42,7 +43,7 @@ type broCfg struct { var defaultBroOptions = broCfg{ target: "127.0.0.1:26657", sshListener: "", - defaultRealm: "/r/gnoland/blog", + defaultRealm: "gno.land/r/gnoland/home", chainID: "dev", } @@ -145,13 +146,11 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { // io.ErrPrintfln("error: %+v", err) // os.Exit(1) // } - // fmt.Println(res) renderer := lipgloss.DefaultRenderer() input := initURLInput(renderer) if cfg.defaultRealm != "" { - path := strings.TrimLeft(cfg.defaultRealm, "gno.land") - path = strings.TrimLeft(cfg.defaultRealm, "/") + path := strings.TrimLeft(cfg.defaultRealm, gnoPrefix) input.SetValue(path) } @@ -165,6 +164,7 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { commandInput: cmd, zone: zone.New(), pageurls: map[string]string{}, + history: list.New(), } if cfg.sshListener == "" { diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index d6db5208088..d2cd341800b 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -2,6 +2,7 @@ package main import ( "bytes" + clist "container/list" "fmt" "regexp" "strings" @@ -14,6 +15,7 @@ import ( "github.com/charmbracelet/lipgloss" zone "github.com/lrstanley/bubblezone" + "github.com/gnolang/gno/gno.land/pkg/gnoweb" "github.com/gnolang/gno/tm2/pkg/crypto/keys" "github.com/muesli/reflow/wordwrap" ) @@ -31,6 +33,18 @@ const gnoPrefix = "gno.land/" const useHighPerformanceRenderer = false var ( + navStyleEnable = func(r *lipgloss.Renderer) lipgloss.Style { + return r.NewStyle().Margin(0, 1). + Background(lipgloss.Color("#FF06B7")) + // Foreground(lipgloss.Color("201")) + } + + navStyleDisable = func(r *lipgloss.Renderer) lipgloss.Style { + return r.NewStyle().Margin(0, 1). + Background(lipgloss.Color("235")). + Foreground(lipgloss.Color("240")) + } + boxRoundedStyle = func(r *lipgloss.Renderer) lipgloss.Style { b := lipgloss.RoundedBorder() return r.NewStyle(). @@ -105,29 +119,47 @@ type model struct { pageurls map[string]string messageDisplay bool + + history *clist.List + current *clist.Element } func (m model) Init() tea.Cmd { + m.history.Init() return nil } -var urlPattern = regexp.MustCompile(`(?m)/(?:p|r)/[^[;\s]+`) +// realm path surrounded by ansi escape sequences +var reUrlPattern = regexp.MustCompile(`(?mU)\x1b[^m]*m(?:gno.land)?(/[^\s]+)\x1b[^m]*m`) + +func redirectWebPath(path string) string { + if alias, ok := gnoweb.Aliases[path]; ok { + return alias + } + + if redirect, ok := gnoweb.Redirects[path]; ok { + return redirect + } + + return path +} -func (m model) FindAndMarkURLs(body []byte) []byte { +func (m model) findAndMarkURLs(body []byte) []byte { var buf bytes.Buffer lastIndex := 0 - indexes := urlPattern.FindAllIndex(body, -1) + indexes := reUrlPattern.FindAllSubmatchIndex(body, -1) for i, loc := range indexes { + match := string(body[loc[0]:loc[1]]) + uri := string(body[loc[2]:loc[3]]) markid := fmt.Sprintf("url_%d", i) // Write bytes before match buf.Write(body[lastIndex:loc[0]]) // Write quoted URL - u := string(body[loc[0]:loc[1]]) - buf.WriteString(m.zone.Mark(markid, u)) - m.pageurls[markid] = u + buf.WriteString(m.zone.Mark(markid, match)) + m.pageurls[markid] = uri lastIndex = loc[1] } // Write remaining bytes @@ -166,7 +198,7 @@ func (m model) fetchRenderView() (view []byte, err error) { return nil, fmt.Errorf("uanble to render markdown view: %w", err) } - return m.FindAndMarkURLs(view), nil + return m.findAndMarkURLs(view), nil } func (m model) fetchFuncsList() (view []list.Item, err error) { @@ -196,6 +228,65 @@ func (m model) getError(err error) string { return serr } +func (m *model) moveToRealm(realm string) bool { + // Trim prefix + path := strings.TrimPrefix(realm, gnoPrefix) + // redirect if any well known path + path = redirectWebPath(path) + // trim any slash + path = strings.TrimPrefix(path, "/") + if path == m.urlInput.Value() { + return false + } + + // Set uri input + m.urlInput.SetValue(path) + m.urlInput.CursorEnd() + // Update render + m.RenderUpdate() + + return true +} + +func (m *model) updateHistory() { + v := m.urlInput.Value() + if m.history.Len() == 0 { + m.current = m.history.PushBack(v) + return + } + + m.current = m.history.InsertAfter(v, m.current) + if next := m.current.Next(); next != nil { + m.history.Remove(next) + } + +} + +func (m *model) moveHistoryForward() (string, bool) { + if next := m.current.Next(); next != nil { + m.current = next + return m.current.Value.(string), true + } + return "", false +} + +func (m *model) moveHistoryBackward() (string, bool) { + if prev := m.current.Prev(); prev != nil { + m.current = prev + return m.current.Value.(string), true + } + return "", false +} + +func (m *model) updateHistoryBackward(forward bool) { + v := m.urlInput.Value() + if m.history.Len() == 0 { + m.current = m.history.PushBack(v) + } else { + m.current = m.history.InsertAfter(v, m.current) + } +} + func (m *model) RenderUpdate() { var err error render, err := m.fetchRenderView() @@ -272,28 +363,43 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, cmd // stop here to avoid update input view } - if m.zone.Get("url_input").InBounds(msg) { - m.commandInput.Blur() - cmds = append(cmds, m.urlInput.Focus()) - m.commandFocus = false - } else if m.zone.Get("command_input").InBounds(msg) { - m.urlInput.Blur() - cmds = append(cmds, m.commandInput.Focus()) - m.commandFocus = true - } else { - for mark := range m.pageurls { - if !m.zone.Get(mark).InBounds(msg) { - continue + if msg.Action == tea.MouseActionRelease { + switch { + case m.zone.Get("prev_button").InBounds(msg): + if path, ok := m.moveHistoryBackward(); ok { + m.moveToRealm(path) } - - if uri := m.pageurls[mark]; uri != "" { - uri = strings.TrimPrefix(uri, "/") - m.urlInput.SetValue(uri) - m.urlInput.CursorEnd() - m.RenderUpdate() + case m.zone.Get("next_button").InBounds(msg): + if path, ok := m.moveHistoryForward(); ok { + m.moveToRealm(path) + } + case m.zone.Get("home_button").InBounds(msg): + if m.moveToRealm("gno.land/r/gnoland/home") { + m.updateHistory() } - break + case m.zone.Get("url_input").InBounds(msg): + m.commandInput.Blur() + cmds = append(cmds, m.urlInput.Focus()) + m.commandFocus = false + case m.zone.Get("command_input").InBounds(msg): + m.urlInput.Blur() + cmds = append(cmds, m.commandInput.Focus()) + m.commandFocus = true + default: + for mark := range m.pageurls { + if !m.zone.Get(mark).InBounds(msg) { + continue + } + + if uri := m.pageurls[mark]; uri != "" { + if m.moveToRealm(uri) { + m.updateHistory() + } + } + + break + } } } case tea.KeyMsg: @@ -353,6 +459,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.messageDisplay || m.urlInput.Focused() { m.listFuncs.Erase() m.RenderUpdate() + if m.current.Value.(string) != m.urlInput.Value() { + m.updateHistory() + } + m.messageDisplay = false } @@ -396,6 +506,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if value := m.urlInput.Value(); value != "" { m.RenderUpdate() + m.updateHistory() } } else { m.viewport.Width = msg.Width @@ -466,11 +577,42 @@ func (m model) bodyView() string { } func (m model) headerView() string { + return lipgloss.JoinVertical(lipgloss.Left, m.navView(), m.urlView()) +} + +func (m model) urlView() string { return m.zone.Mark("url_input", boxRoundedStyle(m.render). Width(m.viewport.Width-2). Render(m.urlInput.View())) } +func (m model) navView() string { + home := navStyleEnable(m.render).Render("[home]") + + next := "[next]" + if m.current != nil && m.current.Next() != nil { + next = navStyleEnable(m.render).Render(next) + } else { + next = navStyleDisable(m.render).Render(next) + } + + prev := "[prev]" + if m.current != nil && m.current.Prev() != nil { + prev = navStyleEnable(m.render).Render(prev) + } else { + prev = navStyleDisable(m.render).Render(prev) + } + + return m.render.NewStyle().Width(m.width-2). + Padding(0, 2).Render(lipgloss.JoinHorizontal(lipgloss.Left, + m.zone.Mark("home_button", home), + m.zone.Mark("prev_button", prev), + m.zone.Mark("next_button", next), + ), + ) + +} + func (m model) footerView() string { info := infoStyle(m.render).Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) command := m.zone.Mark("command_input", inputStyleLeft(m.render). diff --git a/contribs/gnobro/go.mod b/contribs/gnobro/go.mod index 36890689ab9..cfec3e3ea1a 100644 --- a/contribs/gnobro/go.mod +++ b/contribs/gnobro/go.mod @@ -9,6 +9,8 @@ require ( github.com/charmbracelet/bubbletea v0.26.2 github.com/charmbracelet/glamour v0.7.0 github.com/charmbracelet/lipgloss v0.10.0 + github.com/charmbracelet/log v0.4.0 + github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917 github.com/charmbracelet/wish v1.4.0 github.com/gnolang/gno v0.0.0-00010101000000-000000000000 github.com/lrstanley/bubblezone v0.0.0-20240125042004-b7bafc493195 @@ -27,8 +29,6 @@ require ( github.com/btcsuite/btcd/btcutil v1.1.5 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/charmbracelet/keygen v0.5.0 // indirect - github.com/charmbracelet/log v0.4.0 // indirect - github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917 // indirect github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 // indirect github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect @@ -46,8 +46,12 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gorilla/css v1.0.0 // indirect + github.com/gorilla/mux v1.8.1 // indirect + github.com/gorilla/securecookie v1.1.1 // indirect + github.com/gorilla/sessions v1.2.1 // indirect github.com/gorilla/websocket v1.5.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect + github.com/gotuna/gotuna v0.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.18 // indirect @@ -73,28 +77,29 @@ require ( github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.3.9 // indirect - go.opentelemetry.io/otel v1.25.0 // indirect + go.opentelemetry.io/otel v1.27.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0 // indirect - go.opentelemetry.io/otel/metric v1.25.0 // indirect - go.opentelemetry.io/otel/sdk v1.25.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.25.0 // indirect - go.opentelemetry.io/otel/trace v1.25.0 // indirect - go.opentelemetry.io/proto/otlp v1.1.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.27.0 // indirect + go.opentelemetry.io/otel/sdk v1.27.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.27.0 // indirect + go.opentelemetry.io/otel/trace v1.27.0 // indirect + go.opentelemetry.io/proto/otlp v1.2.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.uber.org/zap/exp v0.2.0 // indirect - golang.org/x/crypto v0.21.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect golang.org/x/mod v0.16.0 // indirect - golang.org/x/net v0.23.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.19.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect - google.golang.org/grpc v1.63.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/contribs/gnobro/go.sum b/contribs/gnobro/go.sum index f4d4f9042a8..454a7666d13 100644 --- a/contribs/gnobro/go.sum +++ b/contribs/gnobro/go.sum @@ -116,13 +116,23 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/csrf v1.7.0/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= +github.com/gotuna/gotuna v0.6.0 h1:N1lQKXEi/lwRp8u3sccTYLhzOffA4QasExz/1M5Riws= +github.com/gotuna/gotuna v0.6.0/go.mod h1:F/ecRt29ChB6Ycy1AFIBpBiMNK0j7Heq+gFbLWquhjc= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -214,20 +224,22 @@ github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfU github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= -go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0 h1:hDKnobznDpcdTlNzO0S/owRB8tyVr1OoeZZhDoqY+Cs= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0/go.mod h1:kUDQaUs1h8iTIHbQTk+iJRiUvSfJYMMKTtMCaiVu7B0= -go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= -go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= -go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= -go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= -go.opentelemetry.io/otel/sdk/metric v1.25.0 h1:7CiHOy08LbrxMAp4vWpbiPcklunUshVpAvGBrdDRlGw= -go.opentelemetry.io/otel/sdk/metric v1.25.0/go.mod h1:LzwoKptdbBBdYfvtGCzGwk6GWMA3aUzBOwtQpR6Nz7o= -go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= -go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= -go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= -go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= +go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= +go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -239,8 +251,8 @@ go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= @@ -250,8 +262,8 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -273,30 +285,28 @@ golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8= -google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= +google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/gno.land/pkg/gnoweb/gnoweb.go b/gno.land/pkg/gnoweb/gnoweb.go index b997de7840d..5e41765430e 100644 --- a/gno.land/pkg/gnoweb/gnoweb.go +++ b/gno.land/pkg/gnoweb/gnoweb.go @@ -79,34 +79,11 @@ func MakeApp(logger *slog.Logger, cfg Config) gotuna.App { Static: static.EmbeddedStatic, } - // realm aliases - aliases := map[string]string{ - "/": "/r/gnoland/home", - "/about": "/r/gnoland/pages:p/about", - "/gnolang": "/r/gnoland/pages:p/gnolang", - "/ecosystem": "/r/gnoland/pages:p/ecosystem", - "/partners": "/r/gnoland/pages:p/partners", - "/testnets": "/r/gnoland/pages:p/testnets", - "/start": "/r/gnoland/pages:p/start", - "/license": "/r/gnoland/pages:p/license", - "/game-of-realms": "/r/gnoland/pages:p/gor", // XXX: replace with gor realm - "/events": "/r/gnoland/events", - } - - for from, to := range aliases { + for from, to := range Aliases { app.Router.Handle(from, handlerRealmAlias(logger, app, &cfg, to)) } - // http redirects - redirects := map[string]string{ - "/r/demo/boards:gnolang/6": "/r/demo/boards:gnolang/3", // XXX: temporary - "/blog": "/r/gnoland/blog", - "/gor": "/game-of-realms", - "/grants": "/partners", - "/language": "/gnolang", - "/getting-started": "/start", - "/gophercon24": "https://docs.gno.land", - } - for from, to := range redirects { + + for from, to := range Redirects { app.Router.Handle(from, handlerRedirect(logger, app, &cfg, to)) } // realm routes From 3f3fbf41357edc5fc3cd050a2c7f6431085871c3 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 24 Jun 2024 01:04:42 +0200 Subject: [PATCH 11/64] fix: fixup nav Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/model.go | 46 ++++++++++++++++------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index d2cd341800b..8113ee3268f 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -34,14 +34,13 @@ const useHighPerformanceRenderer = false var ( navStyleEnable = func(r *lipgloss.Renderer) lipgloss.Style { - return r.NewStyle().Margin(0, 1). - Background(lipgloss.Color("#FF06B7")) + return r.NewStyle(). + Foreground(lipgloss.Color("#fab387")) // Foreground(lipgloss.Color("201")) } navStyleDisable = func(r *lipgloss.Renderer) lipgloss.Style { - return r.NewStyle().Margin(0, 1). - Background(lipgloss.Color("235")). + return r.NewStyle(). Foreground(lipgloss.Color("240")) } @@ -587,28 +586,33 @@ func (m model) urlView() string { } func (m model) navView() string { - home := navStyleEnable(m.render).Render("[home]") - - next := "[next]" - if m.current != nil && m.current.Next() != nil { - next = navStyleEnable(m.render).Render(next) - } else { - next = navStyleDisable(m.render).Render(next) - } + home := navStyleEnable(m.render).Padding(0, 1).Render("[Home]") - prev := "[prev]" + var style lipgloss.Style if m.current != nil && m.current.Prev() != nil { - prev = navStyleEnable(m.render).Render(prev) + style = navStyleEnable(m.render) } else { - prev = navStyleDisable(m.render).Render(prev) + style = navStyleDisable(m.render) } + prev := style.Margin(0, 1).Render("") + + spaceWidth := m.width / 3 + return lipgloss.JoinHorizontal(lipgloss.Left, + m.render.NewStyle().Width(spaceWidth).Padding(0, 1). + Render(lipgloss.JoinHorizontal(lipgloss.Left, + m.zone.Mark("prev_button", prev), + m.zone.Mark("next_button", next), + )), + m.render.PlaceHorizontal(spaceWidth, lipgloss.Center, "Gno.Land"), + m.render.PlaceHorizontal(spaceWidth, lipgloss.Right, + m.zone.Mark("home_button", home)), ) } From 4570f564af27ea7157fe510e6b0f8eefc98f3be6 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 24 Jun 2024 01:12:51 +0200 Subject: [PATCH 12/64] feat: add accesscontrol Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/main.go | 2 ++ contribs/gnobro/cmd/gnobro/model.go | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index 6b7845c9043..76b81fc2211 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -20,6 +20,7 @@ import ( charmlog "github.com/charmbracelet/log" "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" + "github.com/charmbracelet/wish/accesscontrol" "github.com/charmbracelet/wish/activeterm" "github.com/charmbracelet/wish/bubbletea" "github.com/charmbracelet/wish/logging" @@ -200,6 +201,7 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { wish.WithAddress(sshaddr.String()), wish.WithHostKeyPath(".ssh/id_ed25519"), wish.WithMiddleware( + accesscontrol.Middleware(), bubbletea.Middleware(teaHandler), activeterm.Middleware(), // Bubble Tea apps usually require a PTY. logging.Middleware(), diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index 8113ee3268f..2711cb37843 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -36,7 +36,6 @@ var ( navStyleEnable = func(r *lipgloss.Renderer) lipgloss.Style { return r.NewStyle(). Foreground(lipgloss.Color("#fab387")) - // Foreground(lipgloss.Color("201")) } navStyleDisable = func(r *lipgloss.Renderer) lipgloss.Style { From a46bcdb92b4f163cede05e007ab8aa6490f52e3f Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 24 Jun 2024 01:14:40 +0200 Subject: [PATCH 13/64] feat: gnoweb export alias and redirect Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- gno.land/pkg/gnoweb/alias.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 gno.land/pkg/gnoweb/alias.go diff --git a/gno.land/pkg/gnoweb/alias.go b/gno.land/pkg/gnoweb/alias.go new file mode 100644 index 00000000000..d7297ed9d5d --- /dev/null +++ b/gno.land/pkg/gnoweb/alias.go @@ -0,0 +1,26 @@ +package gnoweb + +// realm aliases +var Aliases = map[string]string{ + "/": "/r/gnoland/home", + "/about": "/r/gnoland/pages:p/about", + "/gnolang": "/r/gnoland/pages:p/gnolang", + "/ecosystem": "/r/gnoland/pages:p/ecosystem", + "/partners": "/r/gnoland/pages:p/partners", + "/testnets": "/r/gnoland/pages:p/testnets", + "/start": "/r/gnoland/pages:p/start", + "/license": "/r/gnoland/pages:p/license", + "/game-of-realms": "/r/gnoland/pages:p/gor", // XXX: replace with gor realm + "/events": "/r/gnoland/events", +} + +// http redirects +var Redirects = map[string]string{ + "/r/demo/boards:gnolang/6": "/r/demo/boards:gnolang/3", // XXX: temporary + "/blog": "/r/gnoland/blog", + "/gor": "/game-of-realms", + "/grants": "/partners", + "/language": "/gnolang", + "/getting-started": "/start", + "/gophercon24": "https://docs.gno.land", +} From 1e525ec22129e849dd8cd640522cdaa5c813faf0 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 24 Jun 2024 01:30:15 +0200 Subject: [PATCH 14/64] fix: align banner Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/main.go | 30 +++++++++++++++++++---------- contribs/gnobro/cmd/gnobro/model.go | 5 +++-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index 76b81fc2211..a1fa83d6d3e 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -35,17 +35,19 @@ import ( ) type broCfg struct { - target string - chainID string - defaultRealm string - sshListener string + target string + chainID string + defaultRealm string + sshListener string + sshHostKeyPath string } var defaultBroOptions = broCfg{ - target: "127.0.0.1:26657", - sshListener: "", - defaultRealm: "gno.land/r/gnoland/home", - chainID: "dev", + target: "127.0.0.1:26657", + sshListener: "", + defaultRealm: "gno.land/r/gnoland/home", + chainID: "dev", + sshHostKeyPath: ".ssh/id_ed25519", } //go:embed banner2.txt @@ -98,6 +100,14 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { defaultBroOptions.sshListener, "ssh server listener address", ) + + fs.StringVar( + &c.sshHostKeyPath, + "ssh-key", + defaultBroOptions.sshHostKeyPath, + "ssh host key path", + ) + } func execBrowser(cfg *broCfg, args []string, io commands.IO) error { @@ -157,7 +167,7 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { cmd := initCommandInput(renderer) mod := model{ - // banner: banner, + banner: banner, render: renderer, client: broclient, listFuncs: newFuncList(), @@ -199,7 +209,7 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { logger = slog.New(charmlog.New(io.Out())) s, err := wish.NewServer( wish.WithAddress(sshaddr.String()), - wish.WithHostKeyPath(".ssh/id_ed25519"), + wish.WithHostKeyPath(cfg.sshHostKeyPath), wish.WithMiddleware( accesscontrol.Middleware(), bubbletea.Middleware(teaHandler), diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index 2711cb37843..d981eeb0d9a 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -551,14 +551,15 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m model) View() string { if !m.ready { - return "\n Initializing..." + return "+" } if m.banner != "" { banner := m.render.NewStyle().Padding(1, 5). Border(lipgloss.DoubleBorder(), true, false, true). Render(m.banner) - return lipgloss.PlaceHorizontal(m.width, lipgloss.Center, banner) + + return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, banner) } mainView := fmt.Sprintf("%s\n%s\n%s", m.headerView(), m.bodyView(), m.footerView()) From 3602784fa2f68fefb3a53ab7b5e15449861f8578 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 24 Jun 2024 14:23:08 +0200 Subject: [PATCH 15/64] feat: update style Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/model.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index d981eeb0d9a..3361990c070 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -50,6 +50,12 @@ var ( Padding(0, 2) } + promptStyle = func(r *lipgloss.Renderer) lipgloss.Style { + return r.NewStyle(). + Bold(true). + Foreground(lipgloss.Color("#ea76cb")) + } + inputStyleLeft = func(r *lipgloss.Renderer) lipgloss.Style { b := lipgloss.RoundedBorder() b.Right = "β”œ" @@ -80,8 +86,7 @@ func initURLInput(r *lipgloss.Renderer) textinput.Model { ti.Placeholder = "r/gnoland/blog" ti.Focus() ti.CharLimit = 156 - ti.PromptStyle = r.NewStyle(). - Foreground(lipgloss.Color("#FF06B7")) + ti.PromptStyle = promptStyle(r) ti.Prompt = gnoPrefix return ti @@ -91,8 +96,7 @@ func initCommandInput(r *lipgloss.Renderer) textinput.Model { ti := textinput.New() ti.Placeholder = "" ti.CharLimit = 156 - ti.PromptStyle = r.NewStyle(). - Foreground(lipgloss.Color("#FF06B7")) + ti.PromptStyle = promptStyle(r) ti.Prompt = "> " return ti From 57b6ea6a775d583ea6ff172974a06384ce9f6947 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 25 Jun 2024 01:49:36 +0200 Subject: [PATCH 16/64] wip: gnodev events endpoint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/main.go | 57 ++++++++++++++++++++++----- contribs/gnobro/cmd/gnobro/model.go | 12 ++++-- contribs/gnobro/go.mod | 9 +++-- contribs/gnodev/pkg/emitter/server.go | 4 +- 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index a1fa83d6d3e..684c87d00d1 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -9,11 +9,12 @@ import ( "fmt" "log/slog" "net" + "net/url" "os" "os/signal" "strings" + "sync" "syscall" - "time" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" @@ -24,6 +25,7 @@ import ( "github.com/charmbracelet/wish/activeterm" "github.com/charmbracelet/wish/bubbletea" "github.com/charmbracelet/wish/logging" + "github.com/gnolang/gno/contribs/gnodev/pkg/events" "github.com/gnolang/gno/gno.land/pkg/gnoclient" "github.com/gnolang/gno/gno.land/pkg/integration" "github.com/gnolang/gno/gnovm/pkg/gnoenv" @@ -111,6 +113,9 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { } func execBrowser(cfg *broCfg, args []string, io commands.IO) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + home := gnoenv.HomeDir() logger := tmlog.NewNoopLogger() @@ -152,11 +157,6 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { } broclient := NewBroClient(logger, base, gnocl) - // res, err := broclient.Render("gno.land/r/gnoland/blogss", "") - // if err != nil { - // io.ErrPrintfln("error: %+v", err) - // os.Exit(1) - // } renderer := lipgloss.DefaultRenderer() input := initURLInput(renderer) @@ -167,7 +167,7 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { cmd := initCommandInput(renderer) mod := model{ - banner: banner, + // banner: banner, render: renderer, client: broclient, listFuncs: newFuncList(), @@ -184,10 +184,51 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel ) + addr := cfg.target + if !strings.HasPrefix(addr, "http://") && !strings.HasPrefix(addr, "https://") { + addr = "http://" + addr + } + + devpoint, err := url.Parse(addr) + if err != nil { + return fmt.Errorf("unable to construct devaddr: %w", err) + } + + host, _, _ := net.SplitHostPort(devpoint.Host) + eventsurl := fmt.Sprintf("ws://%s:8888/_events", host) + // if err := CheckEndpoint(eventsurl); err != nil { + // return fmt.Errorf("unable to check dev events endpoint: %w", err) + // } + + var wg sync.WaitGroup + if true { + var devcl DevClient + // devcl.Logger = log.ZapLoggerToSlog(log.NewZapConsoleLogger(io.Out(), zapcore.DebugLevel)) + devcl.Handler = func(typ events.Type, data any) error { + switch typ { + case events.EvtReload, events.EvtReset, events.EvtTxResult: + p.Send(UpdateRenderMsg{}) + default: + } + + return nil + } + + wg.Add(1) + go func() { + defer wg.Done() + + if err := devcl.Run(ctx, eventsurl, nil); err != nil { + io.ErrPrintfln("run error: %s", err.Error()) + } + }() + } + if _, err := p.Run(); err != nil { return fmt.Errorf("could not run program: %w", err) } + wg.Wait() return nil } @@ -230,8 +271,6 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { <-done logger.Info("Stopping SSH server") - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer func() { cancel() }() if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) { logger.Error("Could not stop server", "error", err) } diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index 3361990c070..dc273a35c22 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -132,7 +132,7 @@ func (m model) Init() tea.Cmd { } // realm path surrounded by ansi escape sequences -var reUrlPattern = regexp.MustCompile(`(?mU)\x1b[^m]*m(?:gno.land)?(/[^\s]+)\x1b[^m]*m`) +var reUrlPattern = regexp.MustCompile(`(?mU)\x1b[^m]*m(?:(?:https?://)?gno.land)?(/[^\s]+)\x1b[^m]*m`) func redirectWebPath(path string) string { if alias, ok := gnoweb.Aliases[path]; ok { @@ -261,7 +261,6 @@ func (m *model) updateHistory() { if next := m.current.Next(); next != nil { m.history.Remove(next) } - } func (m *model) moveHistoryForward() (string, bool) { @@ -339,7 +338,7 @@ func (m *model) ExtendCommandInput() bool { return false } -type updateRenderMsg struct { +type UpdateRenderMsg struct { realmPath string } @@ -358,6 +357,13 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } switch msg := msg.(type) { + case UpdateRenderMsg: + if msg.realmPath != "" { + m.moveToRealm(msg.realmPath) + } else { + m.RenderUpdate() + } + case tea.MouseMsg: if msg.Button == tea.MouseButtonWheelUp || msg.Button == tea.MouseButtonWheelDown || msg.Button == tea.MouseButtonWheelLeft || msg.Button == tea.MouseButtonWheelRight { diff --git a/contribs/gnobro/go.mod b/contribs/gnobro/go.mod index cfec3e3ea1a..45c701a5ad9 100644 --- a/contribs/gnobro/go.mod +++ b/contribs/gnobro/go.mod @@ -2,7 +2,10 @@ module github.com/gnolang/gno/contribs/gnobro go 1.21 -replace github.com/gnolang/gno => ../.. +replace ( + github.com/gnolang/gno => ../.. + github.com/gnolang/gno/contribs/gnodev => ../gnodev +) require ( github.com/charmbracelet/bubbles v0.18.0 @@ -13,6 +16,8 @@ require ( github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917 github.com/charmbracelet/wish v1.4.0 github.com/gnolang/gno v0.0.0-00010101000000-000000000000 + github.com/gnolang/gno/contribs/gnodev v0.0.0-00010101000000-000000000000 + github.com/gorilla/websocket v1.5.1 github.com/lrstanley/bubblezone v0.0.0-20240125042004-b7bafc493195 github.com/muesli/reflow v0.3.0 github.com/sahilm/fuzzy v0.1.1 @@ -38,7 +43,6 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.1 // indirect @@ -49,7 +53,6 @@ require ( github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/securecookie v1.1.1 // indirect github.com/gorilla/sessions v1.2.1 // indirect - github.com/gorilla/websocket v1.5.1 // indirect github.com/gotuna/gotuna v0.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect diff --git a/contribs/gnodev/pkg/emitter/server.go b/contribs/gnodev/pkg/emitter/server.go index e6052890095..3e32984268d 100644 --- a/contribs/gnodev/pkg/emitter/server.go +++ b/contribs/gnodev/pkg/emitter/server.go @@ -60,7 +60,7 @@ func (s *Server) Emit(evt events.Event) { go s.emit(evt) } -type eventJSON struct { +type EventJSON struct { Type events.Type `json:"type"` Data any `json:"data"` } @@ -69,7 +69,7 @@ func (s *Server) emit(evt events.Event) { s.muClients.Lock() defer s.muClients.Unlock() - jsonEvt := eventJSON{evt.Type(), evt} + jsonEvt := EventJSON{evt.Type(), evt} s.logger.Info("sending event to clients", "clients", len(s.clients), From 8774e4569c2e7a82ca8f56ee28599d039efa8f0c Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 25 Jun 2024 13:39:45 +0200 Subject: [PATCH 17/64] feat: add readonly Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/dev.go | 132 +++++++++++++++++++++++++ contribs/gnobro/cmd/gnobro/main.go | 145 ++++++++++++++++++++-------- contribs/gnobro/cmd/gnobro/model.go | 9 +- 3 files changed, 242 insertions(+), 44 deletions(-) create mode 100644 contribs/gnobro/cmd/gnobro/dev.go diff --git a/contribs/gnobro/cmd/gnobro/dev.go b/contribs/gnobro/cmd/gnobro/dev.go new file mode 100644 index 00000000000..0a4d7627286 --- /dev/null +++ b/contribs/gnobro/cmd/gnobro/dev.go @@ -0,0 +1,132 @@ +package main + +import ( + "context" + "errors" + "fmt" + "log/slog" + "net/http" + "time" + + "github.com/gnolang/gno/contribs/gnodev/pkg/emitter" + "github.com/gnolang/gno/contribs/gnodev/pkg/events" + "github.com/gnolang/gno/tm2/pkg/log" + "github.com/gorilla/websocket" +) + +var ErrHandlerNotSet = errors.New("handler not set") +var MaxElapsedTime = time.Second * 20 + +type DevClient struct { + Logger *slog.Logger + Handler func(typ events.Type, data any) error + + attempt int + conn *websocket.Conn +} + +func (c *DevClient) Run(ctx context.Context, addr string, header http.Header) error { + if c.Handler == nil { + return ErrHandlerNotSet + } + + if c.Logger == nil { + c.Logger = log.NewNoopLogger() + } + + for ctx.Err() == nil { + if err := c.dialBackoff(ctx, addr, nil); err != nil { + return err + } + + c.Logger.Info("connected to server", "addr", addr) + + err := c.handleEvents(ctx) + if err == nil { + return nil + } + + var closeError *websocket.CloseError + if errors.As(err, &closeError) { + c.Logger.Error("connection has been closed, reconnecting...", "err", closeError) + continue + } + + return fmt.Errorf("unexpected error: %w", err) + } + + return context.Cause(ctx) +} + +func (c *DevClient) dialBackoff(ctx context.Context, addr string, header http.Header) error { + dialer := websocket.DefaultDialer + backoff := time.Second + for { + var err error + + c.Logger.Debug("connecting to dev events endpoint", addr, "addr") + c.conn, _, err = dialer.DialContext(ctx, addr, header) + c.attempt++ + + if ctx.Err() != nil { + return context.Cause(ctx) + } + + if err == nil { + return nil + } + + // if fail on first attempt return an error early + if c.attempt < 1 { + return fmt.Errorf("unable to connect to dev endpoint: %w", err) + } + + switch { + case backoff > MaxElapsedTime: + backoff = MaxElapsedTime + case backoff < MaxElapsedTime: + backoff *= 2 + default: + } + + c.Logger.Info("could not connect to server", "err", err, "next_attempt", backoff) + select { + case <-ctx.Done(): + return context.Cause(ctx) + case <-time.After(backoff): + } + } +} + +func (c *DevClient) handleEvents(ctx context.Context) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + go func() { + <-ctx.Done() + c.conn.Close() + }() + + for { + var evt emitter.EventJSON + if err := c.conn.ReadJSON(&evt); err != nil { + return fmt.Errorf("unable to read json event: %w", err) + } + + if err := c.Handler(evt.Type, evt.Data); err != nil { + return fmt.Errorf("unable to handle event: %w", err) + } + } +} + +func CheckEndpoint(addr string) error { + resp, err := http.Head(addr) + if err != nil { + return fmt.Errorf("error while checking endpoint: %v", err) + } + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("endpoint returned status: %v", resp.StatusCode) + } + + return nil +} diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index 684c87d00d1..239fa4a853b 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -25,6 +25,7 @@ import ( "github.com/charmbracelet/wish/activeterm" "github.com/charmbracelet/wish/bubbletea" "github.com/charmbracelet/wish/logging" + "github.com/gnolang/gno/contribs/gnodev/pkg/events" "github.com/gnolang/gno/gno.land/pkg/gnoclient" "github.com/gnolang/gno/gno.land/pkg/integration" @@ -33,19 +34,24 @@ import ( "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys" tmlog "github.com/gnolang/gno/tm2/pkg/log" + osm "github.com/gnolang/gno/tm2/pkg/os" zone "github.com/lrstanley/bubblezone" ) type broCfg struct { - target string + readonly bool + remote string + devEndpoint string chainID string + defaultAccount string defaultRealm string sshListener string sshHostKeyPath string } var defaultBroOptions = broCfg{ - target: "127.0.0.1:26657", + remote: "127.0.0.1:26657", + devEndpoint: "", sshListener: "", defaultRealm: "gno.land/r/gnoland/home", chainID: "dev", @@ -76,9 +82,9 @@ func main() { func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.target, + &c.remote, "target", - defaultBroOptions.target, + defaultBroOptions.remote, "target gnoland address", ) @@ -89,11 +95,18 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { "chainid", ) + fs.StringVar( + &c.defaultAccount, + "account", + defaultBroOptions.defaultAccount, + "default local account to use", + ) + fs.StringVar( &c.defaultRealm, "default-realm", defaultBroOptions.defaultRealm, - "default realm to display when gnobro start", + "default realm to display when gnobro start and no argument are provided", ) fs.StringVar( @@ -110,6 +123,19 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { "ssh host key path", ) + fs.StringVar( + &c.devEndpoint, + "dev", + defaultBroOptions.devEndpoint, + "dev endpoint, if empty will be default to `ws://:8888`", + ) + + fs.BoolVar( + &c.readonly, + "readonly", + defaultBroOptions.readonly, + "readonly mode, no command allowed", + ) } func execBrowser(cfg *broCfg, args []string, io commands.IO) error { @@ -121,8 +147,8 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { logger := tmlog.NewNoopLogger() var address string var kb keys.Keybase - if len(args) > 0 && args[0] != "" { - address = args[0] + if cfg.defaultAccount != "" { + address = cfg.defaultAccount var err error kb, err = keys.NewKeyBaseFromDir(home) @@ -142,7 +168,7 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { } // remoteAddr := resolveUnixOrTCPAddr(cfg.target) - cl, err := client.NewHTTPClient(cfg.target) + cl, err := client.NewHTTPClient(cfg.remote) if err != nil { return fmt.Errorf("unable to create http client for %q: %w", remoteAddr, err) } @@ -160,7 +186,16 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { renderer := lipgloss.DefaultRenderer() input := initURLInput(renderer) - if cfg.defaultRealm != "" { + + var targetRealm string + if len(args) > 0 { + targetRealm = args[0] + } + switch { + case targetRealm != "": + path := strings.TrimLeft(targetRealm, gnoPrefix) + input.SetValue(path) + case cfg.defaultRealm != "": path := strings.TrimLeft(cfg.defaultRealm, gnoPrefix) input.SetValue(path) } @@ -169,6 +204,7 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { mod := model{ // banner: banner, render: renderer, + readonly: cfg.readonly, client: broclient, listFuncs: newFuncList(), urlInput: input, @@ -180,53 +216,78 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { if cfg.sshListener == "" { p := tea.NewProgram(mod, - tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" + tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" + tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel ) - addr := cfg.target - if !strings.HasPrefix(addr, "http://") && !strings.HasPrefix(addr, "https://") { - addr = "http://" + addr + host1, port1 := cfg.remote, "8888" + if cfg.devEndpoint != "" { + host1, port1, err = net.SplitHostPort(cfg.devEndpoint) + if err != nil { + return fmt.Errorf("unable to parse dev endpoint: %w", err) + } + } + + if !strings.HasPrefix(host1, "http://") && !strings.HasPrefix(host1, "https://") { + host1 = "http://" + host1 } - devpoint, err := url.Parse(addr) + devpoint, err := url.Parse(host1) if err != nil { return fmt.Errorf("unable to construct devaddr: %w", err) } - host, _, _ := net.SplitHostPort(devpoint.Host) - eventsurl := fmt.Sprintf("ws://%s:8888/_events", host) - // if err := CheckEndpoint(eventsurl); err != nil { - // return fmt.Errorf("unable to check dev events endpoint: %w", err) - // } + host, port2, _ := net.SplitHostPort(devpoint.Host) + devpoint.Host = host + devpoint.Scheme = "ws" + devpoint.Path = "_events" + switch { + case port1 != "": + devpoint.Host += ":" + port1 + case port2 != "": + devpoint.Host += ":" + port2 + } - var wg sync.WaitGroup - if true { - var devcl DevClient - // devcl.Logger = log.ZapLoggerToSlog(log.NewZapConsoleLogger(io.Out(), zapcore.DebugLevel)) - devcl.Handler = func(typ events.Type, data any) error { - switch typ { - case events.EvtReload, events.EvtReset, events.EvtTxResult: - p.Send(UpdateRenderMsg{}) - default: - } - - return nil + // var wg sync.WaitGroup + var devcl DevClient + // devcl.Logger = log.ZapLoggerToSlog(log.NewZapConsoleLogger(io.Out(), zapcore.DebugLevel)) + devcl.Handler = func(typ events.Type, data any) error { + switch typ { + case events.EvtReload, events.EvtReset, events.EvtTxResult: + p.Send(UpdateRenderMsg{}) + default: } - wg.Add(1) - go func() { - defer wg.Done() - - if err := devcl.Run(ctx, eventsurl, nil); err != nil { - io.ErrPrintfln("run error: %s", err.Error()) - } - }() + return nil } - if _, err := p.Run(); err != nil { - return fmt.Errorf("could not run program: %w", err) - } + var wg sync.WaitGroup + + ctx, cancel := context.WithCancelCause(ctx) + defer cancel(nil) + + // Setup trap signal + osm.TrapSignal(func() { + cancel(nil) + p.Quit() + }) + + wg.Add(1) + go func() { + defer wg.Done() + if err := devcl.Run(ctx, devpoint.String(), nil); err != nil { + logger.Error("dev connection failed", "err", err) + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + _, err := p.Run() + cancel(err) + io.Println("Bye!") + }() wg.Wait() return nil diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index dc273a35c22..c3c62afb927 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -118,6 +118,7 @@ type model struct { ready bool viewport viewport.Model height, width int + readonly bool pageurls map[string]string messageDisplay bool @@ -390,7 +391,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.commandInput.Blur() cmds = append(cmds, m.urlInput.Focus()) m.commandFocus = false - case m.zone.Get("command_input").InBounds(msg): + case !m.readonly && m.zone.Get("command_input").InBounds(msg): m.urlInput.Blur() cmds = append(cmds, m.commandInput.Focus()) m.commandFocus = true @@ -413,7 +414,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: switch msg.String() { case "alt+down", "alt+up", "tab": - if m.urlInput.Focused() { + if m.urlInput.Focused() && !m.readonly { m.urlInput.Blur() cmds = append(cmds, m.commandInput.Focus()) m.commandFocus = true @@ -628,6 +629,10 @@ func (m model) navView() string { } func (m model) footerView() string { + if m.readonly { + return "" + } + info := infoStyle(m.render).Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) command := m.zone.Mark("command_input", inputStyleLeft(m.render). Width(m.viewport.Width-lipgloss.Width(info)-5). From e6ba80766e100e0e300254a3ecfc79d239651094 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 25 Jun 2024 14:29:18 +0200 Subject: [PATCH 18/64] feat: readonly Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/main.go | 63 ++++++++++++++++++++--------- contribs/gnobro/cmd/gnobro/model.go | 2 +- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnobro/cmd/gnobro/main.go index 239fa4a853b..0bdcb1782a5 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnobro/cmd/gnobro/main.go @@ -11,17 +11,15 @@ import ( "net" "net/url" "os" - "os/signal" + "path/filepath" "strings" "sync" - "syscall" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" charmlog "github.com/charmbracelet/log" "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" - "github.com/charmbracelet/wish/accesscontrol" "github.com/charmbracelet/wish/activeterm" "github.com/charmbracelet/wish/bubbletea" "github.com/charmbracelet/wish/logging" @@ -202,7 +200,6 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { cmd := initCommandInput(renderer) mod := model{ - // banner: banner, render: renderer, readonly: cfg.readonly, client: broclient, @@ -294,9 +291,18 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { } teaHandler := func(s ssh.Session) (tea.Model, []tea.ProgramOption) { - model := mod + model := mod // copy model + model.readonly = cfg.readonly model.banner = fmt.Sprintf(banner, s.User()) model.render = bubbletea.MakeRenderer(s) + model.history = list.New() // set a new history + + if len(s.Command()) == 1 { + path := filepath.Clean(s.Command()[0]) + model.urlInput.SetValue(path) + model.urlInput.CursorEnd() + } + return model, []tea.ProgramOption{ tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel @@ -313,30 +319,37 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { wish.WithAddress(sshaddr.String()), wish.WithHostKeyPath(cfg.sshHostKeyPath), wish.WithMiddleware( - accesscontrol.Middleware(), - bubbletea.Middleware(teaHandler), + NoCommandMiddleware(), activeterm.Middleware(), // Bubble Tea apps usually require a PTY. + bubbletea.Middleware(teaHandler), logging.Middleware(), ), ) - done := make(chan os.Signal, 1) - signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) - logger.Info("Starting SSH server", "addr", sshaddr.String()) + ctx, cancelCause := context.WithCancelCause(ctx) + defer cancelCause(nil) + + // Setup trap signal + osm.TrapSignal(func() { + logger.Info("Stopping SSH server") + if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) { + cancelCause(fmt.Errorf("Could not stop server: %w", err)) + } else { + cancel() + } + }) + go func() { + logger.Info("Starting SSH server", "addr", sshaddr.String()) if err = s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) { - logger.Error("Could not start server", "error", err) - done <- nil + cancelCause(err) + } else { + cancel() } }() - <-done - logger.Info("Stopping SSH server") - if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) { - logger.Error("Could not stop server", "error", err) - } - - return nil + <-ctx.Done() + return context.Cause(ctx) } func getSignerForAccount(io commands.IO, address string, kb keys.Keybase, cfg *broCfg) (gnoclient.Signer, error) { @@ -392,3 +405,15 @@ func resolveUnixOrTCPAddr(in string) (out string) { panic(err) } + +func NoCommandMiddleware() wish.Middleware { + return func(sh ssh.Handler) ssh.Handler { + return func(s ssh.Session) { + if len(s.Command()) > 0 { + s.Exit(1) + } else { + sh(s) + } + } + } +} diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index c3c62afb927..1cf9ca1795e 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -280,7 +280,7 @@ func (m *model) moveHistoryBackward() (string, bool) { return "", false } -func (m *model) updateHistoryBackward(forward bool) { +func (m *model) updateHistoryBackward() { v := m.urlInput.Value() if m.history.Len() == 0 { m.current = m.history.PushBack(v) From d8f6b5576fd24c615f46b1d5e4103f338dece8cf Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 25 Jun 2024 15:50:31 +0200 Subject: [PATCH 19/64] fix: key move Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/model.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index 1cf9ca1795e..f7037c89c25 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -332,6 +332,7 @@ func (m *model) ExtendCommandInput() bool { m.commandInput.SetValue(value) return true } + m.commandInput.CursorEnd() } } @@ -413,20 +414,20 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case tea.KeyMsg: switch msg.String() { - case "alt+down", "alt+up", "tab": + case "alt+down": if m.urlInput.Focused() && !m.readonly { m.urlInput.Blur() cmds = append(cmds, m.commandInput.Focus()) m.commandFocus = true - } else if m.commandInput.Focused() { - if msg.String() == "tab" && m.ExtendCommandInput() { - break - } else { - m.commandInput.Blur() - cmds = append(cmds, m.urlInput.Focus()) - m.commandFocus = false - } } + case "alt+up": + if m.commandInput.Focused() { + m.commandInput.Blur() + cmds = append(cmds, m.urlInput.Focus()) + m.commandFocus = false + } + case "tab": + m.ExtendCommandInput() case "down": if m.commandFocus { m.listFuncs.CursorDown() From d571be513491d4402a6c5ee9cc6422c75e43e864 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:49:41 +0200 Subject: [PATCH 20/64] fix: unfocus command and path change Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/model.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go index f7037c89c25..b6979858307 100644 --- a/contribs/gnobro/cmd/gnobro/model.go +++ b/contribs/gnobro/cmd/gnobro/model.go @@ -378,14 +378,29 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case m.zone.Get("prev_button").InBounds(msg): if path, ok := m.moveHistoryBackward(); ok { m.moveToRealm(path) + + // unfocus command + m.commandInput.Blur() + cmds = append(cmds, m.urlInput.Focus()) + m.commandFocus = false } case m.zone.Get("next_button").InBounds(msg): if path, ok := m.moveHistoryForward(); ok { m.moveToRealm(path) + + // unfocus command + m.commandInput.Blur() + cmds = append(cmds, m.urlInput.Focus()) + m.commandFocus = false } case m.zone.Get("home_button").InBounds(msg): if m.moveToRealm("gno.land/r/gnoland/home") { m.updateHistory() + + // unfocus command + m.commandInput.Blur() + cmds = append(cmds, m.urlInput.Focus()) + m.commandFocus = false } case m.zone.Get("url_input").InBounds(msg): From 854943ed57525c8fbdc06bfd933b3904f49fe6bb Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 18 Jul 2024 11:13:31 +0200 Subject: [PATCH 21/64] feat: move gnobro into gnodev Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnobro/cmd/gnobro/model.go | 665 ------------------ contribs/gnobro/go.mod | 108 --- contribs/gnobro/go.sum | 323 --------- contribs/gnodev/Makefile | 8 +- .../cmd/gnobro/assets/banner_land_1.txt} | 0 .../cmd/gnobro/assets/banner_wave.txt} | 0 contribs/gnodev/cmd/gnobro/banner.go | 34 + .../{gnobro => gnodev}/cmd/gnobro/main.go | 267 ++++--- .../{gnobro => gnodev}/cmd/gnobro/request.go | 1 - contribs/gnodev/go.mod | 40 +- contribs/gnodev/go.sum | 85 ++- .../pkg/browser/client_dev.go} | 5 +- contribs/gnodev/pkg/browser/client_node.go | 116 +++ .../pkg/browser/list_model.go} | 13 +- contribs/gnodev/pkg/browser/model.go | 566 +++++++++++++++ contribs/gnodev/pkg/browser/model_nav.go | 82 +++ contribs/gnodev/pkg/browser/model_style.go | 239 +++++++ contribs/gnodev/pkg/browser/model_tasks.go | 85 +++ contribs/gnodev/pkg/browser/model_view.go | 167 +++++ contribs/gnodev/pkg/browser/utils.go | 40 ++ 20 files changed, 1613 insertions(+), 1231 deletions(-) delete mode 100644 contribs/gnobro/cmd/gnobro/model.go delete mode 100644 contribs/gnobro/go.mod delete mode 100644 contribs/gnobro/go.sum rename contribs/{gnobro/cmd/gnobro/banner2.txt => gnodev/cmd/gnobro/assets/banner_land_1.txt} (100%) rename contribs/{gnobro/cmd/gnobro/banner.txt => gnodev/cmd/gnobro/assets/banner_wave.txt} (100%) create mode 100644 contribs/gnodev/cmd/gnobro/banner.go rename contribs/{gnobro => gnodev}/cmd/gnobro/main.go (63%) rename contribs/{gnobro => gnodev}/cmd/gnobro/request.go (92%) rename contribs/{gnobro/cmd/gnobro/dev.go => gnodev/pkg/browser/client_dev.go} (97%) create mode 100644 contribs/gnodev/pkg/browser/client_node.go rename contribs/{gnobro/cmd/gnobro/list.go => gnodev/pkg/browser/list_model.go} (94%) create mode 100644 contribs/gnodev/pkg/browser/model.go create mode 100644 contribs/gnodev/pkg/browser/model_nav.go create mode 100644 contribs/gnodev/pkg/browser/model_style.go create mode 100644 contribs/gnodev/pkg/browser/model_tasks.go create mode 100644 contribs/gnodev/pkg/browser/model_view.go create mode 100644 contribs/gnodev/pkg/browser/utils.go diff --git a/contribs/gnobro/cmd/gnobro/model.go b/contribs/gnobro/cmd/gnobro/model.go deleted file mode 100644 index b6979858307..00000000000 --- a/contribs/gnobro/cmd/gnobro/model.go +++ /dev/null @@ -1,665 +0,0 @@ -package main - -import ( - "bytes" - clist "container/list" - "fmt" - "regexp" - "strings" - - "github.com/charmbracelet/bubbles/list" - "github.com/charmbracelet/bubbles/textinput" - "github.com/charmbracelet/bubbles/viewport" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/glamour" - "github.com/charmbracelet/lipgloss" - zone "github.com/lrstanley/bubblezone" - - "github.com/gnolang/gno/gno.land/pkg/gnoweb" - "github.com/gnolang/gno/tm2/pkg/crypto/keys" - "github.com/muesli/reflow/wordwrap" -) - -// var noopLogger = log.NewNopLogger() - -const gnoPrefix = "gno.land/" - -// You generally won't need this unless you're processing stuff with -// complicated ANSI escape sequences. Turn it on if you notice flickering. -// -// Also keep in mind that high performance rendering only works for programs -// that use the full size of the terminal. We're enabling that below with -// tea.EnterAltScreen(). -const useHighPerformanceRenderer = false - -var ( - navStyleEnable = func(r *lipgloss.Renderer) lipgloss.Style { - return r.NewStyle(). - Foreground(lipgloss.Color("#fab387")) - } - - navStyleDisable = func(r *lipgloss.Renderer) lipgloss.Style { - return r.NewStyle(). - Foreground(lipgloss.Color("240")) - } - - boxRoundedStyle = func(r *lipgloss.Renderer) lipgloss.Style { - b := lipgloss.RoundedBorder() - return r.NewStyle(). - BorderStyle(b). - Padding(0, 2) - } - - promptStyle = func(r *lipgloss.Renderer) lipgloss.Style { - return r.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#ea76cb")) - } - - inputStyleLeft = func(r *lipgloss.Renderer) lipgloss.Style { - b := lipgloss.RoundedBorder() - b.Right = "β”œ" - return r.NewStyle(). - BorderStyle(b). - Padding(0, 2) - } - - infoStyle = func(r *lipgloss.Renderer) lipgloss.Style { - b := lipgloss.RoundedBorder() - b.Left = "─" - return boxRoundedStyle(r).Copy().BorderStyle(b) - } -) - -type modelFunc struct { - textInput textinput.Model - err error -} - -type modelInput struct { - textInput textinput.Model - err error -} - -func initURLInput(r *lipgloss.Renderer) textinput.Model { - ti := textinput.New() - ti.Placeholder = "r/gnoland/blog" - ti.Focus() - ti.CharLimit = 156 - ti.PromptStyle = promptStyle(r) - ti.Prompt = gnoPrefix - - return ti -} - -func initCommandInput(r *lipgloss.Renderer) textinput.Model { - ti := textinput.New() - ti.Placeholder = "" - ti.CharLimit = 156 - ti.PromptStyle = promptStyle(r) - ti.Prompt = "> " - - return ti -} - -type model struct { - kb keys.Keybase - render *lipgloss.Renderer - banner string - - client *BroClient - - urlInput textinput.Model - listFuncs FuncListModel - - commandInput textinput.Model - commandFocus bool - zone *zone.Manager - ready bool - viewport viewport.Model - height, width int - readonly bool - - pageurls map[string]string - messageDisplay bool - - history *clist.List - current *clist.Element -} - -func (m model) Init() tea.Cmd { - m.history.Init() - return nil -} - -// realm path surrounded by ansi escape sequences -var reUrlPattern = regexp.MustCompile(`(?mU)\x1b[^m]*m(?:(?:https?://)?gno.land)?(/[^\s]+)\x1b[^m]*m`) - -func redirectWebPath(path string) string { - if alias, ok := gnoweb.Aliases[path]; ok { - return alias - } - - if redirect, ok := gnoweb.Redirects[path]; ok { - return redirect - } - - return path -} - -func (m model) findAndMarkURLs(body []byte) []byte { - var buf bytes.Buffer - lastIndex := 0 - - indexes := reUrlPattern.FindAllSubmatchIndex(body, -1) - for i, loc := range indexes { - match := string(body[loc[0]:loc[1]]) - uri := string(body[loc[2]:loc[3]]) - markid := fmt.Sprintf("url_%d", i) - - // Write bytes before match - buf.Write(body[lastIndex:loc[0]]) - - // Write quoted URL - buf.WriteString(m.zone.Mark(markid, match)) - m.pageurls[markid] = uri - lastIndex = loc[1] - } - // Write remaining bytes - buf.Write(body[lastIndex:]) - - // Cleanup previous urls - for i := len(indexes); i < len(m.pageurls); i++ { - markid := fmt.Sprintf("url_%d", i) - delete(m.pageurls, markid) - } - - return buf.Bytes() -} - -func (m model) fetchRenderView() (view []byte, err error) { - path := m.urlInput.Value() - rlmpath := gnoPrefix + path - - rlmpath, args, _ := strings.Cut(rlmpath, ":") - res, err := m.client.Render(rlmpath, args) - if err != nil { - return nil, fmt.Errorf("unable to fetch Render: %w", err) - } - - r, err := glamour.NewTermRenderer( - glamour.WithAutoStyle(), - glamour.WithWordWrap(m.viewport.Width), - ) - - if err != nil { - return nil, fmt.Errorf("unable to get render view: %w", err) - } - - view, err = r.RenderBytes(res) - if err != nil { - return nil, fmt.Errorf("uanble to render markdown view: %w", err) - } - - return m.findAndMarkURLs(view), nil -} - -func (m model) fetchFuncsList() (view []list.Item, err error) { - path := m.urlInput.Value() - rlmpath := gnoPrefix + path - - rlmpath, _, _ = strings.Cut(rlmpath, ":") - funcs, err := m.client.Funcs(rlmpath) - if err != nil { - return nil, fmt.Errorf("unable to fetch Render: %w", err) - } - - items := make([]list.Item, 0, len(funcs)) - for _, fun := range funcs { - if fun.FuncName != "Render" { - items = append(items, itemFunc(fun)) - } - } - - return items, nil -} - -func (m model) getError(err error) string { - f := wordwrap.NewWriter(m.viewport.Width) - fmt.Fprintf(f, "error: %v", err) - serr := f.String() - return serr -} - -func (m *model) moveToRealm(realm string) bool { - // Trim prefix - path := strings.TrimPrefix(realm, gnoPrefix) - // redirect if any well known path - path = redirectWebPath(path) - // trim any slash - path = strings.TrimPrefix(path, "/") - if path == m.urlInput.Value() { - return false - } - - // Set uri input - m.urlInput.SetValue(path) - m.urlInput.CursorEnd() - // Update render - m.RenderUpdate() - - return true -} - -func (m *model) updateHistory() { - v := m.urlInput.Value() - if m.history.Len() == 0 { - m.current = m.history.PushBack(v) - return - } - - m.current = m.history.InsertAfter(v, m.current) - if next := m.current.Next(); next != nil { - m.history.Remove(next) - } -} - -func (m *model) moveHistoryForward() (string, bool) { - if next := m.current.Next(); next != nil { - m.current = next - return m.current.Value.(string), true - } - return "", false -} - -func (m *model) moveHistoryBackward() (string, bool) { - if prev := m.current.Prev(); prev != nil { - m.current = prev - return m.current.Value.(string), true - } - return "", false -} - -func (m *model) updateHistoryBackward() { - v := m.urlInput.Value() - if m.history.Len() == 0 { - m.current = m.history.PushBack(v) - } else { - m.current = m.history.InsertAfter(v, m.current) - } -} - -func (m *model) RenderUpdate() { - var err error - render, err := m.fetchRenderView() - if err != nil { - f := wordwrap.NewWriter(m.viewport.Width) - fmt.Fprintf(f, "error: %s", err) - m.viewport.SetContent(f.String()) - return - } - - m.viewport.SetContent(string(render)) - list, err := m.fetchFuncsList() - if err != nil { - f := wordwrap.NewWriter(m.viewport.Width) - fmt.Fprintf(f, "error: %s", err) - m.viewport.SetContent(f.String()) - return - } - - if len(list) == 0 { - return - } - - m.listFuncs.Title = m.urlInput.Value() - m.listFuncs.SetItems(list) - m.listFuncs.SetSize(m.viewport.Width, 7) -} - -func (m *model) ExtendCommandInput() bool { - if m.commandInput.Focused() { - if item, ok := m.listFuncs.SelectedItem().(itemFunc); ok { - var value string - if len(item.Params) > 0 { - value = item.Title() + "(" - } else { - value = item.Title() + "()" - } - - currentValue := m.commandInput.Value() - if len(value) > len(currentValue) && strings.HasPrefix(value, currentValue) { - m.commandInput.SetValue(value) - return true - } - m.commandInput.CursorEnd() - - } - } - - return false -} - -type UpdateRenderMsg struct { - realmPath string -} - -// XXX: it's bit messy here, need some rework -func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - var ( - cmd tea.Cmd - cmds []tea.Cmd - ) - - if m.banner != "" { - if _, ok := msg.(tea.KeyMsg); ok { - m.banner = "" - return m, nil - } - } - - switch msg := msg.(type) { - case UpdateRenderMsg: - if msg.realmPath != "" { - m.moveToRealm(msg.realmPath) - } else { - m.RenderUpdate() - } - - case tea.MouseMsg: - if msg.Button == tea.MouseButtonWheelUp || msg.Button == tea.MouseButtonWheelDown || - msg.Button == tea.MouseButtonWheelLeft || msg.Button == tea.MouseButtonWheelRight { - m.viewport, cmd = m.viewport.Update(msg) - return m, cmd // stop here to avoid update input view - } - - if msg.Action == tea.MouseActionRelease { - switch { - case m.zone.Get("prev_button").InBounds(msg): - if path, ok := m.moveHistoryBackward(); ok { - m.moveToRealm(path) - - // unfocus command - m.commandInput.Blur() - cmds = append(cmds, m.urlInput.Focus()) - m.commandFocus = false - } - case m.zone.Get("next_button").InBounds(msg): - if path, ok := m.moveHistoryForward(); ok { - m.moveToRealm(path) - - // unfocus command - m.commandInput.Blur() - cmds = append(cmds, m.urlInput.Focus()) - m.commandFocus = false - } - case m.zone.Get("home_button").InBounds(msg): - if m.moveToRealm("gno.land/r/gnoland/home") { - m.updateHistory() - - // unfocus command - m.commandInput.Blur() - cmds = append(cmds, m.urlInput.Focus()) - m.commandFocus = false - } - - case m.zone.Get("url_input").InBounds(msg): - m.commandInput.Blur() - cmds = append(cmds, m.urlInput.Focus()) - m.commandFocus = false - case !m.readonly && m.zone.Get("command_input").InBounds(msg): - m.urlInput.Blur() - cmds = append(cmds, m.commandInput.Focus()) - m.commandFocus = true - default: - for mark := range m.pageurls { - if !m.zone.Get(mark).InBounds(msg) { - continue - } - - if uri := m.pageurls[mark]; uri != "" { - if m.moveToRealm(uri) { - m.updateHistory() - } - } - - break - } - } - } - case tea.KeyMsg: - switch msg.String() { - case "alt+down": - if m.urlInput.Focused() && !m.readonly { - m.urlInput.Blur() - cmds = append(cmds, m.commandInput.Focus()) - m.commandFocus = true - } - case "alt+up": - if m.commandInput.Focused() { - m.commandInput.Blur() - cmds = append(cmds, m.urlInput.Focus()) - m.commandFocus = false - } - case "tab": - m.ExtendCommandInput() - case "down": - if m.commandFocus { - m.listFuncs.CursorDown() - } - case "up": - if m.commandFocus { - m.listFuncs.CursorUp() - } - case "alt+r": - m.RenderUpdate() - case "enter": - if m.commandInput.Focused() && !m.messageDisplay { - if len(m.listFuncs.Items()) > 1 { - m.ExtendCommandInput() - break - } - - path := m.urlInput.Value() - rlmpath := gnoPrefix + path - res, err := m.client.Call(rlmpath, m.commandInput.Value()) - if err != nil { - content := fmt.Sprintf("%s\n\npress [enter] to dismiss", m.getError(err)) - m.viewport.SetContent(content) - m.messageDisplay = true - } else { - if strings.TrimSpace(string(res)) == "" { - m.RenderUpdate() - m.messageDisplay = false - } else { - m.viewport.SetContent(fmt.Sprintf("%s\n\npress [enter] to dismiss", string(res))) - m.messageDisplay = true - } - } - - m.listFuncs.Erase() - break - } - - if m.messageDisplay || m.urlInput.Focused() { - m.listFuncs.Erase() - m.RenderUpdate() - if m.current.Value.(string) != m.urlInput.Value() { - m.updateHistory() - } - - m.messageDisplay = false - } - - case "ctrl+c", "esc": - return m, tea.Quit - default: - if m.urlInput.Focused() { - m.urlInput, cmd = m.urlInput.Update(msg) - } else { - m.commandInput, cmd = m.commandInput.Update(msg) - } - // handle url input - - } - - case tea.WindowSizeMsg: - m.width = msg.Width - - headerHeight := lipgloss.Height(m.headerView()) - footerHeight := lipgloss.Height(m.footerView()) - verticalMarginHeight := headerHeight + footerHeight - - if !m.ready { - // Since this program is using the full size of the viewport we - // need to wait until we've received the window dimensions before - // we can initialize the viewport. The initial dimensions come in - // quickly, though asynchronously, which is why we wait for them - // here. - m.viewport = viewport.New(msg.Width, msg.Height-verticalMarginHeight) - m.viewport.YPosition = headerHeight - m.viewport.MouseWheelEnabled = true - m.viewport.MouseWheelDelta = 1 - m.viewport.HighPerformanceRendering = useHighPerformanceRenderer - m.ready = true - - // This is only necessary for high performance rendering, which in - // most cases you won't need. - // - // Render the viewport one line below the header. - m.viewport.YPosition = headerHeight + 1 - - if value := m.urlInput.Value(); value != "" { - m.RenderUpdate() - m.updateHistory() - } - } else { - m.viewport.Width = msg.Width - m.viewport.Height = msg.Height - verticalMarginHeight - } - - m.height = m.viewport.Height - if !m.urlInput.Focused() && len(m.listFuncs.Items()) > 0 { - m.viewport.Height = m.height - lipgloss.Height(m.listFuncsView()) - } - - if useHighPerformanceRenderer { - // Render (or re-render) the whole viewport. Necessary both to - // initialize the viewport and when the window is resized. - // - // This is needed for high-performance rendering only. - cmds = append(cmds, viewport.Sync(m.viewport)) - } - } - - // m.listFuncs, cmd = m.listFuncs.Update(msg) - // cmds = append(cmds, cmd) - - if v := m.commandInput.Value(); v != "" { - m.listFuncs.FilterItems(v) - } else { - m.listFuncs.Reset() - } - - if m.commandFocus && len(m.listFuncs.Items()) > 0 { - m.viewport.Height = m.height - lipgloss.Height(m.listFuncsView()) - } else { - m.viewport.Height = m.height - } - - // Handle keyboard and mouse events in the viewport - if m.urlInput.Focused() { - m.viewport, cmd = m.viewport.Update(msg) - cmds = append(cmds, cmd) - } - - return m, tea.Batch(cmds...) -} - -func (m model) View() string { - if !m.ready { - return "+" - } - - if m.banner != "" { - banner := m.render.NewStyle().Padding(1, 5). - Border(lipgloss.DoubleBorder(), true, false, true). - Render(m.banner) - - return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, banner) - } - - mainView := fmt.Sprintf("%s\n%s\n%s", m.headerView(), m.bodyView(), m.footerView()) - return m.zone.Scan(mainView) -} - -func (m model) listFuncsView() string { - return boxRoundedStyle(m.render). - Render(m.listFuncs.View()) -} - -func (m model) bodyView() string { - return m.viewport.View() -} - -func (m model) headerView() string { - return lipgloss.JoinVertical(lipgloss.Left, m.navView(), m.urlView()) -} - -func (m model) urlView() string { - return m.zone.Mark("url_input", boxRoundedStyle(m.render). - Width(m.viewport.Width-2). - Render(m.urlInput.View())) -} - -func (m model) navView() string { - home := navStyleEnable(m.render).Padding(0, 1).Render("[Home]") - - var style lipgloss.Style - if m.current != nil && m.current.Prev() != nil { - style = navStyleEnable(m.render) - } else { - style = navStyleDisable(m.render) - } - prev := style.Margin(0, 1).Render("") - - spaceWidth := m.width / 3 - return lipgloss.JoinHorizontal(lipgloss.Left, - m.render.NewStyle().Width(spaceWidth).Padding(0, 1). - Render(lipgloss.JoinHorizontal(lipgloss.Left, - m.zone.Mark("prev_button", prev), - m.zone.Mark("next_button", next), - )), - m.render.PlaceHorizontal(spaceWidth, lipgloss.Center, "Gno.Land"), - m.render.PlaceHorizontal(spaceWidth, lipgloss.Right, - m.zone.Mark("home_button", home)), - ) - -} - -func (m model) footerView() string { - if m.readonly { - return "" - } - - info := infoStyle(m.render).Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) - command := m.zone.Mark("command_input", inputStyleLeft(m.render). - Width(m.viewport.Width-lipgloss.Width(info)-5). - Render(m.commandInput.View())) - line := strings.Repeat("─", 3) - - powerline := lipgloss.JoinHorizontal(lipgloss.Center, command, line, info) - if m.commandFocus && len(m.listFuncs.Items()) > 0 { - suggestions := m.listFuncsView() - return lipgloss.JoinVertical(lipgloss.Left, suggestions, powerline) - } - - return powerline -} diff --git a/contribs/gnobro/go.mod b/contribs/gnobro/go.mod deleted file mode 100644 index 45c701a5ad9..00000000000 --- a/contribs/gnobro/go.mod +++ /dev/null @@ -1,108 +0,0 @@ -module github.com/gnolang/gno/contribs/gnobro - -go 1.21 - -replace ( - github.com/gnolang/gno => ../.. - github.com/gnolang/gno/contribs/gnodev => ../gnodev -) - -require ( - github.com/charmbracelet/bubbles v0.18.0 - github.com/charmbracelet/bubbletea v0.26.2 - github.com/charmbracelet/glamour v0.7.0 - github.com/charmbracelet/lipgloss v0.10.0 - github.com/charmbracelet/log v0.4.0 - github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917 - github.com/charmbracelet/wish v1.4.0 - github.com/gnolang/gno v0.0.0-00010101000000-000000000000 - github.com/gnolang/gno/contribs/gnodev v0.0.0-00010101000000-000000000000 - github.com/gorilla/websocket v1.5.1 - github.com/lrstanley/bubblezone v0.0.0-20240125042004-b7bafc493195 - github.com/muesli/reflow v0.3.0 - github.com/sahilm/fuzzy v0.1.1 -) - -require ( - dario.cat/mergo v1.0.0 // indirect - github.com/alecthomas/chroma/v2 v2.8.0 // indirect - github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect - github.com/atotto/clipboard v0.1.4 // indirect - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/aymerick/douceur v0.2.0 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect - github.com/btcsuite/btcd/btcutil v1.1.5 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/charmbracelet/keygen v0.5.0 // indirect - github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 // indirect - github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd // indirect - github.com/cockroachdb/apd/v3 v3.2.1 // indirect - github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect - github.com/creack/pty v1.1.21 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect - github.com/dlclark/regexp2 v1.4.0 // indirect - github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect - github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect - github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/go-logr/logr v1.4.1 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/gorilla/css v1.0.0 // indirect - github.com/gorilla/mux v1.8.1 // indirect - github.com/gorilla/securecookie v1.1.1 // indirect - github.com/gorilla/sessions v1.2.1 // indirect - github.com/gotuna/gotuna v0.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect - github.com/libp2p/go-buffer-pool v0.1.0 // 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.15 // indirect - github.com/microcosm-cc/bluemonday v1.0.25 // indirect - github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect - github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.15.2 // indirect - github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/peterbourgon/ff/v3 v3.4.0 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect - github.com/rs/cors v1.10.1 // indirect - github.com/rs/xid v1.5.0 // indirect - github.com/stretchr/testify v1.9.0 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect - github.com/yuin/goldmark v1.5.4 // indirect - github.com/yuin/goldmark-emoji v1.0.2 // indirect - github.com/zondax/hid v0.9.2 // indirect - github.com/zondax/ledger-go v0.14.3 // indirect - go.etcd.io/bbolt v1.3.9 // indirect - go.opentelemetry.io/otel v1.27.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 // indirect - go.opentelemetry.io/otel/metric v1.27.0 // indirect - go.opentelemetry.io/otel/sdk v1.27.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.27.0 // indirect - go.opentelemetry.io/otel/trace v1.27.0 // indirect - go.opentelemetry.io/proto/otlp v1.2.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - go.uber.org/zap/exp v0.2.0 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect - golang.org/x/mod v0.16.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.19.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect - google.golang.org/grpc v1.64.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/contribs/gnobro/go.sum b/contribs/gnobro/go.sum deleted file mode 100644 index 454a7666d13..00000000000 --- a/contribs/gnobro/go.sum +++ /dev/null @@ -1,323 +0,0 @@ -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= -github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= -github.com/alecthomas/chroma/v2 v2.8.0 h1:w9WJUjFFmHHB2e8mRpL9jjy3alYDlU0QLDezj1xE264= -github.com/alecthomas/chroma/v2 v2.8.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw= -github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= -github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= -github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -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/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= -github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7Nzl7WHaOTlTr5hIrR4n1NM4v9n4Kw= -github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= -github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= -github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= -github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= -github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= -github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= -github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= -github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= -github.com/charmbracelet/bubbletea v0.26.2 h1:Eeb+n75Om9gQ+I6YpbCXQRKHt5Pn4vMwusQpwLiEgJQ= -github.com/charmbracelet/bubbletea v0.26.2/go.mod h1:6I0nZ3YHUrQj7YHIHlM8RySX4ZIthTliMY+W8X8b+Gs= -github.com/charmbracelet/glamour v0.7.0 h1:2BtKGZ4iVJCDfMF229EzbeR1QRKLWztO9dMtjmqZSng= -github.com/charmbracelet/glamour v0.7.0/go.mod h1:jUMh5MeihljJPQbJ/wf4ldw2+yBP59+ctV36jASy7ps= -github.com/charmbracelet/keygen v0.5.0 h1:XY0fsoYiCSM9axkrU+2ziE6u6YjJulo/b9Dghnw6MZc= -github.com/charmbracelet/keygen v0.5.0/go.mod h1:DfvCgLHxZ9rJxdK0DGw3C/LkV4SgdGbnliHcObV3L+8= -github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s= -github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE= -github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= -github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= -github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917 h1:NZKjJ7d/pzk/AfcJYEzmF8M48JlIrrY00RR5JdDc3io= -github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917/go.mod h1:8/Ve8iGRRIGFM1kepYfRF2pEOF5Y3TEZYoJaA54228U= -github.com/charmbracelet/wish v1.4.0 h1:pL1uVP/YuYgJheHEj98teZ/n6pMYnmlZq/fcHvomrfc= -github.com/charmbracelet/wish v1.4.0/go.mod h1:ew4/MjJVfW/akEO9KmrQHQv1F7bQRGscRMrA+KtovTk= -github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 h1:3RXpZWGWTOeVXCTv0Dnzxdv/MhNUkBfEcbaTY0zrTQI= -github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= -github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd h1:HqBjkSFXXfW4IgX3TMKipWoPEN08T3Pi4SA/3DLss/U= -github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd/go.mod h1:6GZ13FjIP6eOCqWU4lqgveGnYxQo9c3qBzHPeFu4HBE= -github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= -github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= -github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= -github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= -github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= -github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= -github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= -github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= -github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk= -github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8= -github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= -github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/gorilla/csrf v1.7.0/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA= -github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= -github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= -github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= -github.com/gotuna/gotuna v0.6.0 h1:N1lQKXEi/lwRp8u3sccTYLhzOffA4QasExz/1M5Riws= -github.com/gotuna/gotuna v0.6.0/go.mod h1:F/ecRt29ChB6Ycy1AFIBpBiMNK0j7Heq+gFbLWquhjc= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= -github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/lrstanley/bubblezone v0.0.0-20240125042004-b7bafc493195 h1:zcxmFnwisGZSaEzgvkOrs4belfcRlKyIUfa3sOQSttQ= -github.com/lrstanley/bubblezone v0.0.0-20240125042004-b7bafc493195/go.mod h1:v5lEwWaguF1o2MW/ucO0ZIA/IZymdBYJJ+2cMRLE7LU= -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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= -github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= -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/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= -github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= -github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -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/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= -github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= -github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= -github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s= -github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= -github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= -github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= -github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= -github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= -go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= -go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0 h1:hDKnobznDpcdTlNzO0S/owRB8tyVr1OoeZZhDoqY+Cs= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0/go.mod h1:kUDQaUs1h8iTIHbQTk+iJRiUvSfJYMMKTtMCaiVu7B0= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= -go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= -go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= -go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= -go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= -go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= -go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= -go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= -go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= -go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= -go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs= -go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= -google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/contribs/gnodev/Makefile b/contribs/gnodev/Makefile index 01801064d1f..2fef77bf8c1 100644 --- a/contribs/gnodev/Makefile +++ b/contribs/gnodev/Makefile @@ -5,11 +5,11 @@ GOTEST_FLAGS ?= $(GOBUILD_FLAGS) -v -p 1 -timeout=5m rundep := go run -modfile ../../misc/devdeps/go.mod golangci_lint := $(rundep) github.com/golangci/golangci-lint/cmd/golangci-lint -install: +install: install.gnodev install.gnobro +install.gnodev: go install $(GOBUILD_FLAGS) ./cmd/gnodev - -build: - go build $(GOBUILD_FLAGS) -o build/gnodev ./cmd/gnodev +install.gnobro: + go install $(GOBUILD_FLAGS) ./cmd/gnobro lint: $(golangci_lint) --config ../../.github/golangci.yml run ./... diff --git a/contribs/gnobro/cmd/gnobro/banner2.txt b/contribs/gnodev/cmd/gnobro/assets/banner_land_1.txt similarity index 100% rename from contribs/gnobro/cmd/gnobro/banner2.txt rename to contribs/gnodev/cmd/gnobro/assets/banner_land_1.txt diff --git a/contribs/gnobro/cmd/gnobro/banner.txt b/contribs/gnodev/cmd/gnobro/assets/banner_wave.txt similarity index 100% rename from contribs/gnobro/cmd/gnobro/banner.txt rename to contribs/gnodev/cmd/gnobro/assets/banner_wave.txt diff --git a/contribs/gnodev/cmd/gnobro/banner.go b/contribs/gnodev/cmd/gnobro/banner.go new file mode 100644 index 00000000000..ddd96291427 --- /dev/null +++ b/contribs/gnodev/cmd/gnobro/banner.go @@ -0,0 +1,34 @@ +package main + +import ( + _ "embed" +) + +// //go:embed assets/*.txt +// var banners embed.FS + +// func getBannerFrames(prefix string) []string { +// files, err := banners.ReadDir("assets") +// if err != nil { +// panic(err) +// } + +// frames := []string{} +// for _, file := range files { +// if file.IsDir() { +// continue +// } + +// if !strings.HasPrefix(file.Name(), prefix) { +// continue +// } + +// frame, err := banners.ReadFile(filepath.Join("assets", file.Name())) +// if err != nil { +// panic(err) +// } + +// frames = append(frames, string(frame)) +// } +// return frames +// } diff --git a/contribs/gnobro/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go similarity index 63% rename from contribs/gnobro/cmd/gnobro/main.go rename to contribs/gnodev/cmd/gnobro/main.go index 0bdcb1782a5..e73c422bf82 100644 --- a/contribs/gnobro/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -1,7 +1,6 @@ package main import ( - "container/list" "context" _ "embed" "errors" @@ -14,6 +13,7 @@ import ( "path/filepath" "strings" "sync" + "time" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" @@ -24,6 +24,7 @@ import ( "github.com/charmbracelet/wish/bubbletea" "github.com/charmbracelet/wish/logging" + "github.com/gnolang/gno/contribs/gnodev/pkg/browser" "github.com/gnolang/gno/contribs/gnodev/pkg/events" "github.com/gnolang/gno/gno.land/pkg/gnoclient" "github.com/gnolang/gno/gno.land/pkg/integration" @@ -31,11 +32,14 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys" - tmlog "github.com/gnolang/gno/tm2/pkg/log" osm "github.com/gnolang/gno/tm2/pkg/os" - zone "github.com/lrstanley/bubblezone" ) +//go:embed assets/banner_land_1.txt +var banner string + +const gnoPrefix = "gno.land" + type broCfg struct { readonly bool remote string @@ -56,9 +60,6 @@ var defaultBroOptions = broCfg{ sshHostKeyPath: ".ssh/id_ed25519", } -//go:embed banner2.txt -var banner string - func main() { cfg := &broCfg{} @@ -142,7 +143,6 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { home := gnoenv.HomeDir() - logger := tmlog.NewNoopLogger() var address string var kb keys.Keybase if cfg.defaultAccount != "" { @@ -165,144 +165,130 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { return fmt.Errorf("unable to get signer for account %q: %w", address, err) } - // remoteAddr := resolveUnixOrTCPAddr(cfg.target) - cl, err := client.NewHTTPClient(cfg.remote) + target := resolveUnixOrTCPAddr(cfg.remote) + cl, err := client.NewHTTPClient(target) if err != nil { - return fmt.Errorf("unable to create http client for %q: %w", remoteAddr, err) + return fmt.Errorf("unable to create http client for %q: %w", target, err) } gnocl := &gnoclient.Client{ RPCClient: cl, Signer: signer, } - base := gnoclient.BaseTxCfg{ - GasFee: "1000000ugnot", - GasWanted: 2000000, - } - - broclient := NewBroClient(logger, base, gnocl) - renderer := lipgloss.DefaultRenderer() - input := initURLInput(renderer) - - var targetRealm string - if len(args) > 0 { - targetRealm = args[0] - } + var path string switch { - case targetRealm != "": - path := strings.TrimLeft(targetRealm, gnoPrefix) - input.SetValue(path) + case len(args) > 0: + path = strings.TrimSpace(args[0]) + path = strings.TrimPrefix(path, gnoPrefix) case cfg.defaultRealm != "": - path := strings.TrimLeft(cfg.defaultRealm, gnoPrefix) - input.SetValue(path) + path = strings.TrimLeft(cfg.defaultRealm, gnoPrefix) } - cmd := initCommandInput(renderer) - mod := model{ - render: renderer, - readonly: cfg.readonly, - client: broclient, - listFuncs: newFuncList(), - urlInput: input, - commandInput: cmd, - zone: zone.New(), - pageurls: map[string]string{}, - history: list.New(), - } + bcfg := browser.DefaultConfig() + bcfg.Readonly = cfg.readonly + bcfg.Renderer = lipgloss.DefaultRenderer() + bcfg.GnoClient = gnocl + bcfg.URLDefaultValue = path + bcfg.URLPrefix = gnoPrefix if cfg.sshListener == "" { - p := tea.NewProgram(mod, - tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" + return runLocal(ctx, cfg, bcfg, io) + } - tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel - ) + return runServer(ctx, cfg, bcfg, io) +} - host1, port1 := cfg.remote, "8888" - if cfg.devEndpoint != "" { - host1, port1, err = net.SplitHostPort(cfg.devEndpoint) - if err != nil { - return fmt.Errorf("unable to parse dev endpoint: %w", err) - } - } +func runLocal(ctx context.Context, cfg *broCfg, bcfg browser.Config, io commands.IO) error { + var err error - if !strings.HasPrefix(host1, "http://") && !strings.HasPrefix(host1, "https://") { - host1 = "http://" + host1 - } + model := browser.New(bcfg) + p := tea.NewProgram(model, + tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" - devpoint, err := url.Parse(host1) - if err != nil { - return fmt.Errorf("unable to construct devaddr: %w", err) - } + tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel + ) - host, port2, _ := net.SplitHostPort(devpoint.Host) - devpoint.Host = host - devpoint.Scheme = "ws" - devpoint.Path = "_events" - switch { - case port1 != "": - devpoint.Host += ":" + port1 - case port2 != "": - devpoint.Host += ":" + port2 - } + devpoint, err := getDevEndpoint(cfg) + if err != nil { + return fmt.Errorf("unable to parse dev endpoint: %w", err) + } + + var wg sync.WaitGroup + + ctx, cancel := context.WithCancelCause(ctx) + defer cancel(nil) - // var wg sync.WaitGroup - var devcl DevClient - // devcl.Logger = log.ZapLoggerToSlog(log.NewZapConsoleLogger(io.Out(), zapcore.DebugLevel)) + // setup trap signal + osm.TrapSignal(func() { + cancel(nil) + p.Quit() + }) + + if devpoint != "" { + var devcl browser.DevClient devcl.Handler = func(typ events.Type, data any) error { switch typ { case events.EvtReload, events.EvtReset, events.EvtTxResult: - p.Send(UpdateRenderMsg{}) + p.Send(browser.RefreshRealm()) default: } return nil } - var wg sync.WaitGroup - - ctx, cancel := context.WithCancelCause(ctx) - defer cancel(nil) - - // Setup trap signal - osm.TrapSignal(func() { - cancel(nil) - p.Quit() - }) - wg.Add(1) go func() { defer wg.Done() - if err := devcl.Run(ctx, devpoint.String(), nil); err != nil { - logger.Error("dev connection failed", "err", err) + if err := devcl.Run(ctx, devpoint, nil); err != nil { + cancel(fmt.Errorf("dev connection failed: %w", err)) } }() + } - wg.Add(1) - go func() { - defer wg.Done() - _, err := p.Run() - cancel(err) - io.Println("Bye!") - }() + wg.Add(1) + go func() { + defer wg.Done() + _, err := p.Run() + cancel(err) + }() - wg.Wait() - return nil - } + wg.Wait() + + io.Println("Bye!") + return context.Cause(ctx) +} + +func runServer(ctx context.Context, cfg *broCfg, bcfg browser.Config, io commands.IO) error { + // setup logger + charmlogger := charmlog.New(io.Out()) + charmlogger.SetLevel(charmlog.DebugLevel) + logger := slog.New(charmlogger) teaHandler := func(s ssh.Session) (tea.Model, []tea.ProgramOption) { - model := mod // copy model - model.readonly = cfg.readonly - model.banner = fmt.Sprintf(banner, s.User()) - model.render = bubbletea.MakeRenderer(s) - model.history = list.New() // set a new history + shortid := fmt.Sprintf("%.10s", s.Context().SessionID()) + + bcfgCopy := bcfg // copy config + + bcfgCopy.Logger = logger.WithGroup(shortid) + bcfgCopy.Renderer = bubbletea.MakeRenderer(s) - if len(s.Command()) == 1 { + switch len(s.Command()) { + case 0: + bcfgCopy.Banner = fmt.Sprintf(banner, s.User()) + case 1: + // use command argument as path path := filepath.Clean(s.Command()[0]) - model.urlInput.SetValue(path) - model.urlInput.CursorEnd() + bcfgCopy.URLDefaultValue = path + default: } + bcfgCopy.Logger.Info("session started", + "time", time.Now(), + "path", bcfgCopy.URLDefaultValue, + "sid", s.Context().SessionID(), + "user", s.User()) + model := browser.New(bcfgCopy) return model, []tea.ProgramOption{ tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel @@ -314,44 +300,91 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { return fmt.Errorf("unable to resolve address: %w", err) } - logger = slog.New(charmlog.New(io.Out())) s, err := wish.NewServer( wish.WithAddress(sshaddr.String()), wish.WithHostKeyPath(cfg.sshHostKeyPath), wish.WithMiddleware( - NoCommandMiddleware(), - activeterm.Middleware(), // Bubble Tea apps usually require a PTY. bubbletea.Middleware(teaHandler), - logging.Middleware(), + logging.StructuredMiddlewareWithLogger( + charmlogger, charmlog.DebugLevel, + ), + activeterm.Middleware(), // Bubble Tea apps usually require a PTY. + CommandLimiterMiddleware(), ), ) ctx, cancelCause := context.WithCancelCause(ctx) defer cancelCause(nil) - // Setup trap signal + // setup trap signal osm.TrapSignal(func() { - logger.Info("Stopping SSH server") + logger.Info("stopping SSH server") if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) { - cancelCause(fmt.Errorf("Could not stop server: %w", err)) + cancelCause(fmt.Errorf("could not stop server: %w", err)) } else { - cancel() + cancelCause(nil) } }) go func() { - logger.Info("Starting SSH server", "addr", sshaddr.String()) + logger.Info("starting SSH server", "addr", sshaddr.String()) if err = s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) { cancelCause(err) } else { - cancel() + cancelCause(nil) } }() <-ctx.Done() + + io.Println("Bye!") + return context.Cause(ctx) } +func getDevEndpoint(cfg *broCfg) (string, error) { + var err error + + // use remote address as default + host, port1 := cfg.remote, "8888" + if cfg.devEndpoint != "" { + // if any dev endpoint as been set, fallback on this + host, port1, err = net.SplitHostPort(cfg.devEndpoint) + if err != nil { + return "", fmt.Errorf("unable to parse dev endpoint: %w", err) + } + } + + // ensure having a (any) protocol scheme + if strings.Index(host, "://") < 0 { + host = "http://" + host + } + + // parse full host including port + devpoint, err := url.Parse(host) + if err != nil { + return "", fmt.Errorf("unable to construct devaddr: %w", err) + } + + host, _, _ = net.SplitHostPort(devpoint.Host) + if port1 != "" { + devpoint.Host = host + ":" + port1 + } else { + devpoint.Host = host + } + + switch devpoint.Scheme { + case "ws", "wss": + case "https": + devpoint.Scheme = "wss" + default: + devpoint.Scheme = "ws" + } + devpoint.Path = "_events" + + return devpoint.String(), nil +} + func getSignerForAccount(io commands.IO, address string, kb keys.Keybase, cfg *broCfg) (gnoclient.Signer, error) { var signer gnoclient.SignerFromKeybase @@ -406,13 +439,13 @@ func resolveUnixOrTCPAddr(in string) (out string) { panic(err) } -func NoCommandMiddleware() wish.Middleware { - return func(sh ssh.Handler) ssh.Handler { +func CommandLimiterMiddleware() wish.Middleware { + return func(next ssh.Handler) ssh.Handler { return func(s ssh.Session) { - if len(s.Command()) > 0 { + if len(s.Command()) > 1 { s.Exit(1) } else { - sh(s) + next(s) } } } diff --git a/contribs/gnobro/cmd/gnobro/request.go b/contribs/gnodev/cmd/gnobro/request.go similarity index 92% rename from contribs/gnobro/cmd/gnobro/request.go rename to contribs/gnodev/cmd/gnobro/request.go index 693caffcd2e..a04a54b3f52 100644 --- a/contribs/gnobro/cmd/gnobro/request.go +++ b/contribs/gnodev/cmd/gnobro/request.go @@ -34,7 +34,6 @@ func NewBroClient(logger *slog.Logger, base gnoclient.BaseTxCfg, client *gnoclie } } -// gnokey maketx call -pkgpath "gno.land/r/dev/hello" -func "Inc" -gas-fee 1000000ugnot -gas-wanted 2000000 -send "" -broadcast -chainid "tendermint-test" -remote "http://127.0.0.1:36657" g1jg8mtut func (bl *BroClient) Call(path, call string) ([]byte, error) { method, args, err := parseMethodToArgs(call) if err != nil { diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod index 77631a0190f..2d40263c118 100644 --- a/contribs/gnodev/go.mod +++ b/contribs/gnodev/go.mod @@ -7,12 +7,20 @@ toolchain go1.22.4 replace github.com/gnolang/gno => ../.. require ( - github.com/charmbracelet/lipgloss v0.9.1 - github.com/charmbracelet/log v0.3.1 + github.com/charmbracelet/bubbles v0.18.0 + github.com/charmbracelet/bubbletea v0.26.6 + github.com/charmbracelet/glamour v0.7.0 + github.com/charmbracelet/lipgloss v0.11.0 + github.com/charmbracelet/log v0.4.0 + github.com/charmbracelet/ssh v0.0.0-20240604154955-a40c6a0d028f + github.com/charmbracelet/wish v1.4.0 github.com/fsnotify/fsnotify v1.7.0 github.com/gnolang/gno v0.0.0-00010101000000-000000000000 github.com/gorilla/websocket v1.5.1 + github.com/lrstanley/bubblezone v0.0.0-20240624011428-67235275f80c + github.com/muesli/reflow v0.3.0 github.com/muesli/termenv v0.15.2 + github.com/sahilm/fuzzy v0.1.1 github.com/stretchr/testify v1.9.0 go.uber.org/zap v1.27.0 golang.org/x/term v0.22.0 @@ -20,14 +28,29 @@ require ( require ( dario.cat/mergo v1.0.0 // indirect + github.com/alecthomas/chroma/v2 v2.8.0 // indirect + github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect + github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/aymerick/douceur v0.2.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect github.com/btcsuite/btcd/btcutil v1.1.5 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/charmbracelet/keygen v0.5.0 // indirect + github.com/charmbracelet/x/ansi v0.1.2 // indirect + github.com/charmbracelet/x/conpty v0.1.0 // indirect + github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 // indirect + github.com/charmbracelet/x/input v0.1.2 // indirect + github.com/charmbracelet/x/term v0.1.1 // indirect + github.com/charmbracelet/x/termios v0.1.0 // indirect + github.com/charmbracelet/x/windows v0.1.2 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/creack/pty v1.1.21 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/dlclark/regexp2 v1.4.0 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.2 // indirect @@ -35,6 +58,7 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/securecookie v1.1.1 // indirect github.com/gorilla/sessions v1.2.1 // indirect @@ -42,9 +66,13 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/muesli/reflow v0.3.0 // indirect + github.com/microcosm-cc/bluemonday v1.0.25 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/peterbourgon/ff/v3 v3.4.0 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -54,6 +82,9 @@ require ( github.com/rs/cors v1.11.0 // indirect github.com/rs/xid v1.5.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/yuin/goldmark v1.5.4 // indirect + github.com/yuin/goldmark-emoji v1.0.2 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.3.9 // indirect @@ -71,6 +102,7 @@ require ( golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect golang.org/x/mod v0.19.0 // indirect golang.org/x/net v0.27.0 // indirect + golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/tools v0.22.0 // indirect diff --git a/contribs/gnodev/go.sum b/contribs/gnodev/go.sum index f4eb9423c21..88ac0e10a06 100644 --- a/contribs/gnodev/go.sum +++ b/contribs/gnodev/go.sum @@ -1,8 +1,20 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/chroma/v2 v2.8.0 h1:w9WJUjFFmHHB2e8mRpL9jjy3alYDlU0QLDezj1xE264= +github.com/alecthomas/chroma/v2 v2.8.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= 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/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7Nzl7WHaOTlTr5hIrR4n1NM4v9n4Kw= @@ -30,14 +42,42 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= -github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= -github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw= -github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g= +github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= +github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= +github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s= +github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk= +github.com/charmbracelet/glamour v0.7.0 h1:2BtKGZ4iVJCDfMF229EzbeR1QRKLWztO9dMtjmqZSng= +github.com/charmbracelet/glamour v0.7.0/go.mod h1:jUMh5MeihljJPQbJ/wf4ldw2+yBP59+ctV36jASy7ps= +github.com/charmbracelet/keygen v0.5.0 h1:XY0fsoYiCSM9axkrU+2ziE6u6YjJulo/b9Dghnw6MZc= +github.com/charmbracelet/keygen v0.5.0/go.mod h1:DfvCgLHxZ9rJxdK0DGw3C/LkV4SgdGbnliHcObV3L+8= +github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g= +github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8= +github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= +github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= +github.com/charmbracelet/ssh v0.0.0-20240604154955-a40c6a0d028f h1:DnNHMcvpjh51pFVuYCxf+pVNdfZ3w51gGAtDiuVmFEk= +github.com/charmbracelet/ssh v0.0.0-20240604154955-a40c6a0d028f/go.mod h1:LmMZag2g7ILMmWtDmU7dIlctUopwmb73KpPzj0ip1uk= +github.com/charmbracelet/wish v1.4.0 h1:pL1uVP/YuYgJheHEj98teZ/n6pMYnmlZq/fcHvomrfc= +github.com/charmbracelet/wish v1.4.0/go.mod h1:ew4/MjJVfW/akEO9KmrQHQv1F7bQRGscRMrA+KtovTk= +github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY= +github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U= +github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ= +github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA= +github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= +github.com/charmbracelet/x/input v0.1.2 h1:QJAZr33eOhDowkkEQ24rsJy4Llxlm+fRDf/cQrmqJa0= +github.com/charmbracelet/x/input v0.1.2/go.mod h1:LGBim0maUY4Pitjn/4fHnuXb4KirU3DODsyuHuXdOyA= +github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= +github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= +github.com/charmbracelet/x/termios v0.1.0 h1:y4rjAHeFksBAfGbkRDmVinMg7x7DELIGAFbdNvxg97k= +github.com/charmbracelet/x/termios v0.1.0/go.mod h1:H/EVv/KRnrYjz+fCYa9bsKdqF3S8ouDK0AZEbG7r+/U= +github.com/charmbracelet/x/windows v0.1.2 h1:Iumiwq2G+BRmgoayww/qfcvof7W/3uLoelhxojXlRWg= +github.com/charmbracelet/x/windows v0.1.2/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= +github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= +github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -49,6 +89,10 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeC github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -85,6 +129,8 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/csrf v1.7.0/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= @@ -99,6 +145,8 @@ github.com/gotuna/gotuna v0.6.0 h1:N1lQKXEi/lwRp8u3sccTYLhzOffA4QasExz/1M5Riws= github.com/gotuna/gotuna v0.6.0/go.mod h1:F/ecRt29ChB6Ycy1AFIBpBiMNK0j7Heq+gFbLWquhjc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -108,17 +156,30 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/lrstanley/bubblezone v0.0.0-20240624011428-67235275f80c h1:hu82xYs8yOIM1TSq+L5VIZeRsHVROpe3gL0qscUlXJA= +github.com/lrstanley/bubblezone v0.0.0-20240624011428-67235275f80c/go.mod h1:fMHACHXouhQO+NLAFvHEeKdVSzG7L/O1khqsvswCTmk= 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-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= +github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +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= @@ -126,6 +187,8 @@ github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1n github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -154,12 +217,21 @@ github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= +github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= +github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s= +github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= @@ -218,6 +290,7 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/contribs/gnobro/cmd/gnobro/dev.go b/contribs/gnodev/pkg/browser/client_dev.go similarity index 97% rename from contribs/gnobro/cmd/gnobro/dev.go rename to contribs/gnodev/pkg/browser/client_dev.go index 0a4d7627286..443c0b29172 100644 --- a/contribs/gnobro/cmd/gnobro/dev.go +++ b/contribs/gnodev/pkg/browser/client_dev.go @@ -1,4 +1,4 @@ -package main +package browser import ( "context" @@ -14,8 +14,9 @@ import ( "github.com/gorilla/websocket" ) +const MaxElapsedTime = time.Second * 20 + var ErrHandlerNotSet = errors.New("handler not set") -var MaxElapsedTime = time.Second * 20 type DevClient struct { Logger *slog.Logger diff --git a/contribs/gnodev/pkg/browser/client_node.go b/contribs/gnodev/pkg/browser/client_node.go new file mode 100644 index 00000000000..303c558153e --- /dev/null +++ b/contribs/gnodev/pkg/browser/client_node.go @@ -0,0 +1,116 @@ +package browser + +import ( + "errors" + "fmt" + "log/slog" + "regexp" + "strings" + + "github.com/gnolang/gno/gno.land/pkg/gnoclient" + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + "github.com/gnolang/gno/tm2/pkg/amino" +) + +var ( + ErrInternalError = errors.New("internal error") + ErrRenderNotFound = errors.New("render not found") +) + +type NodeClient struct { + base gnoclient.BaseTxCfg + client *gnoclient.Client + logger *slog.Logger +} + +func NewNodeClient(logger *slog.Logger, base gnoclient.BaseTxCfg, client *gnoclient.Client) *NodeClient { + return &NodeClient{ + base: base, + client: client, + logger: logger, + } +} + +func (bl *NodeClient) Call(path, call string) ([]byte, error) { + method, args, err := parseMethodToArgs(call) + if err != nil { + return nil, fmt.Errorf("unable to parse method/args: %w", err) + } + + if len(args) == 0 { + args = nil + } + + cm, err := bl.client.Call(bl.base, gnoclient.MsgCall{ + PkgPath: path, + FuncName: method, + Args: args, + }) + + if err != nil { + return nil, err + } + + if cm.CheckTx.Error != nil { + return nil, fmt.Errorf("check error: %w", err) + } + + if cm.DeliverTx.Error != nil { + return nil, fmt.Errorf("delivry error: %w", err) + } + + return cm.DeliverTx.Data, nil +} + +func (bl *NodeClient) Funcs(path string) (vm.FunctionSignatures, error) { + res, err := bl.client.Query(gnoclient.QueryCfg{ + Path: "vm/qfuncs", + Data: []byte(path), + }) + if err != nil { + return nil, err + } + + if err := res.Response.Error; err != nil { + return nil, err + } + + var fsigs vm.FunctionSignatures + amino.MustUnmarshalJSON(res.Response.Data, &fsigs) + return fsigs, nil +} + +func (bl *NodeClient) Render(path, args string) ([]byte, error) { + data, res, err := bl.client.Render(path, args) + if err != nil { + return nil, err + } + if err := res.Response.Error; err != nil { + return nil, err + } + + return []byte(data), nil +} + +var reMethod = regexp.MustCompile(`([^(]+)\(([^)]*)\)`) + +func parseMethodToArgs(call string) (method string, args []string, err error) { + matches := reMethod.FindStringSubmatch(call) + if len(matches) == 0 { + err = fmt.Errorf("invalid call: %w", err) + return + } + + method = matches[1] + sargs := matches[2] + if sargs == "" { + return + } + + // Splitting arguments by comma + args = strings.Split(sargs, ",") + for i, arg := range args { + args[i] = strings.Trim(strings.TrimSpace(arg), "\"") + } + return +} diff --git a/contribs/gnobro/cmd/gnobro/list.go b/contribs/gnodev/pkg/browser/list_model.go similarity index 94% rename from contribs/gnobro/cmd/gnobro/list.go rename to contribs/gnodev/pkg/browser/list_model.go index ef521c416f2..86f1813d48b 100644 --- a/contribs/gnobro/cmd/gnobro/list.go +++ b/contribs/gnodev/pkg/browser/list_model.go @@ -1,4 +1,4 @@ -package main +package browser import ( "fmt" @@ -32,6 +32,12 @@ func (m *FuncListModel) SetItems(items []list.Item) { m.items = items } +func (m FuncListModel) Update(msg tea.Msg) (FuncListModel, tea.Cmd) { + var cmd tea.Cmd + m.Model, cmd = m.Model.Update(msg) + return m, cmd +} + func (m *FuncListModel) OriginItems() []list.Item { return m.items } @@ -45,6 +51,11 @@ func (m *FuncListModel) Reset() { } func (m *FuncListModel) FilterItems(pattern string) { + if pattern == "" { + m.Reset() + return + } + i := strings.IndexRune(pattern, '(') if i > 0 { pattern = pattern[:i] diff --git a/contribs/gnodev/pkg/browser/model.go b/contribs/gnodev/pkg/browser/model.go new file mode 100644 index 00000000000..f55fe5529b3 --- /dev/null +++ b/contribs/gnodev/pkg/browser/model.go @@ -0,0 +1,566 @@ +package browser + +import ( + "bytes" + clist "container/list" + "errors" + "fmt" + "log/slog" + "path/filepath" + "regexp" + "strings" + + "github.com/charmbracelet/bubbles/list" + "github.com/charmbracelet/bubbles/textinput" + "github.com/charmbracelet/bubbles/viewport" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + zone "github.com/lrstanley/bubblezone" + + "github.com/gnolang/gno/gno.land/pkg/gnoclient" + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + "github.com/gnolang/gno/tm2/pkg/log" +) + +var promptStyle = func(r *lipgloss.Renderer) lipgloss.Style { + return r.NewStyle(). + Bold(true). + Foreground(lipgloss.Color("#dd7878")) +} + +var ( + ErrEmptyRenderer = errors.New("empty rendrer") +) + +type Config struct { + URLPrefix string + URLDefaultValue string + Logger *slog.Logger + Banner string + Renderer *lipgloss.Renderer + GnoClient *gnoclient.Client + Readonly bool +} + +const DefaultGnoLandPrefix = "gno.land/" + +func DefaultConfig() Config { + return Config{ + Logger: log.NewNoopLogger(), + URLPrefix: "gno.land/", + Renderer: lipgloss.DefaultRenderer(), + URLDefaultValue: "gnoland/home", + } +} + +type model struct { + render *lipgloss.Renderer + client *NodeClient + logger *slog.Logger + + // misc + banner string + + // Viewport + zone *zone.Manager + ready bool + viewport viewport.Model + height, width int + readonly bool + messageDisplay bool + + // Nav + taskLoader LoaderModel + + pageurls map[string]string + history *clist.List + current *clist.Element + + // Url + urlInput textinput.Model + urlPrefix string + + // Commands + listFuncs FuncListModel + commandInput textinput.Model + commandFocus bool +} + +func initURLInput(prefix string, r *lipgloss.Renderer) textinput.Model { + ti := textinput.New() + ti.Placeholder = "r/gnoland/blog" // XXX: Use as example, customize this ? + ti.Focus() + ti.CharLimit = 156 + ti.PromptStyle = promptStyle(r) + ti.Prompt = prefix + "/" + + return ti +} + +func initCommandInput(r *lipgloss.Renderer) textinput.Model { + ti := textinput.New() + ti.Placeholder = "" + ti.CharLimit = 156 + ti.PromptStyle = promptStyle(r) + ti.Prompt = "> " + + return ti +} + +func New(cfg Config) tea.Model { + renderer := lipgloss.DefaultRenderer() + if cfg.Renderer != nil { + renderer = cfg.Renderer + } + + // Setup url input + urlinput := initURLInput(cfg.URLPrefix, renderer) + + path := cleanupRealmPath(cfg.URLPrefix, cfg.URLDefaultValue) + urlinput.SetValue(path) + + // Setup cmd input + cmdinput := initCommandInput(renderer) + + // XXX: Customize this + base := gnoclient.BaseTxCfg{ + GasFee: "1000000ugnot", + GasWanted: 2000000, + } + + nodeclient := NewNodeClient(cfg.Logger, base, cfg.GnoClient) + return &model{ + logger: cfg.Logger, + banner: cfg.Banner, + render: cfg.Renderer, + readonly: cfg.Readonly, + client: nodeclient, + listFuncs: newFuncList(), + taskLoader: NewTasksManager(3), + + urlInput: urlinput, + urlPrefix: cfg.URLPrefix, + + commandInput: cmdinput, + + zone: zone.New(), + pageurls: map[string]string{}, + history: clist.New(), + } +} + +func (m model) Init() tea.Cmd { + m.history.Init() + return nil +} + +type fetchRealmMsg struct { + realmPath string +} + +func FetchRealm(path string) tea.Cmd { + return func() tea.Msg { return fetchRealmMsg{path} } +} + +func RefreshRealm() tea.Cmd { + return func() tea.Msg { return fetchRealmMsg{""} } +} + +type renderUpdateMsg struct { + Render []byte + Funcs vm.FunctionSignatures + Error error +} + +func (m *model) RenderUpdate(path string) tea.Cmd { + return func() tea.Msg { + var msg renderUpdateMsg + var err error + msg.Render, err = m.fetchRenderView(path) + if err != nil { + msg.Error = fmt.Errorf("unable to fetch view: %w", err) + return msg + } + + msg.Funcs, err = m.fetchFuncsList(path) + if err != nil { + msg.Error = fmt.Errorf("unable to fetch function list: %w", err) + return msg + } + + return msg + } +} + +type execCommandRequestMsg struct { + Path string + Command string +} + +func (m *model) ExecCommandRequest(path, command string) tea.Cmd { + return func() tea.Msg { + return execCommandRequestMsg{path, command} + } +} + +type execCommandMsg struct { + Response []byte + Error error +} + +func (m *model) ExecCommand(path, command string) tea.Cmd { + return func() tea.Msg { + res, err := m.client.Call(path, command) + return execCommandMsg{res, err} + } +} + +func (m *model) ExtendCommandInput() bool { + if !m.commandInput.Focused() { + return false + } + + if item, ok := m.listFuncs.SelectedItem().(itemFunc); ok { + var value string + if len(item.Params) > 0 { + value = item.Title() + "(" + } else { + value = item.Title() + "()" + } + + currentValue := m.commandInput.Value() + if len(value) > len(currentValue) && strings.HasPrefix(value, currentValue) { + m.commandInput.SetValue(value) + return true + } + + // Put cursor at the end + m.commandInput.CursorEnd() + } + + return false +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmd tea.Cmd + + switch msg := msg.(type) { + case fetchRealmMsg: + if msg.realmPath != "" { + cmd = m.moveToRealm(msg.realmPath) + } else { + path := m.getCurrentPath() + m.logger.Info("rendering realm", "path", path) + cmd = m.RenderUpdate(m.getCurrentPath()) + } + + return m, tea.Sequence(m.taskLoader.Add(1), cmd) + + case execCommandRequestMsg: + m.logger.Info("requesting command", "path", msg.Path, "cmd", msg.Command) + cmd = m.ExecCommand(msg.Path, msg.Command) + return m, tea.Sequence(m.taskLoader.Add(1), cmd) + + case execCommandMsg: + m.taskLoader.Done() + + if msg.Error != nil { + m.logger.Warn("command exec", "error", msg.Error) + content := fmt.Sprintf( + "%s\n\npress [enter] to dismiss", + warpLine(msg.Error.Error(), m.viewport.Width), + ) + m.viewport.SetContent(content) + m.messageDisplay = true + } else { + content := string(msg.Response) + if strings.TrimSpace(content) == "" { + cmd = RefreshRealm() + m.messageDisplay = false + } else { + m.viewport.SetContent(fmt.Sprintf("%s\n\npress [enter] to dismiss", content)) + m.messageDisplay = true + } + } + + return m, cmd + + case renderUpdateMsg: + m.taskLoader.Done() + + var content string + if err := msg.Error; err != nil { + m.logger.Warn("render", "error", msg.Error) + // Write error to the frame + content = fmt.Sprintf("ERROR: %s", err.Error()) + } else { + content = string(m.findAndMarkURLs(msg.Render)) + } + + if len(msg.Funcs) > 0 { + items := make([]list.Item, 0, len(msg.Funcs)) + for _, fun := range msg.Funcs { + if fun.FuncName != "Render" { + items = append(items, itemFunc(fun)) + } + } + m.listFuncs.SetItems(items) + m.listFuncs.FilterItems(m.commandInput.Value()) + + // Update funcs list + m.listFuncs.Title = m.urlInput.Value() + m.listFuncs.SetSize(m.viewport.Width, 7) + } + + m.viewport.SetContent(content) + + return m, cmd + case SpinnerTickMsg: + if m.taskLoader.Active() { + m.taskLoader, cmd = m.taskLoader.Update(msg) + } + + case tea.MouseMsg: + cmd = m.updateMouse(msg) + + // Fallback on viewport + if cmd == nil { + m.viewport, cmd = m.viewport.Update(msg) + } + + return m, cmd + + case tea.KeyMsg: + cmd = m.updateKey(msg) + m.listFuncs.FilterItems(m.commandInput.Value()) + if !m.readonly && cmd == nil { + m.listFuncs, cmd = m.listFuncs.Update(msg) + } + + // Fallback on list funcs update + if cmd == nil { + m.viewport, cmd = m.viewport.Update(msg) + } + + // Fallback on viewport update + return m, cmd + + case tea.WindowSizeMsg: + m.width = msg.Width + + headerHeight := lipgloss.Height(m.headerView()) + footerHeight := lipgloss.Height(m.footerView()) + verticalMarginHeight := headerHeight + footerHeight + + if !m.ready { + m.viewport = viewport.New(msg.Width, msg.Height-verticalMarginHeight) + m.viewport.YPosition = headerHeight + m.viewport.MouseWheelEnabled = true + m.viewport.MouseWheelDelta = 1 + m.ready = true + m.viewport.YPosition = headerHeight + 1 + + if value := m.urlInput.Value(); value != "" { + cmd = RefreshRealm() + m.updateHistory() + } + } else { + m.viewport.Width = msg.Width + m.viewport.Height = msg.Height - verticalMarginHeight + } + + m.height = m.viewport.Height + if !m.urlInput.Focused() && len(m.listFuncs.Items()) > 0 { + m.viewport.Height = m.height - lipgloss.Height(m.listFuncsView()) + } + + return m, cmd + } + + cmds := []tea.Cmd{cmd} + + var viewCmd tea.Cmd + m.viewport, viewCmd = m.viewport.Update(msg) + cmds = append(cmds, viewCmd) + + var funcCmd tea.Cmd + m.listFuncs, funcCmd = m.listFuncs.Update(msg) + cmds = append(cmds, funcCmd) + + return m, tea.Batch(cmds...) +} + +func (m *model) updateKey(msg tea.KeyMsg) tea.Cmd { + var cmd tea.Cmd + if m.banner != "" { + switch key := msg.String(); key { + case "ctrl+c": + return tea.Quit + case "enter": + m.banner = "" + } + // Discard other input while banner is active + return nil + } + + switch msg.String() { + case "alt+down": + if m.urlInput.Focused() && !m.readonly { + m.urlInput.Blur() + cmd = m.commandInput.Focus() + m.commandFocus = true + } + case "alt+up": + if m.commandInput.Focused() { + m.commandInput.Blur() + cmd = m.urlInput.Focus() + m.commandFocus = false + } + case "tab": + if m.commandInput.Focused() { + m.ExtendCommandInput() + } + case "alt+r": + cmd = RefreshRealm() + case "enter": + // Update command on focus + if m.commandInput.Focused() && !m.messageDisplay { + if len(m.listFuncs.Items()) == 1 { + path := m.getCurrentPath() + cmd = m.ExecCommand(path, m.commandInput.Value()) + } else { + m.ExtendCommandInput() + } + + break + } + + // Update url on focus + if m.messageDisplay || m.urlInput.Focused() { + m.listFuncs.Erase() + + cmd = m.moveToRealm(m.urlInput.Value()) + if m.current.Value.(string) != m.urlInput.Value() { + m.updateHistory() + } + + // Discard message + m.messageDisplay = false + } + + case "ctrl+c", "esc": + return tea.Quit + default: + // handle url input + if m.urlInput.Focused() { + m.urlInput, cmd = m.urlInput.Update(msg) + } + + if m.commandInput.Focused() { + // handle command input + m.commandInput, cmd = m.commandInput.Update(msg) + } + + } + + return cmd +} + +func (m *model) updateMouse(msg tea.MouseMsg) tea.Cmd { + if msg.Action != tea.MouseActionRelease { + return nil + } + + var cmd tea.Cmd + + switch { + case m.zone.Get("prev_button").InBounds(msg): + if path, ok := m.moveHistoryBackward(); ok { + cmd = m.moveToRealm(path) + } + case m.zone.Get("next_button").InBounds(msg): + if path, ok := m.moveHistoryForward(); ok { + cmd = m.moveToRealm(path) + } + case m.zone.Get("home_button").InBounds(msg): + if cmd = m.moveToRealm("gno.land/r/gnoland/home"); cmd != nil { + m.updateHistory() + } + + case m.zone.Get("url_input").InBounds(msg): + m.commandInput.Blur() + cmd = m.urlInput.Focus() + m.commandFocus = false + case !m.readonly && m.zone.Get("command_input").InBounds(msg): + m.urlInput.Blur() + cmd = m.commandInput.Focus() + m.commandFocus = true + default: + for mark := range m.pageurls { + if !m.zone.Get(mark).InBounds(msg) { + continue + } + + if uri := m.pageurls[mark]; uri != "" { + if cmd = m.moveToRealm(uri); cmd != nil { + m.updateHistory() + break + } + } + } + } + + return cmd +} + +// realm path surrounded by ansi escape sequences +var reUrlPattern = regexp.MustCompile(`(?mU)\x1b[^m]*m(?:(?:https?://)?gno.land)?(/[^\s]+)\x1b[^m]*m`) + +func (m model) findAndMarkURLs(body []byte) []byte { + var buf bytes.Buffer + lastIndex := 0 + + indexes := reUrlPattern.FindAllSubmatchIndex(body, -1) + for i, loc := range indexes { + match := string(body[loc[0]:loc[1]]) + uri := string(body[loc[2]:loc[3]]) + markid := fmt.Sprintf("url_%d", i) + + // Write bytes before match + buf.Write(body[lastIndex:loc[0]]) + + // Write quoted URL + buf.WriteString(m.zone.Mark(markid, match)) + m.pageurls[markid] = uri + lastIndex = loc[1] + } + // Write remaining bytes + buf.Write(body[lastIndex:]) + + // Cleanup previous urls + for i := len(indexes); i < len(m.pageurls); i++ { + markid := fmt.Sprintf("url_%d", i) + delete(m.pageurls, markid) + } + + return buf.Bytes() +} + +func (m model) fetchFuncsList(path string) (view vm.FunctionSignatures, err error) { + rlmpath, _, _ := strings.Cut(path, ":") + funcs, err := m.client.Funcs(rlmpath) + if err != nil { + return nil, fmt.Errorf("unable to fetch Render: %w", err) + } + + return funcs, nil +} + +func (m *model) getCurrentPath() string { + path := strings.Trim(m.urlInput.Value(), "/") + if len(path) == 0 { + return m.urlPrefix + } + + return filepath.Join(m.urlPrefix, path) +} diff --git a/contribs/gnodev/pkg/browser/model_nav.go b/contribs/gnodev/pkg/browser/model_nav.go new file mode 100644 index 00000000000..ce7a5564209 --- /dev/null +++ b/contribs/gnodev/pkg/browser/model_nav.go @@ -0,0 +1,82 @@ +package browser + +import ( + "fmt" + "strings" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/glamour" +) + +func (m *model) moveToRealm(realm string) tea.Cmd { + path := cleanupRealmPath(m.urlPrefix, realm) + + // Set uri input + m.urlInput.SetValue(path) + m.urlInput.CursorEnd() + + // return command update + return tea.Sequence(RefreshRealm(), m.urlInput.Focus()) +} + +func (m *model) updateHistory() { + v := m.urlInput.Value() + if m.history.Len() == 0 { + m.current = m.history.PushBack(v) + return + } + + m.current = m.history.InsertAfter(v, m.current) + for next := m.current.Next(); next != nil; { + m.history.Remove(next) + next = m.current.Next() + } +} + +func (m *model) moveHistoryForward() (string, bool) { + if next := m.current.Next(); next != nil { + m.current = next + return m.current.Value.(string), true + } + return "", false +} + +func (m *model) moveHistoryBackward() (string, bool) { + if prev := m.current.Prev(); prev != nil { + m.current = prev + return m.current.Value.(string), true + } + return "", false +} + +func (m *model) updateHistoryBackward() { + v := m.urlInput.Value() + if m.history.Len() == 0 { + m.current = m.history.PushBack(v) + } else { + m.current = m.history.InsertAfter(v, m.current) + } +} + +func (m model) fetchRenderView(path string) (view []byte, err error) { + rlmpath, args, _ := strings.Cut(path, ":") + res, err := m.client.Render(rlmpath, args) + if err != nil { + return nil, fmt.Errorf("unable to fetch Render: %w", err) + } + + r, err := glamour.NewTermRenderer( + glamour.WithStyles(CatppuccinStyleConfig), // XXX: use gno custom theme + glamour.WithWordWrap(m.viewport.Width), + ) + if err != nil { + return nil, fmt.Errorf("unable to get render view: %w", err) + } + + view, err = r.RenderBytes(res) + if err != nil { + return nil, fmt.Errorf("uanble to render markdown view: %w", err) + } + + return view, nil +} diff --git a/contribs/gnodev/pkg/browser/model_style.go b/contribs/gnodev/pkg/browser/model_style.go new file mode 100644 index 00000000000..f3c3a18fd96 --- /dev/null +++ b/contribs/gnodev/pkg/browser/model_style.go @@ -0,0 +1,239 @@ +package browser + +import "github.com/charmbracelet/glamour/ansi" + +const ( + defaultListIndent = 2 + defaultListLevelIndent = 4 + defaultMargin = 2 +) + +// Catpuccin style: https://github.com/catppuccin/catppuccin +// XXX: update this with `gno` colors scheme +var CatppuccinStyleConfig = ansi.StyleConfig{ + Document: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{ + BlockPrefix: "\n", + BlockSuffix: "\n", + Color: stringPtr("#cad3f5"), + }, + Margin: uintPtr(defaultMargin), + }, + BlockQuote: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{ + Color: stringPtr("#cad3f5"), + Italic: boolPtr(true), + }, + Indent: uintPtr(1), + }, + List: ansi.StyleList{ + LevelIndent: defaultListIndent, + StyleBlock: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{ + Color: stringPtr("#cad3f5"), + }, + }, + }, + Heading: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{ + BlockSuffix: "\n", + Color: stringPtr("#cad3f5"), + Bold: boolPtr(true), + }, + }, + H1: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{ + Prefix: " ", + Suffix: " ", + BackgroundColor: stringPtr("#f0c6c6"), + Color: stringPtr("#181926"), + Bold: boolPtr(true), + }, + }, + H2: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{ + Prefix: "● ", + Color: stringPtr("#f5a97f"), + }, + }, + H3: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{ + Prefix: "β—‰ ", + Color: stringPtr("#eed49f"), + }, + }, + H4: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{ + Prefix: "β—‹ ", + Color: stringPtr("#a6da95"), + }, + }, + H5: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{ + Prefix: "β—Œ ", + Color: stringPtr("#7dc4e4"), + }, + }, + H6: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{ + Prefix: "β€£ ", + Color: stringPtr("#b7bdf8"), + }, + }, + Strikethrough: ansi.StylePrimitive{ + CrossedOut: boolPtr(true), + }, + Emph: ansi.StylePrimitive{ + Color: stringPtr("#cad3f5"), + Italic: boolPtr(true), + }, + Strong: ansi.StylePrimitive{ + Bold: boolPtr(true), + Color: stringPtr("#cad3f5"), + }, + HorizontalRule: ansi.StylePrimitive{ + Color: stringPtr("#6e738d"), + Format: "\n--------\n", + }, + Item: ansi.StylePrimitive{ + BlockPrefix: "β€’ ", + }, + Enumeration: ansi.StylePrimitive{ + BlockPrefix: ". ", + Color: stringPtr("#cad3f5"), + }, + Task: ansi.StyleTask{ + StylePrimitive: ansi.StylePrimitive{}, + Ticked: "[βœ“] ", + Unticked: "[ ] ", + }, + Link: ansi.StylePrimitive{ + Color: stringPtr("#8aadf4"), + Underline: boolPtr(true), + }, + LinkText: ansi.StylePrimitive{ + Color: stringPtr("#b7bdf8"), + }, + Image: ansi.StylePrimitive{ + Color: stringPtr("#8aadf4"), + Underline: boolPtr(true), + }, + ImageText: ansi.StylePrimitive{ + Color: stringPtr("#b7bdf8"), + Format: "Image: {{.text}} β†’", + }, + Code: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{ + Color: stringPtr("#ee99a0"), + }, + }, + CodeBlock: ansi.StyleCodeBlock{ + StyleBlock: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{ + Color: stringPtr("#1e2030"), + }, + Margin: uintPtr(defaultMargin), + }, + Chroma: &ansi.Chroma{ + Text: ansi.StylePrimitive{ + Color: stringPtr("#cad3f5"), + }, + Error: ansi.StylePrimitive{ + Color: stringPtr("#cad3f5"), + BackgroundColor: stringPtr("#ed8796"), + }, + Comment: ansi.StylePrimitive{ + Color: stringPtr("#6e738d"), + }, + CommentPreproc: ansi.StylePrimitive{ + Color: stringPtr("#8aadf4"), + }, + Keyword: ansi.StylePrimitive{ + Color: stringPtr("#c6a0f6"), + }, + KeywordReserved: ansi.StylePrimitive{ + Color: stringPtr("#c6a0f6"), + }, + KeywordNamespace: ansi.StylePrimitive{ + Color: stringPtr("#eed49f"), + }, + KeywordType: ansi.StylePrimitive{ + Color: stringPtr("#eed49f"), + }, + Operator: ansi.StylePrimitive{ + Color: stringPtr("#91d7e3"), + }, + Punctuation: ansi.StylePrimitive{ + Color: stringPtr("#939ab7"), + }, + Name: ansi.StylePrimitive{ + Color: stringPtr("#b7bdf8"), + }, + NameBuiltin: ansi.StylePrimitive{ + Color: stringPtr("#f5a97f"), + }, + NameTag: ansi.StylePrimitive{ + Color: stringPtr("#c6a0f6"), + }, + NameAttribute: ansi.StylePrimitive{ + Color: stringPtr("#eed49f"), + }, + NameClass: ansi.StylePrimitive{ + Color: stringPtr("#eed49f"), + }, + NameConstant: ansi.StylePrimitive{ + Color: stringPtr("#eed49f"), + }, + NameDecorator: ansi.StylePrimitive{ + Color: stringPtr("#f5bde6"), + }, + NameFunction: ansi.StylePrimitive{ + Color: stringPtr("#8aadf4"), + }, + LiteralNumber: ansi.StylePrimitive{ + Color: stringPtr("#f5a97f"), + }, + LiteralString: ansi.StylePrimitive{ + Color: stringPtr("#a6da95"), + }, + LiteralStringEscape: ansi.StylePrimitive{ + Color: stringPtr("#f5bde6"), + }, + GenericDeleted: ansi.StylePrimitive{ + Color: stringPtr("#ed8796"), + }, + GenericEmph: ansi.StylePrimitive{ + Color: stringPtr("#cad3f5"), + Italic: boolPtr(true), + }, + GenericInserted: ansi.StylePrimitive{ + Color: stringPtr("#a6da95"), + }, + GenericStrong: ansi.StylePrimitive{ + Color: stringPtr("#cad3f5"), + Bold: boolPtr(true), + }, + GenericSubheading: ansi.StylePrimitive{ + Color: stringPtr("#91d7e3"), + }, + Background: ansi.StylePrimitive{ + BackgroundColor: stringPtr("#1e2030"), + }, + }, + }, + Table: ansi.StyleTable{ + StyleBlock: ansi.StyleBlock{ + StylePrimitive: ansi.StylePrimitive{}, + }, + CenterSeparator: stringPtr("β”Ό"), + ColumnSeparator: stringPtr("β”‚"), + RowSeparator: stringPtr("─"), + }, + DefinitionDescription: ansi.StylePrimitive{ + BlockPrefix: "\n🠢 ", + }, +} + +func boolPtr(b bool) *bool { return &b } +func stringPtr(s string) *string { return &s } +func uintPtr(u uint) *uint { return &u } diff --git a/contribs/gnodev/pkg/browser/model_tasks.go b/contribs/gnodev/pkg/browser/model_tasks.go new file mode 100644 index 00000000000..353d2cc3d55 --- /dev/null +++ b/contribs/gnodev/pkg/browser/model_tasks.go @@ -0,0 +1,85 @@ +// modified version of ""github.com/charmbracelet/bubbles/spinner" + +package browser + +import ( + "time" + + tea "github.com/charmbracelet/bubbletea" +) + +type Spinner struct { + Frames []string + FPS time.Duration +} + +// TickMsg indicates that the timer has ticked and we should render a frame. +type SpinnerTickMsg time.Time + +var MeterLoader = Spinner{ + Frames: []string{ + "β–±β–±β–±β–±β–±β–±β–±β–±", "β–°β–±β–±β–±β–±β–±β–±β–±", "β–°β–°β–±β–±β–±β–±β–±β–±", "β–°β–°β–°β–±β–±β–±β–±β–±", + "β–°β–°β–°β–°β–±β–±β–±β–±", "β–°β–°β–°β–°β–°β–±β–±β–±", "β–°β–°β–°β–°β–°β–°β–±β–±", "β–°β–°β–°β–°β–°β–°β–°β–±", + "β–°β–°β–°β–°β–°β–°β–°β–°", "β–±β–°β–°β–°β–°β–°β–°β–°", "β–±β–±β–°β–°β–°β–°β–°β–°", "β–±β–±β–±β–°β–°β–°β–°β–°", + "β–±β–±β–±β–±β–°β–°β–°β–°", "β–±β–±β–±β–±β–±β–°β–°β–°", "β–±β–±β–±β–±β–±β–±β–°β–°", "β–±β–±β–±β–±β–±β–±β–±β–°", + }, + FPS: time.Second / 70, //nolint:gomnd +} + +type LoaderModel struct { + spinner Spinner + frame int + task int +} + +func NewTasksManager(tlimit int) LoaderModel { + return LoaderModel{ + spinner: MeterLoader, + } +} + +func (m LoaderModel) Update(msg tea.Msg) (LoaderModel, tea.Cmd) { + switch msg.(type) { + case SpinnerTickMsg: + m.frame = (m.frame + 1) % len(m.spinner.Frames) + return m, m.tick() + default: + return m, nil + } +} + +func (m LoaderModel) tick() tea.Cmd { + return tea.Tick(m.spinner.FPS, func(t time.Time) tea.Msg { + return SpinnerTickMsg(t) + }) +} + +func (m LoaderModel) Tick() tea.Msg { + return SpinnerTickMsg(time.Now()) +} + +func (m *LoaderModel) Active() bool { + return m.frame > 0 || m.task > 0 +} + +func (m *LoaderModel) Add(i int) tea.Cmd { + var cmd tea.Cmd + if i > 0 { + if m.task == 0 { + cmd = m.Tick + } + + m.task += i + } + return cmd +} + +func (m *LoaderModel) Done() { + if m.task > 0 { + m.task -= 1 + } +} + +func (m *LoaderModel) View() string { + return m.spinner.Frames[m.frame] +} diff --git a/contribs/gnodev/pkg/browser/model_view.go b/contribs/gnodev/pkg/browser/model_view.go new file mode 100644 index 00000000000..08ac2029d0e --- /dev/null +++ b/contribs/gnodev/pkg/browser/model_view.go @@ -0,0 +1,167 @@ +package browser + +import ( + "fmt" + "strings" + + "github.com/charmbracelet/lipgloss" +) + +var ( + boxRoundedStyle = func(r *lipgloss.Renderer) lipgloss.Style { + b := lipgloss.RoundedBorder() + return r.NewStyle(). + BorderStyle(b). + Padding(0, 2) + } + + inputStyleLeft = func(r *lipgloss.Renderer) lipgloss.Style { + b := lipgloss.RoundedBorder() + b.Right = "β”œ" + return r.NewStyle(). + BorderStyle(b). + Padding(0, 2) + } + + infoStyle = func(r *lipgloss.Renderer) lipgloss.Style { + b := lipgloss.RoundedBorder() + b.Left = "─" + return boxRoundedStyle(r).Copy().BorderStyle(b) + } +) + +func (m model) View() string { + if m.banner != "" { + return m.bannerView() + } + + if !m.ready { + return "+" + } + + mainView := fmt.Sprintf("%s\n%s\n%s", m.headerView(), m.bodyView(), m.footerView()) + return m.zone.Scan(mainView) +} + +func (m model) bannerView() string { + if m.width == 0 || m.height == 0 { + return "x" + } + + banner := m.render.NewStyle().Padding(1, 5). + Border(lipgloss.DoubleBorder(), true, false, true). + Render(m.banner) + + return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, banner) +} + +func (m model) listFuncsView() string { + return boxRoundedStyle(m.render). + Render(m.listFuncs.View()) +} + +func (m model) bodyView() string { + if m.commandInput.Focused() { + // handle command input + if v := m.commandInput.Value(); v != "" { + m.listFuncs.FilterItems(v) + } else { + m.listFuncs.Reset() + } + + if len(m.listFuncs.Items()) > 0 { + m.viewport.Height = m.height - lipgloss.Height(m.listFuncsView()) + } else { + m.viewport.Height = m.height + } + } + + return m.viewport.View() +} + +var ( + loadingStyle = func(r *lipgloss.Renderer) lipgloss.Style { + return r.NewStyle(). + Foreground(lipgloss.Color("#dd7878")). + Bold(true) + } + + navStyleEnable = func(r *lipgloss.Renderer) lipgloss.Style { + return r.NewStyle(). + Foreground(lipgloss.Color("#fab387")) + } + + navStyleDisable = func(r *lipgloss.Renderer) lipgloss.Style { + return r.NewStyle(). + Foreground(lipgloss.Color("240")) + } +) + +func (m model) navView() string { + home := navStyleEnable(m.render).Padding(0, 1).Render("[Home]") + + var style lipgloss.Style + if m.current != nil && m.current.Prev() != nil { + style = navStyleEnable(m.render) + } else { + style = navStyleDisable(m.render) + } + prev := style.Margin(0, 1).Render("") + + title := m.render.NewStyle().Bold(true).Render("Gno.Land") + if m.taskLoader.Active() { + title = loadingStyle(m.render).Render(m.taskLoader.View()) + } + + spaceWidth := m.width / 3 // left middle and right + return lipgloss.JoinHorizontal(lipgloss.Left, + m.render.NewStyle().Width(spaceWidth).Padding(0, 1). + Render(lipgloss.JoinHorizontal(lipgloss.Left, + m.zone.Mark("prev_button", prev), + m.zone.Mark("next_button", next), + )), + m.render.PlaceHorizontal(spaceWidth, lipgloss.Center, title), + m.render.PlaceHorizontal(spaceWidth, lipgloss.Right, + m.zone.Mark("home_button", home)), + ) +} + +func (m model) headerView() string { + return lipgloss.JoinVertical(lipgloss.Left, m.navView(), m.urlView()) +} + +func (m model) urlView() string { + return m.zone.Mark("url_input", boxRoundedStyle(m.render). + Width(m.viewport.Width-2). + Render(m.urlInput.View())) +} + +func (m model) footerView() string { + info := infoStyle(m.render).Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) + + if m.readonly { + // On readonly, simply discard command input interface + line := strings.Repeat("─", max(0, m.viewport.Width-lipgloss.Width(info))) + return lipgloss.JoinHorizontal(lipgloss.Center, line, info) + } + + command := m.zone.Mark("command_input", inputStyleLeft(m.render). + Width(m.viewport.Width-lipgloss.Width(info)-5). + Render(m.commandInput.View())) + line := strings.Repeat("─", 3) + + powerline := lipgloss.JoinHorizontal(lipgloss.Center, command, line, info) + if m.commandFocus && len(m.listFuncs.Items()) > 0 { + suggestions := m.listFuncsView() + return lipgloss.JoinVertical(lipgloss.Left, suggestions, powerline) + } + + return powerline +} diff --git a/contribs/gnodev/pkg/browser/utils.go b/contribs/gnodev/pkg/browser/utils.go new file mode 100644 index 00000000000..18398c029b8 --- /dev/null +++ b/contribs/gnodev/pkg/browser/utils.go @@ -0,0 +1,40 @@ +package browser + +import ( + "path/filepath" + "strings" + + "github.com/gnolang/gno/gno.land/pkg/gnoweb" + "github.com/muesli/reflow/wordwrap" +) + +func warpLine(str string, width int) string { + f := wordwrap.NewWriter(width) + f.Write([]byte(str)) + return f.String() +} + +func redirectWebPath(path string) string { + if alias, ok := gnoweb.Aliases[path]; ok { + return alias + } + + if redirect, ok := gnoweb.Redirects[path]; ok { + return redirect + } + + return path +} + +func cleanupRealmPath(prefix, realm string) string { + // Trim prefix + path := strings.TrimPrefix(realm, prefix) + // redirect if any well known path + path = redirectWebPath(path) + // trim any slash + path = strings.TrimPrefix(path, "/") + // clean up path + path = filepath.Clean(path) + + return path +} From 79fbcbe79f1257341577f807bc57edcd85956e9b Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Sun, 21 Jul 2024 21:38:41 +0200 Subject: [PATCH 22/64] chore: delete banner file Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/banner.go | 34 ---------------------------- 1 file changed, 34 deletions(-) delete mode 100644 contribs/gnodev/cmd/gnobro/banner.go diff --git a/contribs/gnodev/cmd/gnobro/banner.go b/contribs/gnodev/cmd/gnobro/banner.go deleted file mode 100644 index ddd96291427..00000000000 --- a/contribs/gnodev/cmd/gnobro/banner.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - _ "embed" -) - -// //go:embed assets/*.txt -// var banners embed.FS - -// func getBannerFrames(prefix string) []string { -// files, err := banners.ReadDir("assets") -// if err != nil { -// panic(err) -// } - -// frames := []string{} -// for _, file := range files { -// if file.IsDir() { -// continue -// } - -// if !strings.HasPrefix(file.Name(), prefix) { -// continue -// } - -// frame, err := banners.ReadFile(filepath.Join("assets", file.Name())) -// if err != nil { -// panic(err) -// } - -// frames = append(frames, string(frame)) -// } -// return frames -// } From 4fb71d4d73f7c1778b9196ae60a2d42e2025af33 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Sun, 21 Jul 2024 21:58:38 +0200 Subject: [PATCH 23/64] chore: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 2 +- contribs/gnodev/cmd/gnobro/request.go | 119 --------------------- contribs/gnodev/pkg/browser/client_dev.go | 13 --- contribs/gnodev/pkg/browser/client_node.go | 1 - contribs/gnodev/pkg/browser/list_model.go | 5 +- contribs/gnodev/pkg/browser/model.go | 5 +- contribs/gnodev/pkg/browser/model_nav.go | 9 -- contribs/gnodev/pkg/emitter/server_test.go | 2 +- 8 files changed, 4 insertions(+), 152 deletions(-) delete mode 100644 contribs/gnodev/cmd/gnobro/request.go diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index e73c422bf82..0a4b4cad414 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -408,7 +408,7 @@ func getSignerForAccount(io commands.IO, address string, kb keys.Keybase, cfg *b return nil, fmt.Errorf("error while reading password: %w", err) } - if _, err := kb.ExportPrivKeyUnsafe(address, string(signer.Password)); err != nil { + if _, err := kb.ExportPrivKeyUnsafe(address, signer.Password); err != nil { return nil, fmt.Errorf("invalid password: %w", err) } } diff --git a/contribs/gnodev/cmd/gnobro/request.go b/contribs/gnodev/cmd/gnobro/request.go deleted file mode 100644 index a04a54b3f52..00000000000 --- a/contribs/gnodev/cmd/gnobro/request.go +++ /dev/null @@ -1,119 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "log/slog" - "regexp" - "strings" - - "github.com/gnolang/gno/gno.land/pkg/gnoclient" - "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - "github.com/gnolang/gno/tm2/pkg/amino" -) - -const remoteAddr = "http://localhost:36657" -const qFileStr = "vm/qfile" - -var ( - ErrInternalError = errors.New("internal error") - ErrRenderNotFound = errors.New("render not found") -) - -type BroClient struct { - base gnoclient.BaseTxCfg - client *gnoclient.Client - logger *slog.Logger -} - -func NewBroClient(logger *slog.Logger, base gnoclient.BaseTxCfg, client *gnoclient.Client) *BroClient { - return &BroClient{ - base: base, - client: client, - logger: logger, - } -} - -func (bl *BroClient) Call(path, call string) ([]byte, error) { - method, args, err := parseMethodToArgs(call) - if err != nil { - return nil, fmt.Errorf("unable to parse method/args: %w", err) - } - - if len(args) == 0 { - args = nil - } - - cm, err := bl.client.Call(bl.base, gnoclient.MsgCall{ - PkgPath: path, - FuncName: method, - Args: args, - }) - - if err != nil { - return nil, err - } - - if cm.CheckTx.Error != nil { - return nil, fmt.Errorf("check error: %w", err) - } - - if cm.DeliverTx.Error != nil { - return nil, fmt.Errorf("delivry error: %w", err) - } - - return cm.DeliverTx.Data, nil -} - -func (bl *BroClient) Funcs(path string) (vm.FunctionSignatures, error) { - res, err := bl.client.Query(gnoclient.QueryCfg{ - Path: "vm/qfuncs", - Data: []byte(path), - }) - if err != nil { - return nil, err - } - - if err := res.Response.Error; err != nil { - return nil, err - } - - var fsigs vm.FunctionSignatures - amino.MustUnmarshalJSON(res.Response.Data, &fsigs) - return fsigs, nil -} - -func (bl *BroClient) Render(path, args string) ([]byte, error) { - data, res, err := bl.client.Render(path, args) - if err != nil { - return nil, err - } - if err := res.Response.Error; err != nil { - return nil, err - } - - return []byte(data), nil -} - -var reMethod = regexp.MustCompile(`([^(]+)\(([^)]*)\)`) - -func parseMethodToArgs(call string) (method string, args []string, err error) { - matches := reMethod.FindStringSubmatch(call) - if len(matches) == 0 { - err = fmt.Errorf("invalid call: %w", err) - return - } - - method = matches[1] - sargs := matches[2] - if sargs == "" { - return - } - - // Splitting arguments by comma - args = strings.Split(sargs, ",") - for i, arg := range args { - args[i] = strings.Trim(strings.TrimSpace(arg), "\"") - } - return -} diff --git a/contribs/gnodev/pkg/browser/client_dev.go b/contribs/gnodev/pkg/browser/client_dev.go index 443c0b29172..a158e38373c 100644 --- a/contribs/gnodev/pkg/browser/client_dev.go +++ b/contribs/gnodev/pkg/browser/client_dev.go @@ -118,16 +118,3 @@ func (c *DevClient) handleEvents(ctx context.Context) error { } } } - -func CheckEndpoint(addr string) error { - resp, err := http.Head(addr) - if err != nil { - return fmt.Errorf("error while checking endpoint: %v", err) - } - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("endpoint returned status: %v", resp.StatusCode) - } - - return nil -} diff --git a/contribs/gnodev/pkg/browser/client_node.go b/contribs/gnodev/pkg/browser/client_node.go index 303c558153e..979f054bfd6 100644 --- a/contribs/gnodev/pkg/browser/client_node.go +++ b/contribs/gnodev/pkg/browser/client_node.go @@ -46,7 +46,6 @@ func (bl *NodeClient) Call(path, call string) ([]byte, error) { FuncName: method, Args: args, }) - if err != nil { return nil, err } diff --git a/contribs/gnodev/pkg/browser/list_model.go b/contribs/gnodev/pkg/browser/list_model.go index 86f1813d48b..db4656fbf24 100644 --- a/contribs/gnodev/pkg/browser/list_model.go +++ b/contribs/gnodev/pkg/browser/list_model.go @@ -14,12 +14,9 @@ import ( ) var ( - listTitleStyle = lipgloss.NewStyle().MarginLeft(2) listItemStyle = lipgloss.NewStyle().PaddingLeft(4) listSelectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170")) listPaginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4) - listHelpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1) - listQuitTextStyle = lipgloss.NewStyle().Margin(1, 0, 2, 4) ) type FuncListModel struct { @@ -66,7 +63,7 @@ func (m *FuncListModel) FilterItems(pattern string) { data[i] = item.FilterValue() } - var ranks = fuzzy.Find(pattern, data) + ranks := fuzzy.Find(pattern, data) sort.Stable(ranks) if len(ranks) > 0 && i > 0 { m.Model.SetItems([]list.Item{m.items[ranks[0].Index]}) diff --git a/contribs/gnodev/pkg/browser/model.go b/contribs/gnodev/pkg/browser/model.go index f55fe5529b3..5ec6a6d74a3 100644 --- a/contribs/gnodev/pkg/browser/model.go +++ b/contribs/gnodev/pkg/browser/model.go @@ -28,9 +28,7 @@ var promptStyle = func(r *lipgloss.Renderer) lipgloss.Style { Foreground(lipgloss.Color("#dd7878")) } -var ( - ErrEmptyRenderer = errors.New("empty rendrer") -) +var ErrEmptyRenderer = errors.New("empty rendrer") type Config struct { URLPrefix string @@ -460,7 +458,6 @@ func (m *model) updateKey(msg tea.KeyMsg) tea.Cmd { // handle command input m.commandInput, cmd = m.commandInput.Update(msg) } - } return cmd diff --git a/contribs/gnodev/pkg/browser/model_nav.go b/contribs/gnodev/pkg/browser/model_nav.go index ce7a5564209..6936f413d94 100644 --- a/contribs/gnodev/pkg/browser/model_nav.go +++ b/contribs/gnodev/pkg/browser/model_nav.go @@ -49,15 +49,6 @@ func (m *model) moveHistoryBackward() (string, bool) { return "", false } -func (m *model) updateHistoryBackward() { - v := m.urlInput.Value() - if m.history.Len() == 0 { - m.current = m.history.PushBack(v) - } else { - m.current = m.history.InsertAfter(v, m.current) - } -} - func (m model) fetchRenderView(path string) (view []byte, err error) { rlmpath, args, _ := strings.Cut(path, ":") res, err := m.client.Render(rlmpath, args) diff --git a/contribs/gnodev/pkg/emitter/server_test.go b/contribs/gnodev/pkg/emitter/server_test.go index 4725378dbda..8795d2da048 100644 --- a/contribs/gnodev/pkg/emitter/server_test.go +++ b/contribs/gnodev/pkg/emitter/server_test.go @@ -40,7 +40,7 @@ func TestServer_ServeHTTP(t *testing.T) { sendEvt := events.Custom("TEST") svr.Emit(sendEvt) // simulate reload - var recvEvt eventJSON + var recvEvt EventJSON err = c.ReadJSON(&recvEvt) require.NoError(t, err) assert.Equal(t, sendEvt.Type(), recvEvt.Type) From e4f86ccb69dbef942b09e120be07c793ca80bc48 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 22 Jul 2024 09:09:11 +0200 Subject: [PATCH 24/64] fix: remote dial Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 0a4b4cad414..ca2347bfc0f 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -165,10 +165,9 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { return fmt.Errorf("unable to get signer for account %q: %w", address, err) } - target := resolveUnixOrTCPAddr(cfg.remote) - cl, err := client.NewHTTPClient(target) + cl, err := client.NewHTTPClient(cfg.remote) if err != nil { - return fmt.Errorf("unable to create http client for %q: %w", target, err) + return fmt.Errorf("unable to create http client for %q: %w", cfg.remote, err) } gnocl := &gnoclient.Client{ From e24688bdcf15b5e2635420773bd5584c8832a323 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 22 Jul 2024 09:22:23 +0200 Subject: [PATCH 25/64] chore(lint): remvoe unsed function Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index ca2347bfc0f..6d44cecc575 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -415,29 +415,6 @@ func getSignerForAccount(io commands.IO, address string, kb keys.Keybase, cfg *b return signer, nil } -func resolveUnixOrTCPAddr(in string) (out string) { - var err error - var addr net.Addr - - if strings.HasPrefix(in, "unix://") { - in = strings.TrimPrefix(in, "unix://") - if addr, err := net.ResolveUnixAddr("unix", in); err == nil { - return fmt.Sprintf("%s://%s", addr.Network(), addr.String()) - } - - err = fmt.Errorf("unable to resolve unix address `unix://%s`: %w", in, err) - } else { // don't bother to checking prefix - in = strings.TrimPrefix(in, "tcp://") - if addr, err = net.ResolveTCPAddr("tcp", in); err == nil { - return fmt.Sprintf("%s://%s", addr.Network(), addr.String()) - } - - err = fmt.Errorf("unable to resolve tcp address `tcp://%s`: %w", in, err) - } - - panic(err) -} - func CommandLimiterMiddleware() wish.Middleware { return func(next ssh.Handler) ssh.Handler { return func(s ssh.Session) { From 2c562ab62cab87e1957f87a116eea4b3c4538132 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 23 Jul 2024 13:53:09 +0200 Subject: [PATCH 26/64] chore: only build gnodev by default Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contribs/gnodev/Makefile b/contribs/gnodev/Makefile index 2fef77bf8c1..df57040d92d 100644 --- a/contribs/gnodev/Makefile +++ b/contribs/gnodev/Makefile @@ -5,9 +5,11 @@ GOTEST_FLAGS ?= $(GOBUILD_FLAGS) -v -p 1 -timeout=5m rundep := go run -modfile ../../misc/devdeps/go.mod golangci_lint := $(rundep) github.com/golangci/golangci-lint/cmd/golangci-lint -install: install.gnodev install.gnobro +install: install.gnodev install.gnodev: go install $(GOBUILD_FLAGS) ./cmd/gnodev + +# keep gnobro out the default install for now install.gnobro: go install $(GOBUILD_FLAGS) ./cmd/gnobro From c0c0117044b68539a908e9a6724163f5275f16be Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:22:32 +0200 Subject: [PATCH 27/64] feat: add cool banner Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- .../gnobro/assets/gnoland-ansi-pink.utf8ans | 25 ++++++++ contribs/gnodev/cmd/gnobro/main.go | 49 ++++++++++----- contribs/gnodev/go.mod | 3 +- contribs/gnodev/go.sum | 2 + contribs/gnodev/pkg/browser/model.go | 25 +++++--- contribs/gnodev/pkg/browser/model_banner.go | 63 +++++++++++++++++++ contribs/gnodev/pkg/browser/model_tasks.go | 2 +- contribs/gnodev/pkg/browser/model_view.go | 6 +- 8 files changed, 148 insertions(+), 27 deletions(-) create mode 100644 contribs/gnodev/cmd/gnobro/assets/gnoland-ansi-pink.utf8ans create mode 100644 contribs/gnodev/pkg/browser/model_banner.go diff --git a/contribs/gnodev/cmd/gnobro/assets/gnoland-ansi-pink.utf8ans b/contribs/gnodev/cmd/gnobro/assets/gnoland-ansi-pink.utf8ans new file mode 100644 index 00000000000..bfda83f06c7 --- /dev/null +++ b/contribs/gnodev/cmd/gnobro/assets/gnoland-ansi-pink.utf8ans @@ -0,0 +1,25 @@ + Β· . Β· Β· + . + . . * . Β· + Β· Β· Β· Β· . . Β· + Β· . . . . Β· + + . Β· . . Β· + β–‘β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘ β–‘β–‘β–‘β–‘ β–‘ β–‘β–‘ +β–‘β–‘β–’β–’β–‘β–‘β–‘β–’β–’β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–‘β–‘β–’β–‘β–‘β–’β–’β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–‘β–’β–’β–’β–‘β–’β–’β–’β–’β–’β–’β–‘β–‘β–’β–’β–’β–‘β–‘β–‘β–’β–’β–’β–’β–‘β–’β–‘β–‘β–’β–’β–‘β–‘ +β–’β–’β–“β–“β–’β–’β–’β–“β–“β–’β–’β–’β–’β–’β–“β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–’β–’β–“β–“β–’β–“β–’β–’β–’β–’β–’β–’β–’β–‘β–’β–’β–’β–’β–’β–’β–“β–“β–’β–“β–“β–“β–’β–“β–“β–“β–“β–“β–“β–’β–’β–“β–“β–“β–’β–’β–’β–“β–“β–“β–“β–’β–“β–’β–’β–“β–“β–’β–’ +β–“β–“β–ˆβ–ˆβ–“β–“β–“β–ˆβ–ˆβ–“β–“β–“β–“β–“β–ˆβ–“β–“β–“β–“β–“β–“β–ˆβ–ˆβ–“β–“β–ˆβ–“β–“β–ˆβ–ˆβ–“β–ˆβ–“β–“β–“β–“β–“β–“β–“β–’β–“β–“β–“β–“β–“β–“β–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–“β–ˆβ–ˆβ–ˆβ–“β–“β–“β–ˆβ–ˆβ–ˆβ–ˆβ–“β–ˆβ–“β–“β–ˆβ–ˆβ–“β–“ +β–€β–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Œβ–β–ˆβ–ˆβ–’β–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–Œβ–β–ˆβ–ˆβ–’β–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆ + β–β–’β–’β–’β–ˆ β–ˆβ–’β–ˆβ–Œ β–β–’β–ˆβ–ˆβ–ˆβ–Œ β–β–’β–’β–’β–Œ β–β–’β–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–ˆβ–Œ β–β–’β–’β–’β–Œ β–β–’β–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–’β–’β–’β–ˆβ–Œ ▐▒ + β–’β–’β–’β–’β–„ β–„β–’β–ˆ β–’β–ˆβ–’β–ˆβ–„ β–’β–’β–’β–‘ β–€β–’β–’β–ˆβ–Œ β–β–’β–ˆβ–ˆ▐ β–ˆβ–’β–ˆβ–„ β–’β–’β–’β–‘ β–€β–’β–’β–ˆβ–Œ ▐▒ β–ˆβ–„ β–„β–’β–’ β–’ β–’ + β–‘ β–‘ β–„ β–„β–„β–’β–Œ β–’β–ˆβ–’β–€ β–€β–’β–’ β–’β–’β–’β–€β–„ β–„β–’β–„β–€β–€β–„▐▒▀ β–€β–’β–’ β–’β–’β–’β–€β–„ β–„β–’ β–ˆ β–„ β–„β–„β–„β–€β–€β–„ β–’ + β–€β–‘ β–’β–‘β–’β–„β–‘ β–€β–„β–’β–€β–’β–’ β–€β–ˆβ–’β–„β–„ β–„β– β–„β–’β–’β–€ β–’β–Œ ▐β–‘β–€ β–’ β–€ β–„ β– β–’β–‘β–€ β–’ β–€β–‘ β–’β–‘β–Œ ▐β–‘ β–€β–„ + β–Œβ–‘β–‘▐ β–Œβ–‘β–‘▐ + β–„β–„β–€β–€β–€β–€β–€β–€β–€β–„β–„β–€β–€β–„β–€β–€β–€β–€β–€β–„ β–„β–€β–€β–€β–€β–€β–„ β–Œβ–’β–’▐ β–„β–„β–€β–€β–€β–€β–€β–„ β–„β–€β–„β–„β–€β–€β–€β–€β–€β–„ β–„β–„β–„β–„β–„β–Œβ–’β–’▐ + β–Œβ–„β–“β–“β–“β–“β–“β–“β–“β–„ β–“β–“β–„β–“β–“β–“β–“β–“β–„β–€β–€β–„β–“β–“β–“β–“β–“β–„β–€β–„ β–Œβ–“β–“▐ β–Œβ–„β–“β–“β–“β–“β–“β–„β–€β–Œβ–“β–Œβ–„β–“β–“β–“β–“β–“β–„β–€β–€β–„β–„β–„β–„β–„β–„β–“β–“▐ + β–Œβ–ˆβ–Œ β–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–ˆ β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–Œ▐ β–Œβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–Œ β–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–ˆ β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–ˆ▐ + β–Œβ–€β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–Œβ–Œ β–Œβ–‘β–‘ β–‘β–‘ β–‘β–‘▐ β–Œβ–‘β–‘▐ β–Œβ–‘β–‘ β–‘β–‘ β–‘β–‘β–Œβ–ˆ β–Œβ–‘β–‘ β–‘β–‘β–Œ ▐β–‘β–‘▐ + ▐ β–’β–’ β–’β–’β–Œβ–Œ β–Œβ–’β–’ β–’β–’β–Œ ▐β–’β–’β–β–„β–€β–„β–Œβ–’β–’▐ β–Œβ–’β–’β–Œ ▐β–’β–’ β–’β–’β–Œβ–Œ β–Œβ–’β–’ β–’β–’β–Œ ▐β–’β–’▐ + β–Œβ–„β–„ β–“β–“ β–“β–“β–Œβ–Œ β–Œβ–“β–“ β–“β–“β–“ β–“β–“β–“ β–„β–“β–„ β–“β–“ β–€β–€ β–“β–“β–“ ▐β–“β–“ β–“β–“β–Œβ–Œ β–Œβ–“β–“ β–“β–“β–“ ▐β–“β–“▐ +β–‘ β–Œβ–€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–€ β–ˆβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–€β–„β–€β–ˆβ–ˆβ–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆ β–ˆβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆ▐ β–‘ +β–’ β–“β–“β–„β–„β–„β–„β–„β–„β–„β–„β–€β–„β–„β–„β–€ β–€β–„β–„β–€ β–€β–„β–„β–„β–„β–„β–€ β–€β–„β–€ β–€β–„β–„β–„β–„β–€ β–€β–„β–„β–„β–„β–€β–„β–„β–€β–„β–„β–„β–€ β–€β–„β–„β–€ β–€β–„β–„β–„β–„β–€β–„β–„β–“ β–’ +β–“ β–“ +β–ˆβ–“β–’β–‘ β–‘β–’β–“β–ˆ \ No newline at end of file diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 6d44cecc575..2e1db7c3f1a 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -6,11 +6,11 @@ import ( "errors" "flag" "fmt" + "io" "log/slog" "net" "net/url" "os" - "path/filepath" "strings" "sync" "time" @@ -35,7 +35,7 @@ import ( osm "github.com/gnolang/gno/tm2/pkg/os" ) -//go:embed assets/banner_land_1.txt +//go:embed assets/gnoland-ansi-pink.utf8ans var banner string const gnoPrefix = "gno.land" @@ -49,6 +49,7 @@ type broCfg struct { defaultRealm string sshListener string sshHostKeyPath string + banner bool } var defaultBroOptions = broCfg{ @@ -129,6 +130,13 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { "dev endpoint, if empty will be default to `ws://:8888`", ) + fs.BoolVar( + &c.banner, + "banner", + defaultBroOptions.banner, + "if enable, display a banner", + ) + fs.BoolVar( &c.readonly, "readonly", @@ -137,7 +145,7 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { ) } -func execBrowser(cfg *broCfg, args []string, io commands.IO) error { +func execBrowser(cfg *broCfg, args []string, cio commands.IO) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -160,7 +168,7 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { address = integration.DefaultAccount_Name } - signer, err := getSignerForAccount(io, address, kb, cfg) + signer, err := getSignerForAccount(cio, address, kb, cfg) if err != nil { return fmt.Errorf("unable to get signer for account %q: %w", address, err) } @@ -190,12 +198,17 @@ func execBrowser(cfg *broCfg, args []string, io commands.IO) error { bcfg.GnoClient = gnocl bcfg.URLDefaultValue = path bcfg.URLPrefix = gnoPrefix + bcfg.URLPrefix = gnoPrefix + + if cfg.banner { + bcfg.Banner = browser.NewModelBanner(time.Second/100, BannerReader()) + } if cfg.sshListener == "" { - return runLocal(ctx, cfg, bcfg, io) + return runLocal(ctx, cfg, bcfg, cio) } - return runServer(ctx, cfg, bcfg, io) + return runServer(ctx, cfg, bcfg, cio) } func runLocal(ctx context.Context, cfg *broCfg, bcfg browser.Config, io commands.IO) error { @@ -272,14 +285,9 @@ func runServer(ctx context.Context, cfg *broCfg, bcfg browser.Config, io command bcfgCopy.Logger = logger.WithGroup(shortid) bcfgCopy.Renderer = bubbletea.MakeRenderer(s) - switch len(s.Command()) { - case 0: - bcfgCopy.Banner = fmt.Sprintf(banner, s.User()) - case 1: - // use command argument as path - path := filepath.Clean(s.Command()[0]) - bcfgCopy.URLDefaultValue = path - default: + if len(s.Command()) > 1 { + // Erase banner on specifc command + bcfgCopy.Banner = browser.ModelBanner{} } bcfgCopy.Logger.Info("session started", @@ -426,3 +434,16 @@ func CommandLimiterMiddleware() wish.Middleware { } } } + +func BannerReader() io.Reader { + // XXX: read ans file directly + // var buf bytes.Buffer + // inputBanner := strings.NewReader(banner) + // if err := ansicat.ProcessFile(inputBanner, &buf, 0, 4096); err != nil { + // panic("unable to process anc file: " + err.Error()) + // } + // read all & convert to delimited bytes reader + // return bytes.NewReader(buf.Bytes()) + + return strings.NewReader(banner) +} diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod index 2d40263c118..0697b692815 100644 --- a/contribs/gnodev/go.mod +++ b/contribs/gnodev/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/contribs/gnodev -go 1.22 +go 1.22.1 toolchain go1.22.4 @@ -24,6 +24,7 @@ require ( github.com/stretchr/testify v1.9.0 go.uber.org/zap v1.27.0 golang.org/x/term v0.22.0 + moul.io/ansicat v0.0.0-20240721080627-fc0662bd47f5 ) require ( diff --git a/contribs/gnodev/go.sum b/contribs/gnodev/go.sum index 88ac0e10a06..9bc4f9ac5f6 100644 --- a/contribs/gnodev/go.sum +++ b/contribs/gnodev/go.sum @@ -335,3 +335,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +moul.io/ansicat v0.0.0-20240721080627-fc0662bd47f5 h1:AF7fMxDfo03AF+kA9XmNvDL/HGJSnG5wfn1mr5GvnKc= +moul.io/ansicat v0.0.0-20240721080627-fc0662bd47f5/go.mod h1:4RgLeii5/zDFhXRUAfX+VYKFJvY+7qEQrYrBq2wjufM= diff --git a/contribs/gnodev/pkg/browser/model.go b/contribs/gnodev/pkg/browser/model.go index 5ec6a6d74a3..31ab9805397 100644 --- a/contribs/gnodev/pkg/browser/model.go +++ b/contribs/gnodev/pkg/browser/model.go @@ -34,10 +34,10 @@ type Config struct { URLPrefix string URLDefaultValue string Logger *slog.Logger - Banner string Renderer *lipgloss.Renderer GnoClient *gnoclient.Client Readonly bool + Banner ModelBanner } const DefaultGnoLandPrefix = "gno.land/" @@ -57,7 +57,8 @@ type model struct { logger *slog.Logger // misc - banner string + banner ModelBanner + bannerDiscarded bool // Viewport zone *zone.Manager @@ -129,17 +130,19 @@ func New(cfg Config) tea.Model { nodeclient := NewNodeClient(cfg.Logger, base, cfg.GnoClient) return &model{ logger: cfg.Logger, - banner: cfg.Banner, render: cfg.Renderer, readonly: cfg.Readonly, client: nodeclient, - listFuncs: newFuncList(), - taskLoader: NewTasksManager(3), + taskLoader: newLoaderModel(), + + banner: cfg.Banner, + bannerDiscarded: cfg.Banner.Empty(), urlInput: urlinput, urlPrefix: cfg.URLPrefix, commandInput: cmdinput, + listFuncs: newFuncList(), zone: zone.New(), pageurls: map[string]string{}, @@ -149,7 +152,7 @@ func New(cfg Config) tea.Model { func (m model) Init() tea.Cmd { m.history.Init() - return nil + return m.banner.tick() } type fetchRealmMsg struct { @@ -377,6 +380,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmds := []tea.Cmd{cmd} + if !m.bannerDiscarded { + var bannerCmd tea.Cmd + m.banner, bannerCmd = m.banner.Update(msg) + cmds = append(cmds, bannerCmd) + } + var viewCmd tea.Cmd m.viewport, viewCmd = m.viewport.Update(msg) cmds = append(cmds, viewCmd) @@ -390,12 +399,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m *model) updateKey(msg tea.KeyMsg) tea.Cmd { var cmd tea.Cmd - if m.banner != "" { + if !m.bannerDiscarded { switch key := msg.String(); key { case "ctrl+c": return tea.Quit case "enter": - m.banner = "" + m.bannerDiscarded = true } // Discard other input while banner is active return nil diff --git a/contribs/gnodev/pkg/browser/model_banner.go b/contribs/gnodev/pkg/browser/model_banner.go new file mode 100644 index 00000000000..7dc745e86f4 --- /dev/null +++ b/contribs/gnodev/pkg/browser/model_banner.go @@ -0,0 +1,63 @@ +package browser + +import ( + "bufio" + "fmt" + "io" + "time" + + tea "github.com/charmbracelet/bubbletea" +) + +type ModelBanner struct { + Banner string + + enable bool + scanner *bufio.Scanner + fps time.Duration +} + +func NewModelBanner(fps time.Duration, banner io.Reader) ModelBanner { + scan := bufio.NewScanner(banner) + return ModelBanner{ + scanner: scan, + fps: fps, + } +} + +func (m ModelBanner) Empty() bool { + return m.scanner == nil +} + +type tickBannerMsg struct{} + +func (m ModelBanner) tick() tea.Cmd { + return tea.Tick(m.fps, func(_ time.Time) tea.Msg { + return tickBannerMsg{} + }) +} + +func (m ModelBanner) Init() tea.Cmd { + if m.Empty() { + return nil + } + + return m.tick() +} + +func (m ModelBanner) Update(msg tea.Msg) (ModelBanner, tea.Cmd) { + var cmd tea.Cmd + switch msg.(type) { + case tickBannerMsg: + if !m.Empty() && m.scanner.Scan() { + m.Banner += fmt.Sprintln(m.scanner.Text()) + cmd = m.tick() + } + // XXX: handle window size + } + return m, cmd +} + +func (m ModelBanner) View() string { + return m.Banner +} diff --git a/contribs/gnodev/pkg/browser/model_tasks.go b/contribs/gnodev/pkg/browser/model_tasks.go index 353d2cc3d55..553d0168b6d 100644 --- a/contribs/gnodev/pkg/browser/model_tasks.go +++ b/contribs/gnodev/pkg/browser/model_tasks.go @@ -32,7 +32,7 @@ type LoaderModel struct { task int } -func NewTasksManager(tlimit int) LoaderModel { +func newLoaderModel() LoaderModel { return LoaderModel{ spinner: MeterLoader, } diff --git a/contribs/gnodev/pkg/browser/model_view.go b/contribs/gnodev/pkg/browser/model_view.go index 08ac2029d0e..ed2c2541954 100644 --- a/contribs/gnodev/pkg/browser/model_view.go +++ b/contribs/gnodev/pkg/browser/model_view.go @@ -31,7 +31,7 @@ var ( ) func (m model) View() string { - if m.banner != "" { + if !m.bannerDiscarded { return m.bannerView() } @@ -49,8 +49,8 @@ func (m model) bannerView() string { } banner := m.render.NewStyle().Padding(1, 5). - Border(lipgloss.DoubleBorder(), true, false, true). - Render(m.banner) + Border(lipgloss.DoubleBorder(), true, false, false). + Render(m.banner.View()) return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, banner) } From 393c119d9624d36fa80d1510542c4613a9bc4819 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:22:51 +0200 Subject: [PATCH 28/64] chore: update gno mod Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/go.mod | 1 - contribs/gnodev/go.sum | 2 -- 2 files changed, 3 deletions(-) diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod index 0697b692815..9bbbcca5dad 100644 --- a/contribs/gnodev/go.mod +++ b/contribs/gnodev/go.mod @@ -24,7 +24,6 @@ require ( github.com/stretchr/testify v1.9.0 go.uber.org/zap v1.27.0 golang.org/x/term v0.22.0 - moul.io/ansicat v0.0.0-20240721080627-fc0662bd47f5 ) require ( diff --git a/contribs/gnodev/go.sum b/contribs/gnodev/go.sum index 9bc4f9ac5f6..88ac0e10a06 100644 --- a/contribs/gnodev/go.sum +++ b/contribs/gnodev/go.sum @@ -335,5 +335,3 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -moul.io/ansicat v0.0.0-20240721080627-fc0662bd47f5 h1:AF7fMxDfo03AF+kA9XmNvDL/HGJSnG5wfn1mr5GvnKc= -moul.io/ansicat v0.0.0-20240721080627-fc0662bd47f5/go.mod h1:4RgLeii5/zDFhXRUAfX+VYKFJvY+7qEQrYrBq2wjufM= From a127e201401cdc5f92b9187c00750c02cc538599 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:26:32 +0200 Subject: [PATCH 29/64] fix: empty banner Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 2e1db7c3f1a..2b6a2242315 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -200,11 +200,11 @@ func execBrowser(cfg *broCfg, args []string, cio commands.IO) error { bcfg.URLPrefix = gnoPrefix bcfg.URLPrefix = gnoPrefix - if cfg.banner { - bcfg.Banner = browser.NewModelBanner(time.Second/100, BannerReader()) - } - if cfg.sshListener == "" { + if cfg.banner { + bcfg.Banner = browser.NewModelBanner(time.Second/100, BannerReader()) + } + return runLocal(ctx, cfg, bcfg, cio) } @@ -285,6 +285,10 @@ func runServer(ctx context.Context, cfg *broCfg, bcfg browser.Config, io command bcfgCopy.Logger = logger.WithGroup(shortid) bcfgCopy.Renderer = bubbletea.MakeRenderer(s) + if cfg.banner { + bcfgCopy.Banner = browser.NewModelBanner(time.Second/100, BannerReader()) + } + if len(s.Command()) > 1 { // Erase banner on specifc command bcfgCopy.Banner = browser.ModelBanner{} From d457be481f7eed2e46bd61e30fae2be22a5ac43e Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:30:49 +0200 Subject: [PATCH 30/64] fix: banner faster Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 4 ++-- contribs/gnodev/pkg/browser/model_view.go | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 2b6a2242315..3fea8417e5f 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -202,7 +202,7 @@ func execBrowser(cfg *broCfg, args []string, cio commands.IO) error { if cfg.sshListener == "" { if cfg.banner { - bcfg.Banner = browser.NewModelBanner(time.Second/100, BannerReader()) + bcfg.Banner = browser.NewModelBanner(time.Second/50, BannerReader()) } return runLocal(ctx, cfg, bcfg, cio) @@ -286,7 +286,7 @@ func runServer(ctx context.Context, cfg *broCfg, bcfg browser.Config, io command bcfgCopy.Renderer = bubbletea.MakeRenderer(s) if cfg.banner { - bcfgCopy.Banner = browser.NewModelBanner(time.Second/100, BannerReader()) + bcfgCopy.Banner = browser.NewModelBanner(time.Second/50, BannerReader()) } if len(s.Command()) > 1 { diff --git a/contribs/gnodev/pkg/browser/model_view.go b/contribs/gnodev/pkg/browser/model_view.go index ed2c2541954..56c2715cf31 100644 --- a/contribs/gnodev/pkg/browser/model_view.go +++ b/contribs/gnodev/pkg/browser/model_view.go @@ -44,14 +44,11 @@ func (m model) View() string { } func (m model) bannerView() string { - if m.width == 0 || m.height == 0 { - return "x" + banner := m.banner.View() + if banner == "" || m.width == 0 || m.height == 0 { + return "" } - banner := m.render.NewStyle().Padding(1, 5). - Border(lipgloss.DoubleBorder(), true, false, false). - Render(m.banner.View()) - return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, banner) } From f80245b1eb68f03f679ddf9bd64aec1db6a01cdf Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 23 Jul 2024 16:41:27 +0200 Subject: [PATCH 31/64] fix: encapsulate banner Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/pkg/browser/model_view.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/contribs/gnodev/pkg/browser/model_view.go b/contribs/gnodev/pkg/browser/model_view.go index 56c2715cf31..85bf0736a67 100644 --- a/contribs/gnodev/pkg/browser/model_view.go +++ b/contribs/gnodev/pkg/browser/model_view.go @@ -49,7 +49,15 @@ func (m model) bannerView() string { return "" } - return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, banner) + bannerView := m.render.NewStyle().Margin(1). + Render(banner) + + return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, + lipgloss.JoinVertical(lipgloss.Center, + bannerView, + "press to continue", + ), + ) } func (m model) listFuncsView() string { From a9952bf115b509f7c7e6e6454d44f8fc4a9213ef Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Sun, 28 Jul 2024 15:38:57 +0200 Subject: [PATCH 32/64] wip: gnodev readme doc Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/README.md | 59 +++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/contribs/gnodev/README.md b/contribs/gnodev/README.md index 6da9e7b1ebc..6dccedcac82 100644 --- a/contribs/gnodev/README.md +++ b/contribs/gnodev/README.md @@ -1,30 +1,53 @@ -## `gnodev`: Your Gno Companion Tool +## `gnodev`: Your Gno Development Companion -`gnodev` is designed to be a robust and user-friendly tool in your realm package development journey, streamlining your workflow and enhancing productivity. +`gnodev` is a robust tool designed to streamline your Gno package development process, enhancing productivity +by providing immediate feedback on code changes. -We will only give a quick overview below. You may find the official documentation at [docs/gno-tooling/gnodev.md](../../docs/gno-tooling/cli/gnodev.md). +Please note that this is a quick overview. For a more detailed guide, refer to the official documentation at +[docs/gno-tooling/gnodev.md](../../docs/gno-tooling/cli/gnodev.md). ### Synopsis -**gnodev** [**-minimal**] [**-no-watch**] [**PKG_PATH ...**] +**gnodev** [**options**] [**PKG_PATH ...**] ### Features -- **In-Memory Node**: Gnodev starts an in-memory node, and automatically loads - the **examples** folder and any user-specified paths. -- **Web Interface Server**: Starts a `gnoweb` server on `localhost:8888`. -- **Hot Reload**: Monitors the example packages folder and specified directories for file changes, - reloading the package and automatically restarting the node as needed. -- **State Maintenance**: Ensures the current state is preserved by replaying all transactions. +- **In-Memory Node**: Gnodev starts an in-memory node, automatically loading the **examples** folder and any + user-specified paths. +- **Web Interface Server**: Gnodev starts a `gnoweb` server on [`localhost:8888`](https://localhost:8888). +- **Balances and Keybase Customization**: Set account balances, load them from a file, or add new accounts via a flag. +- **Hot Reload**: Monitors the **examples** folder and specified directories for file changes, reloading the + package and automatically restarting the node as needed. +- **State Maintenance**: Ensures the previous node state is preserved by replaying all transactions. +- **Transaction Manipulation**: Allows for interactive cancellation and redoing of transactions. +- **State Export**: Export the current state at any time in a genesis doc format. ### Commands -While `gnodev` is running, the user can trigger specific actions by pressing -the following combinations: -- **H**: Display help information. -- **R**: Reload the node, without resetting the state. -- **Ctrl+R**: Reset the current node state. -- **Ctrl+C**: Exit `gnodev`. +While `gnodev` is running, trigger specific actions by pressing the following combinations: +- **H**: Display help information. +- **A**: Display account balances. +- **R**: Reload the node manually. +- **P**: Cancel the last action. +- **N**: Redo the last cancelled action. +- **Ctrl+S**: Save the current state. +- **Ctrl+R**: Restore the saved state. +- **E**: Export the current state to a genesis file. +- **Cmd+R**: Reset the current node state. +- **Cmd+C**: Exit `gnodev`. -### Loading 'examples' -The **examples** directory is loaded automatically. If working within this folder, you don't have to specify any additional paths to `gnodev`. Use `--minimal` to prevent this. +### Usage +Run `gnodev` followed by any specific options and/or package paths. The **examples** directory is loaded +automatically. Use `--minimal` to prevent this. + +Example: +``` +gnodev --add-account [:] ./myrealm +``` + +### `gnobro`: realm interface +`gnobro` is a terminal user interface (TUI) that allows you to browse realms within your terminal. It +automatically connects to `gnodev` for real-time development. In addition to hot reload, it also has the +ability to execute commands and interact with your realm. ### Installation Run `make install` to install `gnodev`. + +Run `make install.gnobro` to install `gnobro`. From 018bab220264d776ac2b6b883f75886212d9c5d5 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Sun, 28 Jul 2024 21:38:32 +0200 Subject: [PATCH 33/64] chore: update doc Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/README.md | 14 +++++++ contribs/gnodev/cmd/gnobro/main.go | 62 +++++++++++++++--------------- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/contribs/gnodev/README.md b/contribs/gnodev/README.md index 6dccedcac82..3b26903c7eb 100644 --- a/contribs/gnodev/README.md +++ b/contribs/gnodev/README.md @@ -47,6 +47,20 @@ gnodev --add-account [:] ./myrealm automatically connects to `gnodev` for real-time development. In addition to hot reload, it also has the ability to execute commands and interact with your realm. + +#### Usage +**gnobro** [**options**] [**PKG_PATH **] + +Run gnobro followed by any specific options and/or a target pacakge path. + +Use `gnobro -h` for a detailed list of options. + +Example: +``` +gnobro gno.land/r/demo/home +``` + + ### Installation Run `make install` to install `gnodev`. diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 3fea8417e5f..040505d799d 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -2,15 +2,14 @@ package main import ( "context" - _ "embed" "errors" "flag" "fmt" - "io" "log/slog" "net" "net/url" "os" + "path/filepath" "strings" "sync" "time" @@ -35,9 +34,6 @@ import ( osm "github.com/gnolang/gno/tm2/pkg/os" ) -//go:embed assets/gnoland-ansi-pink.utf8ans -var banner string - const gnoPrefix = "gno.land" type broCfg struct { @@ -68,9 +64,13 @@ func main() { cmd := commands.NewCommand( commands.Metadata{ Name: "gnobro", - ShortUsage: "gnobro [flags]", - ShortHelp: "runs a cli browser.", - LongHelp: `run a cli browser`, + ShortUsage: "gnobro [flags] [pkg_path]", + ShortHelp: "Gno Browser, a realm explorer", + LongHelp: `Gnobro is a terminal user interface (TUI) that allows you to browse realms within your +terminal. It automatically connects to Gnodev for real-time development. In +addition to hot reload, it also has the ability to execute commands and interact +with your realm. +`, }, cfg, func(_ context.Context, args []string) error { @@ -127,7 +127,7 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { &c.devEndpoint, "dev", defaultBroOptions.devEndpoint, - "dev endpoint, if empty will be default to `ws://:8888`", + "dev endpoint, if empty will default to `ws://:8888`", ) fs.BoolVar( @@ -141,7 +141,7 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { &c.readonly, "readonly", defaultBroOptions.readonly, - "readonly mode, no command allowed", + "readonly mode, no commands allowed", ) } @@ -202,7 +202,7 @@ func execBrowser(cfg *broCfg, args []string, cio commands.IO) error { if cfg.sshListener == "" { if cfg.banner { - bcfg.Banner = browser.NewModelBanner(time.Second/50, BannerReader()) + bcfg.Banner = NewBanner_GnoLand() } return runLocal(ctx, cfg, bcfg, cio) @@ -286,7 +286,7 @@ func runServer(ctx context.Context, cfg *broCfg, bcfg browser.Config, io command bcfgCopy.Renderer = bubbletea.MakeRenderer(s) if cfg.banner { - bcfgCopy.Banner = browser.NewModelBanner(time.Second/50, BannerReader()) + bcfgCopy.Banner = NewBanner_GnoLand() } if len(s.Command()) > 1 { @@ -319,8 +319,9 @@ func runServer(ctx context.Context, cfg *broCfg, bcfg browser.Config, io command logging.StructuredMiddlewareWithLogger( charmlogger, charmlog.DebugLevel, ), - activeterm.Middleware(), // Bubble Tea apps usually require a PTY. - CommandLimiterMiddleware(), + activeterm.Middleware(), // ensure PTY + ValidatePathCommandMiddleware(), + // XXX: add ip throttler ), ) @@ -427,27 +428,26 @@ func getSignerForAccount(io commands.IO, address string, kb keys.Keybase, cfg *b return signer, nil } -func CommandLimiterMiddleware() wish.Middleware { +func ValidatePathCommandMiddleware() wish.Middleware { return func(next ssh.Handler) ssh.Handler { return func(s ssh.Session) { - if len(s.Command()) > 1 { - s.Exit(1) - } else { + switch cmd := s.Command(); len(cmd) { + case 0: // ok next(s) + return + case 1: // check for valid path + path := cmd[0] + if filepath.Clean(path) == path { + next(s) + return + } + + fmt.Fprintln(s.Stderr(), "provided path is invalid") + default: + fmt.Fprintln(s.Stderr(), "too many arguments") } + + s.Exit(1) } } } - -func BannerReader() io.Reader { - // XXX: read ans file directly - // var buf bytes.Buffer - // inputBanner := strings.NewReader(banner) - // if err := ansicat.ProcessFile(inputBanner, &buf, 0, 4096); err != nil { - // panic("unable to process anc file: " + err.Error()) - // } - // read all & convert to delimited bytes reader - // return bytes.NewReader(buf.Bytes()) - - return strings.NewReader(banner) -} From fec2042612926a2f86df3de30c55208b43db4f01 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Sun, 28 Jul 2024 21:38:50 +0200 Subject: [PATCH 34/64] chore: banner file Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/banner.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 contribs/gnodev/cmd/gnobro/banner.go diff --git a/contribs/gnodev/cmd/gnobro/banner.go b/contribs/gnodev/cmd/gnobro/banner.go new file mode 100644 index 00000000000..bed04053ec0 --- /dev/null +++ b/contribs/gnodev/cmd/gnobro/banner.go @@ -0,0 +1,18 @@ +package main + +import ( + _ "embed" + + "strings" + "time" + + "github.com/gnolang/gno/contribs/gnodev/pkg/browser" +) + +//go:embed assets/gnoland-ansi-pink.utf8ans +var banner_gnoland string + +func NewBanner_GnoLand() browser.ModelBanner { + r := strings.NewReader(banner_gnoland) + return browser.NewModelBanner(time.Second/50, r) +} From 281190c074138283375e551de862158560d3055d Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Mon, 29 Jul 2024 15:40:37 +0200 Subject: [PATCH 35/64] fix: new model Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 10 +++++----- contribs/gnodev/pkg/browser/model.go | 5 ++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 040505d799d..13bab1205e5 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -205,16 +205,16 @@ func execBrowser(cfg *broCfg, args []string, cio commands.IO) error { bcfg.Banner = NewBanner_GnoLand() } - return runLocal(ctx, cfg, bcfg, cio) + return runLocal(ctx, gnocl, cfg, bcfg, cio) } return runServer(ctx, cfg, bcfg, cio) } -func runLocal(ctx context.Context, cfg *broCfg, bcfg browser.Config, io commands.IO) error { +func runLocal(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg browser.Config, io commands.IO) error { var err error - model := browser.New(bcfg) + model := browser.New(bcfg, gnocl) p := tea.NewProgram(model, tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" @@ -271,7 +271,7 @@ func runLocal(ctx context.Context, cfg *broCfg, bcfg browser.Config, io commands return context.Cause(ctx) } -func runServer(ctx context.Context, cfg *broCfg, bcfg browser.Config, io commands.IO) error { +func runServer(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg browser.Config, io commands.IO) error { // setup logger charmlogger := charmlog.New(io.Out()) charmlogger.SetLevel(charmlog.DebugLevel) @@ -299,7 +299,7 @@ func runServer(ctx context.Context, cfg *broCfg, bcfg browser.Config, io command "path", bcfgCopy.URLDefaultValue, "sid", s.Context().SessionID(), "user", s.User()) - model := browser.New(bcfgCopy) + model := browser.New(bcfgCopy, gnocl) return model, []tea.ProgramOption{ tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel diff --git a/contribs/gnodev/pkg/browser/model.go b/contribs/gnodev/pkg/browser/model.go index 31ab9805397..1c075961f7c 100644 --- a/contribs/gnodev/pkg/browser/model.go +++ b/contribs/gnodev/pkg/browser/model.go @@ -35,7 +35,6 @@ type Config struct { URLDefaultValue string Logger *slog.Logger Renderer *lipgloss.Renderer - GnoClient *gnoclient.Client Readonly bool Banner ModelBanner } @@ -106,7 +105,7 @@ func initCommandInput(r *lipgloss.Renderer) textinput.Model { return ti } -func New(cfg Config) tea.Model { +func New(cfg Config, client *gnoclient.Client) tea.Model { renderer := lipgloss.DefaultRenderer() if cfg.Renderer != nil { renderer = cfg.Renderer @@ -127,7 +126,7 @@ func New(cfg Config) tea.Model { GasWanted: 2000000, } - nodeclient := NewNodeClient(cfg.Logger, base, cfg.GnoClient) + nodeclient := NewNodeClient(cfg.Logger, base, client) return &model{ logger: cfg.Logger, render: cfg.Renderer, From 988ff3422cd88124097cd2b29d1704f64731cf60 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 30 Jul 2024 21:42:12 +0200 Subject: [PATCH 36/64] feat: update banner Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- .../gnodev/cmd/gnobro/assets/banner_wave.txt | 24 ------- .../gnodev/cmd/gnobro/assets/gn_hc1.utf8ans | 25 ++++++++ .../gnodev/cmd/gnobro/assets/gn_hc2.utf8ans | 25 ++++++++ .../gnodev/cmd/gnobro/assets/gn_hc3.utf8ans | 25 ++++++++ .../gnodev/cmd/gnobro/assets/gn_hc4.utf8ans | 25 ++++++++ .../gnobro/assets/gnoland-ansi-pink.utf8ans | 25 -------- contribs/gnodev/cmd/gnobro/banner.go | 37 ++++++++--- contribs/gnodev/pkg/browser/model.go | 3 +- contribs/gnodev/pkg/browser/model_banner.go | 62 +++++++++++++++---- contribs/gnodev/pkg/browser/model_view.go | 4 +- 10 files changed, 185 insertions(+), 70 deletions(-) delete mode 100644 contribs/gnodev/cmd/gnobro/assets/banner_wave.txt create mode 100644 contribs/gnodev/cmd/gnobro/assets/gn_hc1.utf8ans create mode 100644 contribs/gnodev/cmd/gnobro/assets/gn_hc2.utf8ans create mode 100644 contribs/gnodev/cmd/gnobro/assets/gn_hc3.utf8ans create mode 100644 contribs/gnodev/cmd/gnobro/assets/gn_hc4.utf8ans delete mode 100644 contribs/gnodev/cmd/gnobro/assets/gnoland-ansi-pink.utf8ans diff --git a/contribs/gnodev/cmd/gnobro/assets/banner_wave.txt b/contribs/gnodev/cmd/gnobro/assets/banner_wave.txt deleted file mode 100644 index 915a4a8d01c..00000000000 --- a/contribs/gnodev/cmd/gnobro/assets/banner_wave.txt +++ /dev/null @@ -1,24 +0,0 @@ - + - + - Hello %s, welcome to + - + - + - :::::::: :::: ::: :::::::: + - :+. . :+: + :+:+: :+: :+: :+: + - +:+ :+:+:+ +:+ +:+ +:+ - :#: + +#+ +:+ +#+ + +#+ +:+ + - +#+ +#+# +#+ +#+#+# +#+ +#+ - #+# #+# #+# #+#+# #+# #+# + - ######## ### #### ######## - + - + + - ::: ::: :::: ::: ::::::::: - + :+: :+: :+: :+:+: :+: :+: :+: - +:+ +:+ +:+ :+:+:+ +:+ + +:+ +:+ - +#+ +#++:++#++: +#+ +:+ +#+ +#+ +:+ - + +#+ + +#+ +#+ +#+ +#+#+# +#+ +#+ - + #+# #+# #+# #+# #+#+# + #+# #+# - ########## ### ### ### #### ######### + - + - + -////////////////////////// press to continue ////////////////////////// diff --git a/contribs/gnodev/cmd/gnobro/assets/gn_hc1.utf8ans b/contribs/gnodev/cmd/gnobro/assets/gn_hc1.utf8ans new file mode 100644 index 00000000000..b50762d0abb --- /dev/null +++ b/contribs/gnodev/cmd/gnobro/assets/gn_hc1.utf8ans @@ -0,0 +1,25 @@ + Β· . Β· Β· + . * . . * . Β· + Β· Β· Β· Β· . . . + Β· . . . . Β· * + . Β· . . Β· + β–‘β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘ β–‘β–‘β–‘β–‘ β–‘ β–‘β–‘ +β–‘β–‘β–’β–’β–‘β–‘β–‘β–’β–’β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–‘β–‘β–’β–‘β–‘β–’β–’β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–‘β–’β–’β–’β–‘β–’β–’β–’β–’β–’β–’β–‘β–‘β–’β–’β–’β–‘β–‘β–‘β–’β–’β–’β–’β–‘β–’β–‘β–‘β–’β–’β–‘β–‘ +β–’β–’β–“β–“β–’β–’β–’β–“β–“β–’β–’β–’β–’β–’β–“β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–’β–’β–“β–“β–’β–“β–’β–’β–’β–’β–’β–’β–’β–‘β–’β–’β–’β–’β–’β–’β–“β–“β–’β–“β–“β–“β–’β–“β–“β–“β–“β–“β–“β–’β–’β–“β–“β–“β–’β–’β–’β–“β–“β–“β–“β–’β–“β–’β–’β–“β–“β–’β–’ +β–“β–“β–ˆβ–ˆβ–“β–“β–“β–ˆβ–ˆβ–“β–“β–“β–“β–“β–ˆβ–“β–“β–“β–“β–“β–“β–ˆβ–ˆβ–“β–“β–ˆβ–“β–“β–ˆβ–ˆβ–“β–ˆβ–“β–“β–“β–“β–“β–“β–“β–’β–“β–“β–“β–“β–“β–“β–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–“β–ˆβ–ˆβ–ˆβ–“β–“β–“β–ˆβ–ˆβ–ˆβ–ˆβ–“β–ˆβ–“β–“β–ˆβ–ˆβ–“β–“ +β–€β–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Œβ–β–ˆβ–ˆβ–’β–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–Œβ–β–ˆβ–ˆβ–’β–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆ + β–β–’β–’β–’β–ˆ β–ˆβ–’β–ˆβ–Œ β–β–’β–ˆβ–ˆβ–ˆβ–Œ β–β–’β–’β–’β–Œ β–β–’β–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–ˆβ–Œ β–β–’β–’β–’β–Œ β–β–’β–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–’β–’β–’β–ˆβ–Œ ▐▒ + β–’β–’β–’β–’β–„ β–„β–’β–ˆ β–’β–ˆβ–’β–ˆβ–„ β–’β–’β–’β–‘ β–€β–’β–’β–ˆβ–Œ β–β–’β–ˆβ–ˆ▐ β–ˆβ–’β–ˆβ–„ β–’β–’β–’β–‘ β–€β–’β–’β–ˆβ–Œ ▐▒ β–ˆβ–„ β–„β–’β–’ β–’ β–’ + β–‘ β–‘ β–„ β–„β–„β–’β–Œ β–’β–ˆβ–’β–€ β–€β–’β–’ β–’β–’β–’β–€β–„ β–„β–’β–„β–€β–€β–„▐▒▀ β–€β–’β–’ β–’β–’β–’β–€β–„ β–„β–’ β–ˆ β–„ β–„β–„β–„β–€β–€β–„ β–’ + β–€β–‘ β–’β–‘β–’β–„β–‘ β–€β–„β–’β–€β–’β–’ β–€β–ˆβ–’β–„β–„ β–„β– β–„β–’β–’β–€ β–’β–Œβ–‘β–‘▐β–‘β–€ β–’ β–€ β–„ β– β–’β–‘β–€ β–’ β–€β–‘ β–’β–‘β–Œβ–‘β–‘▐β–‘ β–€β–„ + β–Œβ–‘β–‘▐ β–Œβ–‘β–‘▐ + β–„β–„β–€β–€β–€β–€β–€β–€β–€β–„β–„β–€β–€β–„β–€β–€β–€β–€β–€β–„ β–„β–€β–€β–€β–€β–€β–„ β–Œβ–’β–’▐ β–„β–„β–€β–€β–€β–€β–€β–„ β–„β–€β–„β–„β–€β–€β–€β–€β–€β–„ β–„β–„β–„β–„β–„β–Œβ–’β–’▐ + β–Œβ–„β–“β–“β–“β–“β–“β–“β–“β–„ β–“β–“β–„β–“β–“β–“β–“β–“β–„β–€β–€β–„β–“β–“β–“β–“β–“β–„β–€β–„ β–Œβ–“β–“▐ β–Œβ–„β–“β–“β–“β–“β–“β–„β–€β–Œβ–“β–Œβ–„β–“β–“β–“β–“β–“β–„β–€β–€β–„β–„β–„β–„β–„β–„β–“β–“▐ + β–Œβ–ˆβ–Œ β–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–ˆ β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–Œ▐ β–Œβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–Œ β–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–ˆ β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–ˆ▐ + β–Œβ–€β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–Œβ–Œ β–Œβ–‘β–‘ β–‘β–‘ β–‘β–‘▐ β–Œβ–‘β–‘▐ β–Œβ–‘β–‘ β–‘β–‘ β–‘β–‘β–Œβ–ˆ β–Œβ–‘β–‘ β–‘β–‘β–Œ ▐β–‘β–‘▐ + ▐ β–’β–’ β–’β–’β–Œβ–Œ β–Œβ–’β–’ β–’β–’β–Œ ▐β–’β–’β–β–„β–€β–„β–Œβ–’β–’▐ β–Œβ–’β–’β–Œ ▐β–’β–’ β–’β–’β–Œβ–Œ β–Œβ–’β–’ β–’β–’β–Œ ▐β–’β–’▐ + β–Œβ–„β–„ β–“β–“ β–“β–“β–Œβ–Œ β–Œβ–“β–“ β–“β–“β–“ β–“β–“β–“ β–„β–“β–„ β–“β–“ β–€β–€ β–“β–“β–“ ▐β–“β–“ β–“β–“β–Œβ–Œ β–Œβ–“β–“ β–“β–“β–“ ▐β–“β–“▐ +β–‘ β–Œβ–€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–€ β–ˆβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–€β–„β–€β–ˆβ–ˆβ–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆ β–ˆβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆ▐ β–‘ +β–’ β–“β–“β–„β–„β–„β–„β–„β–„β–„β–„β–€β–„β–„β–„β–€ β–€β–„β–„β–€ β–€β–„β–„β–„β–„β–„β–€ β–€β–„β–€ β–€β–„β–„β–„β–„β–€ β–€β–„β–„β–„β–„β–€β–„β–„β–€β–„β–„β–„β–€ β–€β–„β–„β–€ β–€β–„β–„β–„β–„β–€β–„β–„β–“ β–’ +β–“ β–“ +β–ˆβ–“β–’β–‘ β–‘β–’β–“β–ˆ \ No newline at end of file diff --git a/contribs/gnodev/cmd/gnobro/assets/gn_hc2.utf8ans b/contribs/gnodev/cmd/gnobro/assets/gn_hc2.utf8ans new file mode 100644 index 00000000000..12a3a9a236f --- /dev/null +++ b/contribs/gnodev/cmd/gnobro/assets/gn_hc2.utf8ans @@ -0,0 +1,25 @@ + Β· . Β· Β· + . + . . + . Β· + Β· Β· Β· Β· . . . + Β· . . . . Β· + + . Β· . . Β· + β–‘β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘ β–‘β–‘β–‘β–‘ β–‘ β–‘β–‘ +β–‘β–‘β–’β–’β–‘β–‘β–‘β–’β–’β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–‘β–‘β–’β–‘β–‘β–’β–’β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–‘β–’β–’β–’β–‘β–’β–’β–’β–’β–’β–’β–‘β–‘β–’β–’β–’β–‘β–‘β–‘β–’β–’β–’β–’β–‘β–’β–‘β–‘β–’β–’β–‘β–‘ +β–’β–’β–“β–“β–’β–’β–’β–“β–“β–’β–’β–’β–’β–’β–“β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–’β–’β–“β–“β–’β–“β–’β–’β–’β–’β–’β–’β–’β–‘β–’β–’β–’β–’β–’β–’β–“β–“β–’β–“β–“β–“β–’β–“β–“β–“β–“β–“β–“β–’β–’β–“β–“β–“β–’β–’β–’β–“β–“β–“β–“β–’β–“β–’β–’β–“β–“β–’β–’ +β–“β–“β–ˆβ–ˆβ–“β–“β–“β–ˆβ–ˆβ–“β–“β–“β–“β–“β–ˆβ–“β–“β–“β–“β–“β–“β–ˆβ–ˆβ–“β–“β–ˆβ–“β–“β–ˆβ–ˆβ–“β–ˆβ–“β–“β–“β–“β–“β–“β–“β–’β–“β–“β–“β–“β–“β–“β–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–“β–ˆβ–ˆβ–ˆβ–“β–“β–“β–ˆβ–ˆβ–ˆβ–ˆβ–“β–ˆβ–“β–“β–ˆβ–ˆβ–“β–“ +β–€β–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Œβ–β–ˆβ–ˆβ–’β–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–Œβ–β–ˆβ–ˆβ–’β–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆ + β–β–’β–’β–’β–ˆ β–ˆβ–’β–ˆβ–Œ β–β–’β–ˆβ–ˆβ–ˆβ–Œ β–β–’β–’β–’β–Œ β–β–’β–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–ˆβ–Œ β–β–’β–’β–’β–Œ β–β–’β–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–’β–’β–’β–ˆβ–Œ ▐▒ + β–’β–’β–’β–’β–„ β–„β–’β–ˆ β–’β–ˆβ–’β–ˆβ–„ β–’β–’β–’β–‘ β–€β–’β–’β–ˆβ–Œ β–β–’β–ˆβ–ˆ▐ β–ˆβ–’β–ˆβ–„ β–’β–’β–’β–‘ β–€β–’β–’β–ˆβ–Œ ▐▒ β–ˆβ–„ β–„β–’β–’ β–’ β–’ + β–‘ β–‘ β–„ β–„β–„β–’β–Œ β–’β–ˆβ–’β–€ β–€β–’β–’ β–’β–’β–’β–€β–„ β–„β–’β–„β–€β–€β–„▐▒▀ β–€β–’β–’ β–’β–’β–’β–€β–„ β–„β–’ β–ˆ β–„ β–„β–„β–„β–€β–€β–„ β–’ + β–€β–‘ β–’β–‘β–’β–„β–‘ β–€β–„β–’β–€β–’β–’ β–€β–ˆβ–’β–„β–„ β–„β– β–„β–’β–’β–€ β–’β–Œβ–‘β–‘▐β–‘β–€ β–’ β–€ β–„ β– β–’β–‘β–€ β–’ β–€β–‘ β–’β–‘β–Œβ–‘β–‘▐β–‘ β–€β–„ + β–Œβ–‘β–‘▐ β–Œβ–‘β–‘▐ + β–„β–„β–€β–€β–€β–€β–€β–€β–€β–„β–„β–€β–€β–„β–€β–€β–€β–€β–€β–„ β–„β–€β–€β–€β–€β–€β–„ β–Œβ–’β–’▐ β–„β–„β–€β–€β–€β–€β–€β–„ β–„β–€β–„β–„β–€β–€β–€β–€β–€β–„ β–„β–„β–„β–„β–„β–Œβ–’β–’▐ + β–Œβ–„β–“β–“β–“β–“β–“β–“β–“β–„ β–“β–“β–„β–“β–“β–“β–“β–“β–„β–€β–€β–„β–“β–“β–“β–“β–“β–„β–€β–„ β–Œβ–“β–“▐ β–Œβ–„β–“β–“β–“β–“β–“β–„β–€β–Œβ–“β–Œβ–„β–“β–“β–“β–“β–“β–„β–€β–€β–„β–„β–„β–„β–„β–„β–“β–“▐ + β–Œβ–ˆβ–Œ β–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–ˆ β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–Œ▐ β–Œβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–Œ β–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–ˆ β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–ˆ▐ + β–Œβ–€β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–Œβ–Œ β–Œβ–‘β–‘ β–‘β–‘ β–‘β–‘▐ β–Œβ–‘β–‘▐ β–Œβ–‘β–‘ β–‘β–‘ β–‘β–‘β–Œβ–ˆ β–Œβ–‘β–‘ β–‘β–‘β–Œ ▐β–‘β–‘▐ + ▐ β–’β–’ β–’β–’β–Œβ–Œ β–Œβ–’β–’ β–’β–’β–Œ ▐β–’β–’β–β–„β–€β–„β–Œβ–’β–’▐ β–Œβ–’β–’β–Œ ▐β–’β–’ β–’β–’β–Œβ–Œ β–Œβ–’β–’ β–’β–’β–Œ ▐β–’β–’▐ + β–Œβ–„β–„ β–“β–“ β–“β–“β–Œβ–Œ β–Œβ–“β–“ β–“β–“β–“ β–“β–“β–“ β–„β–“β–„ β–“β–“ β–€β–€ β–“β–“β–“ ▐β–“β–“ β–“β–“β–Œβ–Œ β–Œβ–“β–“ β–“β–“β–“ ▐β–“β–“▐ +β–‘ β–Œβ–€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–€ β–ˆβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–€β–„β–€β–ˆβ–ˆβ–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆ β–ˆβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆ▐ β–‘ +β–’ β–“β–“β–„β–„β–„β–„β–„β–„β–„β–„β–€β–„β–„β–„β–€ β–€β–„β–„β–€ β–€β–„β–„β–„β–„β–„β–€ β–€β–„β–€ β–€β–„β–„β–„β–„β–€ β–€β–„β–„β–„β–„β–€β–„β–„β–€β–„β–„β–„β–€ β–€β–„β–„β–€ β–€β–„β–„β–„β–„β–€β–„β–„β–“ β–’ +β–“ β–“ +β–ˆβ–“β–’β–‘ β–‘β–’β–“β–ˆ \ No newline at end of file diff --git a/contribs/gnodev/cmd/gnobro/assets/gn_hc3.utf8ans b/contribs/gnodev/cmd/gnobro/assets/gn_hc3.utf8ans new file mode 100644 index 00000000000..ff26c68f964 --- /dev/null +++ b/contribs/gnodev/cmd/gnobro/assets/gn_hc3.utf8ans @@ -0,0 +1,25 @@ + Β· . Β· Β· + . Β· . . Β· . Β· + Β· Β· Β· Β· . . . + Β· . . . . Β· Β· + . Β· . . Β· + β–‘β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘ β–‘β–‘β–‘β–‘ β–‘ β–‘β–‘ +β–‘β–‘β–’β–’β–‘β–‘β–‘β–’β–’β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–‘β–‘β–’β–‘β–‘β–’β–’β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–‘β–’β–’β–’β–‘β–’β–’β–’β–’β–’β–’β–‘β–‘β–’β–’β–’β–‘β–‘β–‘β–’β–’β–’β–’β–‘β–’β–‘β–‘β–’β–’β–‘β–‘ +β–’β–’β–“β–“β–’β–’β–’β–“β–“β–’β–’β–’β–’β–’β–“β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–’β–’β–“β–“β–’β–“β–’β–’β–’β–’β–’β–’β–’β–‘β–’β–’β–’β–’β–’β–’β–“β–“β–’β–“β–“β–“β–’β–“β–“β–“β–“β–“β–“β–’β–’β–“β–“β–“β–’β–’β–’β–“β–“β–“β–“β–’β–“β–’β–’β–“β–“β–’β–’ +β–“β–“β–ˆβ–ˆβ–“β–“β–“β–ˆβ–ˆβ–“β–“β–“β–“β–“β–ˆβ–“β–“β–“β–“β–“β–“β–ˆβ–ˆβ–“β–“β–ˆβ–“β–“β–ˆβ–ˆβ–“β–ˆβ–“β–“β–“β–“β–“β–“β–“β–’β–“β–“β–“β–“β–“β–“β–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–“β–ˆβ–ˆβ–ˆβ–“β–“β–“β–ˆβ–ˆβ–ˆβ–ˆβ–“β–ˆβ–“β–“β–ˆβ–ˆβ–“β–“ +β–€β–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Œβ–β–ˆβ–ˆβ–’β–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–Œβ–β–ˆβ–ˆβ–’β–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆ + β–β–’β–’β–’β–ˆ β–ˆβ–’β–ˆβ–Œ β–β–’β–ˆβ–ˆβ–ˆβ–Œ β–β–’β–’β–’β–Œ β–β–’β–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–ˆβ–Œ β–β–’β–’β–’β–Œ β–β–’β–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–’β–’β–’β–ˆβ–Œ ▐▒ + β–’β–’β–’β–’β–„ β–„β–’β–ˆ β–’β–ˆβ–’β–ˆβ–„ β–’β–’β–’β–‘ β–€β–’β–’β–ˆβ–Œ β–β–’β–ˆβ–ˆ▐ β–ˆβ–’β–ˆβ–„ β–’β–’β–’β–‘ β–€β–’β–’β–ˆβ–Œ ▐▒ β–ˆβ–„ β–„β–’β–’ β–’ β–’ + β–‘ β–‘ β–„ β–„β–„β–’β–Œ β–’β–ˆβ–’β–€ β–€β–’β–’ β–’β–’β–’β–€β–„ β–„β–’β–„β–€β–€β–„▐▒▀ β–€β–’β–’ β–’β–’β–’β–€β–„ β–„β–’ β–ˆ β–„ β–„β–„β–„β–€β–€β–„ β–’ + β–€β–‘ β–’β–‘β–’β–„β–‘ β–€β–„β–’β–€β–’β–’ β–€β–ˆβ–’β–„β–„ β–„β– β–„β–’β–’β–€ β–’β–Œβ–‘β–‘▐β–‘β–€ β–’ β–€ β–„ β– β–’β–‘β–€ β–’ β–€β–‘ β–’β–‘β–Œβ–‘β–‘▐β–‘ β–€β–„ + β–Œβ–‘β–‘▐ β–Œβ–‘β–‘▐ + β–„β–„β–€β–€β–€β–€β–€β–€β–€β–„β–„β–€β–€β–„β–€β–€β–€β–€β–€β–„ β–„β–€β–€β–€β–€β–€β–„ β–Œβ–’β–’▐ β–„β–„β–€β–€β–€β–€β–€β–„ β–„β–€β–„β–„β–€β–€β–€β–€β–€β–„ β–„β–„β–„β–„β–„β–Œβ–’β–’▐ + β–Œβ–„β–“β–“β–“β–“β–“β–“β–“β–„ β–“β–“β–„β–“β–“β–“β–“β–“β–„β–€β–€β–„β–“β–“β–“β–“β–“β–„β–€β–„ β–Œβ–“β–“▐ β–Œβ–„β–“β–“β–“β–“β–“β–„β–€β–Œβ–“β–Œβ–„β–“β–“β–“β–“β–“β–„β–€β–€β–„β–„β–„β–„β–„β–„β–“β–“▐ + β–Œβ–ˆβ–Œ β–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–ˆ β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–Œ▐ β–Œβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–Œ β–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–ˆ β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–ˆ▐ + β–Œβ–€β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–Œβ–Œ β–Œβ–‘β–‘ β–‘β–‘ β–‘β–‘▐ β–Œβ–‘β–‘▐ β–Œβ–‘β–‘ β–‘β–‘ β–‘β–‘β–Œβ–ˆ β–Œβ–‘β–‘ β–‘β–‘β–Œ ▐β–‘β–‘▐ + ▐ β–’β–’ β–’β–’β–Œβ–Œ β–Œβ–’β–’ β–’β–’β–Œ ▐β–’β–’β–β–„β–€β–„β–Œβ–’β–’▐ β–Œβ–’β–’β–Œ ▐β–’β–’ β–’β–’β–Œβ–Œ β–Œβ–’β–’ β–’β–’β–Œ ▐β–’β–’▐ + β–Œβ–„β–„ β–“β–“ β–“β–“β–Œβ–Œ β–Œβ–“β–“ β–“β–“β–“ β–“β–“β–“ β–„β–“β–„ β–“β–“ β–€β–€ β–“β–“β–“ ▐β–“β–“ β–“β–“β–Œβ–Œ β–Œβ–“β–“ β–“β–“β–“ ▐β–“β–“▐ +β–‘ β–Œβ–€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–€ β–ˆβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–€β–„β–€β–ˆβ–ˆβ–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆ β–ˆβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆ▐ β–‘ +β–’ β–“β–“β–„β–„β–„β–„β–„β–„β–„β–„β–€β–„β–„β–„β–€ β–€β–„β–„β–€ β–€β–„β–„β–„β–„β–„β–€ β–€β–„β–€ β–€β–„β–„β–„β–„β–€ β–€β–„β–„β–„β–„β–€β–„β–„β–€β–„β–„β–„β–€ β–€β–„β–„β–€ β–€β–„β–„β–„β–„β–€β–„β–„β–“ β–’ +β–“ β–“ +β–ˆβ–“β–’β–‘ β–‘β–’β–“β–ˆ \ No newline at end of file diff --git a/contribs/gnodev/cmd/gnobro/assets/gn_hc4.utf8ans b/contribs/gnodev/cmd/gnobro/assets/gn_hc4.utf8ans new file mode 100644 index 00000000000..b92dee86843 --- /dev/null +++ b/contribs/gnodev/cmd/gnobro/assets/gn_hc4.utf8ans @@ -0,0 +1,25 @@ + Β· . Β· Β· + . Β· . . Β· . Β· + Β· Β· Β· Β· . . . + Β· . . . . Β· Β· + . Β· . . Β· + β–‘β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘ β–‘β–‘β–‘β–‘ β–‘ β–‘β–‘ +β–‘β–‘β–’β–’β–‘β–‘β–‘β–’β–’β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–‘β–‘β–’β–‘β–‘β–’β–’β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–‘β–’β–’β–’β–‘β–’β–’β–’β–’β–’β–’β–‘β–‘β–’β–’β–’β–‘β–‘β–‘β–’β–’β–’β–’β–‘β–’β–‘β–‘β–’β–’β–‘β–‘ +β–’β–’β–“β–“β–’β–’β–’β–“β–“β–’β–’β–’β–’β–’β–“β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–’β–’β–“β–“β–’β–“β–’β–’β–’β–’β–’β–’β–’β–‘β–’β–’β–’β–’β–’β–’β–“β–“β–’β–“β–“β–“β–’β–“β–“β–“β–“β–“β–“β–’β–’β–“β–“β–“β–’β–’β–’β–“β–“β–“β–“β–’β–“β–’β–’β–“β–“β–’β–’ +β–“β–“β–ˆβ–ˆβ–“β–“β–“β–ˆβ–ˆβ–“β–“β–“β–“β–“β–ˆβ–“β–“β–“β–“β–“β–“β–ˆβ–ˆβ–“β–“β–ˆβ–“β–“β–ˆβ–ˆβ–“β–ˆβ–“β–“β–“β–“β–“β–“β–“β–’β–“β–“β–“β–“β–“β–“β–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–“β–ˆβ–ˆβ–ˆβ–“β–“β–“β–ˆβ–ˆβ–ˆβ–ˆβ–“β–ˆβ–“β–“β–ˆβ–ˆβ–“β–“ +β–€β–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Œβ–β–ˆβ–ˆβ–’β–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–Œβ–β–ˆβ–ˆβ–’β–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆ + β–β–’β–’β–’β–ˆ β–ˆβ–’β–ˆβ–Œ β–β–’β–ˆβ–ˆβ–ˆβ–Œ β–β–’β–’β–’β–Œ β–β–’β–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–ˆβ–Œ β–β–’β–’β–’β–Œ β–β–’β–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–’β–’β–’β–ˆβ–Œ ▐▒ + β–’β–’β–’β–’β–„ β–„β–’β–ˆ β–’β–ˆβ–’β–ˆβ–„ β–’β–’β–’β–‘ β–€β–’β–’β–ˆβ–Œ β–β–’β–ˆβ–ˆ▐ β–ˆβ–’β–ˆβ–„ β–’β–’β–’β–‘ β–€β–’β–’β–ˆβ–Œ ▐▒ β–ˆβ–„ β–„β–’β–’ β–’ β–’ + β–‘ β–‘ β–„ β–„β–„β–’β–Œ β–’β–ˆβ–’β–€ β–€β–’β–’ β–’β–’β–’β–€β–„ β–„β–’β–„β–€β–€β–„▐▒▀ β–€β–’β–’ β–’β–’β–’β–€β–„ β–„β–’ β–ˆ β–„ β–„β–„β–„β–€β–€β–„ β–’ + β–€β–‘ β–’β–‘β–’β–„β–‘ β–€β–„β–’β–€β–’β–’ β–€β–ˆβ–’β–„β–„ β–„β– β–„β–’β–’β–€ β–’β–Œβ–‘β–‘▐β–‘β–€ β–’ β–€ β–„ β– β–’β–‘β–€ β–’ β–€β–‘ β–’β–‘β–Œβ–‘β–‘▐β–‘ β–€β–„ + β–Œβ–‘β–‘▐ β–Œβ–‘β–‘▐ + β–„β–„β–€β–€β–€β–€β–€β–€β–€β–„β–„β–€β–€β–„β–€β–€β–€β–€β–€β–„ β–„β–€β–€β–€β–€β–€β–„ β–Œβ–’β–’▐ β–„β–„β–€β–€β–€β–€β–€β–„ β–„β–€β–„β–„β–€β–€β–€β–€β–€β–„ β–„β–„β–„β–„β–„β–Œβ–’β–’▐ + β–Œβ–„β–“β–“β–“β–“β–“β–“β–“β–„ β–“β–“β–„β–“β–“β–“β–“β–“β–„β–€β–€β–„β–“β–“β–“β–“β–“β–„β–€β–„ β–Œβ–“β–“▐ β–Œβ–„β–“β–“β–“β–“β–“β–„β–€β–Œβ–“β–Œβ–„β–“β–“β–“β–“β–“β–„β–€β–€β–„β–„β–„β–„β–„β–„β–“β–“▐ + β–Œβ–ˆβ–Œ β–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–ˆ β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–Œ▐ β–Œβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–Œ β–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–ˆ β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–ˆ▐ + β–Œβ–€β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–Œβ–Œ β–Œβ–‘β–‘ β–‘β–‘ β–‘β–‘▐ β–Œβ–‘β–‘▐ β–Œβ–‘β–‘ β–‘β–‘ β–‘β–‘β–Œβ–ˆ β–Œβ–‘β–‘ β–‘β–‘β–Œ ▐β–‘β–‘▐ + ▐ β–’β–’ β–’β–’β–Œβ–Œ β–Œβ–’β–’ β–’β–’β–Œ ▐β–’β–’β–β–„β–€β–„β–Œβ–’β–’▐ β–Œβ–’β–’β–Œ ▐β–’β–’ β–’β–’β–Œβ–Œ β–Œβ–’β–’ β–’β–’β–Œ ▐β–’β–’▐ + β–Œβ–„β–„ β–“β–“ β–“β–“β–Œβ–Œ β–Œβ–“β–“ β–“β–“β–“ β–“β–“β–“ β–„β–“β–„ β–“β–“ β–€β–€ β–“β–“β–“ ▐β–“β–“ β–“β–“β–Œβ–Œ β–Œβ–“β–“ β–“β–“β–“ ▐β–“β–“▐ +β–‘ β–Œβ–€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–€ β–ˆβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–€β–„β–€β–ˆβ–ˆβ–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆ β–ˆβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆ▐ β–‘ +β–’ β–“β–“β–„β–„β–„β–„β–„β–„β–„β–„β–€β–„β–„β–„β–€ β–€β–„β–„β–€ β–€β–„β–„β–„β–„β–„β–€ β–€β–„β–€ β–€β–„β–„β–„β–„β–€ β–€β–„β–„β–„β–„β–€β–„β–„β–€β–„β–„β–„β–€ β–€β–„β–„β–€ β–€β–„β–„β–„β–„β–€β–„β–„β–“ β–’ +β–“ β–“ +β–ˆβ–“β–’β–‘ β–‘β–’β–“β–ˆ \ No newline at end of file diff --git a/contribs/gnodev/cmd/gnobro/assets/gnoland-ansi-pink.utf8ans b/contribs/gnodev/cmd/gnobro/assets/gnoland-ansi-pink.utf8ans deleted file mode 100644 index bfda83f06c7..00000000000 --- a/contribs/gnodev/cmd/gnobro/assets/gnoland-ansi-pink.utf8ans +++ /dev/null @@ -1,25 +0,0 @@ - Β· . Β· Β· - . + . . * . Β· - Β· Β· Β· Β· . . Β· - Β· . . . . Β· + - . Β· . . Β· - β–‘β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘ β–‘β–‘β–‘β–‘ β–‘ β–‘β–‘ -β–‘β–‘β–’β–’β–‘β–‘β–‘β–’β–’β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–‘β–‘β–’β–‘β–‘β–’β–’β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–‘β–’β–’β–’β–‘β–’β–’β–’β–’β–’β–’β–‘β–‘β–’β–’β–’β–‘β–‘β–‘β–’β–’β–’β–’β–‘β–’β–‘β–‘β–’β–’β–‘β–‘ -β–’β–’β–“β–“β–’β–’β–’β–“β–“β–’β–’β–’β–’β–’β–“β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–’β–’β–“β–“β–’β–“β–’β–’β–’β–’β–’β–’β–’β–‘β–’β–’β–’β–’β–’β–’β–“β–“β–’β–“β–“β–“β–’β–“β–“β–“β–“β–“β–“β–’β–’β–“β–“β–“β–’β–’β–’β–“β–“β–“β–“β–’β–“β–’β–’β–“β–“β–’β–’ -β–“β–“β–ˆβ–ˆβ–“β–“β–“β–ˆβ–ˆβ–“β–“β–“β–“β–“β–ˆβ–“β–“β–“β–“β–“β–“β–ˆβ–ˆβ–“β–“β–ˆβ–“β–“β–ˆβ–ˆβ–“β–ˆβ–“β–“β–“β–“β–“β–“β–“β–’β–“β–“β–“β–“β–“β–“β–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–“β–ˆβ–ˆβ–ˆβ–“β–“β–“β–ˆβ–ˆβ–ˆβ–ˆβ–“β–ˆβ–“β–“β–ˆβ–ˆβ–“β–“ -β–€β–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Œβ–β–ˆβ–ˆβ–’β–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆβ–“β–ˆβ–ˆβ–Œβ–β–ˆβ–ˆβ–’β–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆ - β–β–’β–’β–’β–ˆ β–ˆβ–’β–ˆβ–Œ β–β–’β–ˆβ–ˆβ–ˆβ–Œ β–β–’β–’β–’β–Œ β–β–’β–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–ˆβ–ˆβ–Œ β–β–’β–’β–’β–Œ β–β–’β–’β–ˆβ–ˆβ–ˆβ–Œβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–’β–’β–’β–ˆβ–Œ ▐▒ - β–’β–’β–’β–’β–„ β–„β–’β–ˆ β–’β–ˆβ–’β–ˆβ–„ β–’β–’β–’β–‘ β–€β–’β–’β–ˆβ–Œ β–β–’β–ˆβ–ˆ▐ β–ˆβ–’β–ˆβ–„ β–’β–’β–’β–‘ β–€β–’β–’β–ˆβ–Œ ▐▒ β–ˆβ–„ β–„β–’β–’ β–’ β–’ - β–‘ β–‘ β–„ β–„β–„β–’β–Œ β–’β–ˆβ–’β–€ β–€β–’β–’ β–’β–’β–’β–€β–„ β–„β–’β–„β–€β–€β–„▐▒▀ β–€β–’β–’ β–’β–’β–’β–€β–„ β–„β–’ β–ˆ β–„ β–„β–„β–„β–€β–€β–„ β–’ - β–€β–‘ β–’β–‘β–’β–„β–‘ β–€β–„β–’β–€β–’β–’ β–€β–ˆβ–’β–„β–„ β–„β– β–„β–’β–’β–€ β–’β–Œ ▐β–‘β–€ β–’ β–€ β–„ β– β–’β–‘β–€ β–’ β–€β–‘ β–’β–‘β–Œ ▐β–‘ β–€β–„ - β–Œβ–‘β–‘▐ β–Œβ–‘β–‘▐ - β–„β–„β–€β–€β–€β–€β–€β–€β–€β–„β–„β–€β–€β–„β–€β–€β–€β–€β–€β–„ β–„β–€β–€β–€β–€β–€β–„ β–Œβ–’β–’▐ β–„β–„β–€β–€β–€β–€β–€β–„ β–„β–€β–„β–„β–€β–€β–€β–€β–€β–„ β–„β–„β–„β–„β–„β–Œβ–’β–’▐ - β–Œβ–„β–“β–“β–“β–“β–“β–“β–“β–„ β–“β–“β–„β–“β–“β–“β–“β–“β–„β–€β–€β–„β–“β–“β–“β–“β–“β–„β–€β–„ β–Œβ–“β–“▐ β–Œβ–„β–“β–“β–“β–“β–“β–„β–€β–Œβ–“β–Œβ–„β–“β–“β–“β–“β–“β–„β–€β–€β–„β–„β–„β–„β–„β–„β–“β–“▐ - β–Œβ–ˆβ–Œ β–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–ˆ β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–Œ▐ β–Œβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–Œ β–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–ˆ β–β–ˆβ–ˆβ–€β–€β–€β–ˆβ–ˆβ–ˆ▐ - β–Œβ–€β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–Œβ–Œ β–Œβ–‘β–‘ β–‘β–‘ β–‘β–‘▐ β–Œβ–‘β–‘▐ β–Œβ–‘β–‘ β–‘β–‘ β–‘β–‘β–Œβ–ˆ β–Œβ–‘β–‘ β–‘β–‘β–Œ ▐β–‘β–‘▐ - ▐ β–’β–’ β–’β–’β–Œβ–Œ β–Œβ–’β–’ β–’β–’β–Œ ▐β–’β–’β–β–„β–€β–„β–Œβ–’β–’▐ β–Œβ–’β–’β–Œ ▐β–’β–’ β–’β–’β–Œβ–Œ β–Œβ–’β–’ β–’β–’β–Œ ▐β–’β–’▐ - β–Œβ–„β–„ β–“β–“ β–“β–“β–Œβ–Œ β–Œβ–“β–“ β–“β–“β–“ β–“β–“β–“ β–„β–“β–„ β–“β–“ β–€β–€ β–“β–“β–“ ▐β–“β–“ β–“β–“β–Œβ–Œ β–Œβ–“β–“ β–“β–“β–“ ▐β–“β–“▐ -β–‘ β–Œβ–€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–€ β–ˆβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–€β–„β–€β–ˆβ–ˆβ–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆ β–ˆβ–ˆβ–ˆ▐ ▐β–β–ˆβ–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆ▐ β–‘ -β–’ β–“β–“β–„β–„β–„β–„β–„β–„β–„β–„β–€β–„β–„β–„β–€ β–€β–„β–„β–€ β–€β–„β–„β–„β–„β–„β–€ β–€β–„β–€ β–€β–„β–„β–„β–„β–€ β–€β–„β–„β–„β–„β–€β–„β–„β–€β–„β–„β–„β–€ β–€β–„β–„β–€ β–€β–„β–„β–„β–„β–€β–„β–„β–“ β–’ -β–“ β–“ -β–ˆβ–“β–’β–‘ β–‘β–’β–“β–ˆ \ No newline at end of file diff --git a/contribs/gnodev/cmd/gnobro/banner.go b/contribs/gnodev/cmd/gnobro/banner.go index bed04053ec0..58200130ce8 100644 --- a/contribs/gnodev/cmd/gnobro/banner.go +++ b/contribs/gnodev/cmd/gnobro/banner.go @@ -1,18 +1,41 @@ package main import ( - _ "embed" - - "strings" + "embed" + "fmt" + "os" + "path/filepath" "time" "github.com/gnolang/gno/contribs/gnodev/pkg/browser" ) -//go:embed assets/gnoland-ansi-pink.utf8ans -var banner_gnoland string +//go:embed assets/*.utf8ans +var banner_gnoland embed.FS func NewBanner_GnoLand() browser.ModelBanner { - r := strings.NewReader(banner_gnoland) - return browser.NewModelBanner(time.Second/50, r) + const assets = "assets" + + entries, err := banner_gnoland.ReadDir(assets) + if err != nil { + panic("unable to banner dir: " + err.Error()) + } + + frames := make([]string, len(entries)) + for i, entry := range entries { + if entry.IsDir() { + continue + } + + frame, err := banner_gnoland.ReadFile(filepath.Join(assets, entry.Name())) + if err != nil { + panic("unable to read banner frame: " + err.Error()) + } + + os.Stdout.Write(frame) + fmt.Println() + frames[i] = string(frame) + } + + return browser.NewModelBanner(time.Second/3, frames) } diff --git a/contribs/gnodev/pkg/browser/model.go b/contribs/gnodev/pkg/browser/model.go index 1c075961f7c..fbcdf358965 100644 --- a/contribs/gnodev/pkg/browser/model.go +++ b/contribs/gnodev/pkg/browser/model.go @@ -151,7 +151,7 @@ func New(cfg Config, client *gnoclient.Client) tea.Model { func (m model) Init() tea.Cmd { m.history.Init() - return m.banner.tick() + return m.banner.Init() } type fetchRealmMsg struct { @@ -377,6 +377,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, cmd } + // Update other models cmds := []tea.Cmd{cmd} if !m.bannerDiscarded { diff --git a/contribs/gnodev/pkg/browser/model_banner.go b/contribs/gnodev/pkg/browser/model_banner.go index 7dc745e86f4..25cccf4f921 100644 --- a/contribs/gnodev/pkg/browser/model_banner.go +++ b/contribs/gnodev/pkg/browser/model_banner.go @@ -2,8 +2,7 @@ package browser import ( "bufio" - "fmt" - "io" + "strings" "time" tea "github.com/charmbracelet/bubbletea" @@ -12,24 +11,38 @@ import ( type ModelBanner struct { Banner string + height int + offset int + frameIndex int + frames [][]string + enable bool scanner *bufio.Scanner fps time.Duration } -func NewModelBanner(fps time.Duration, banner io.Reader) ModelBanner { - scan := bufio.NewScanner(banner) +func NewModelBanner(fps time.Duration, frames []string) ModelBanner { + splited := make([][]string, len(frames)) + for i, frame := range frames { + lines := strings.Split(frame, "\n") + for j, line := range lines { + lines[j] = line + "\033[0m" + } + splited[i] = lines + } + return ModelBanner{ - scanner: scan, - fps: fps, + frames: splited, + fps: fps, } } func (m ModelBanner) Empty() bool { - return m.scanner == nil + return m.frames == nil } type tickBannerMsg struct{} +type tickBannerOffsetMsg struct{} func (m ModelBanner) tick() tea.Cmd { return tea.Tick(m.fps, func(_ time.Time) tea.Msg { @@ -37,22 +50,36 @@ func (m ModelBanner) tick() tea.Cmd { }) } +func (m ModelBanner) tickOffset() tea.Cmd { + return tea.Tick(time.Second/10, func(_ time.Time) tea.Msg { + return tickBannerOffsetMsg{} + }) +} + func (m ModelBanner) Init() tea.Cmd { if m.Empty() { return nil } - return m.tick() + return tea.Batch(m.tickOffset(), m.tick()) } func (m ModelBanner) Update(msg tea.Msg) (ModelBanner, tea.Cmd) { var cmd tea.Cmd switch msg.(type) { - case tickBannerMsg: - if !m.Empty() && m.scanner.Scan() { - m.Banner += fmt.Sprintln(m.scanner.Text()) - cmd = m.tick() + case tickBannerOffsetMsg: + frame := m.frames[m.frameIndex] + m.Banner = getFrameLinesOffset(frame, m.offset) + if m.offset < (len(frame) / 2) { + m.offset++ + cmd = m.tickOffset() } + + case tickBannerMsg: + frame := m.frames[m.frameIndex] + m.Banner = getFrameLinesOffset(frame, m.offset) + m.frameIndex = (m.frameIndex + 1) % len(m.frames) // move to next frame + cmd = m.tick() // XXX: handle window size } return m, cmd @@ -61,3 +88,14 @@ func (m ModelBanner) Update(msg tea.Msg) (ModelBanner, tea.Cmd) { func (m ModelBanner) View() string { return m.Banner } + +func getFrameLinesOffset(lines []string, offset int) string { + middle := len(lines) / 2 + if offset < middle { + start := middle - min(middle, offset) + end := middle + min(middle, offset) + lines = lines[start:end] + } + + return strings.Join(lines, "\n") +} diff --git a/contribs/gnodev/pkg/browser/model_view.go b/contribs/gnodev/pkg/browser/model_view.go index 85bf0736a67..2308134682f 100644 --- a/contribs/gnodev/pkg/browser/model_view.go +++ b/contribs/gnodev/pkg/browser/model_view.go @@ -49,10 +49,12 @@ func (m model) bannerView() string { return "" } + // XXX: Encapsulate banner to avoid banner glitchs bannerView := m.render.NewStyle().Margin(1). Render(banner) + widthView := m.width + 1 - return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, + return lipgloss.Place(widthView, m.height, lipgloss.Center, lipgloss.Center, lipgloss.JoinVertical(lipgloss.Center, bannerView, "press to continue", From 1caf3760a04441fe13913a2079be8aa44208e47c Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 30 Jul 2024 21:42:26 +0200 Subject: [PATCH 37/64] fix: client Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 13bab1205e5..e27ad73122e 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -195,7 +195,6 @@ func execBrowser(cfg *broCfg, args []string, cio commands.IO) error { bcfg := browser.DefaultConfig() bcfg.Readonly = cfg.readonly bcfg.Renderer = lipgloss.DefaultRenderer() - bcfg.GnoClient = gnocl bcfg.URLDefaultValue = path bcfg.URLPrefix = gnoPrefix bcfg.URLPrefix = gnoPrefix @@ -208,7 +207,7 @@ func execBrowser(cfg *broCfg, args []string, cio commands.IO) error { return runLocal(ctx, gnocl, cfg, bcfg, cio) } - return runServer(ctx, cfg, bcfg, cio) + return runServer(ctx, gnocl, cfg, bcfg, cio) } func runLocal(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg browser.Config, io commands.IO) error { From 4e5b806ba2bf84c1845d634b7de89fbfa7740556 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 30 Jul 2024 21:48:12 +0200 Subject: [PATCH 38/64] fix: middleware order Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index e27ad73122e..ae50bec653c 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -315,11 +315,11 @@ func runServer(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg b wish.WithHostKeyPath(cfg.sshHostKeyPath), wish.WithMiddleware( bubbletea.Middleware(teaHandler), + activeterm.Middleware(), // ensure PTY + ValidatePathCommandMiddleware(), logging.StructuredMiddlewareWithLogger( charmlogger, charmlog.DebugLevel, ), - activeterm.Middleware(), // ensure PTY - ValidatePathCommandMiddleware(), // XXX: add ip throttler ), ) From a8b8f3642050bf063328ffcfaee69ea646e0ee03 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 30 Jul 2024 21:52:34 +0200 Subject: [PATCH 39/64] chore: cleanup Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/banner.go | 4 ---- contribs/gnodev/pkg/browser/model_banner.go | 12 +++++------- contribs/gnodev/pkg/browser/model_view.go | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/banner.go b/contribs/gnodev/cmd/gnobro/banner.go index 58200130ce8..103b035160a 100644 --- a/contribs/gnodev/cmd/gnobro/banner.go +++ b/contribs/gnodev/cmd/gnobro/banner.go @@ -2,8 +2,6 @@ package main import ( "embed" - "fmt" - "os" "path/filepath" "time" @@ -32,8 +30,6 @@ func NewBanner_GnoLand() browser.ModelBanner { panic("unable to read banner frame: " + err.Error()) } - os.Stdout.Write(frame) - fmt.Println() frames[i] = string(frame) } diff --git a/contribs/gnodev/pkg/browser/model_banner.go b/contribs/gnodev/pkg/browser/model_banner.go index 25cccf4f921..ac983951dab 100644 --- a/contribs/gnodev/pkg/browser/model_banner.go +++ b/contribs/gnodev/pkg/browser/model_banner.go @@ -1,7 +1,6 @@ package browser import ( - "bufio" "strings" "time" @@ -11,14 +10,11 @@ import ( type ModelBanner struct { Banner string - height int offset int frameIndex int frames [][]string - enable bool - scanner *bufio.Scanner - fps time.Duration + fps time.Duration } func NewModelBanner(fps time.Duration, frames []string) ModelBanner { @@ -41,8 +37,10 @@ func (m ModelBanner) Empty() bool { return m.frames == nil } -type tickBannerMsg struct{} -type tickBannerOffsetMsg struct{} +type ( + tickBannerMsg struct{} + tickBannerOffsetMsg struct{} +) func (m ModelBanner) tick() tea.Cmd { return tea.Tick(m.fps, func(_ time.Time) tea.Msg { diff --git a/contribs/gnodev/pkg/browser/model_view.go b/contribs/gnodev/pkg/browser/model_view.go index 2308134682f..40d78d18c6d 100644 --- a/contribs/gnodev/pkg/browser/model_view.go +++ b/contribs/gnodev/pkg/browser/model_view.go @@ -49,7 +49,7 @@ func (m model) bannerView() string { return "" } - // XXX: Encapsulate banner to avoid banner glitchs + // XXX: Encapsulate banner to avoid banner glitches bannerView := m.render.NewStyle().Margin(1). Render(banner) widthView := m.width + 1 From 37d4b47a6bd1aec5fede3e3cd939fe938e0be8ce Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 11:48:12 +0200 Subject: [PATCH 40/64] chore: remove patch & toolchain in gnomod Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/go.mod | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod index 9e729edfeb5..0246544cf8c 100644 --- a/contribs/gnodev/go.mod +++ b/contribs/gnodev/go.mod @@ -1,8 +1,6 @@ module github.com/gnolang/gno/contribs/gnodev -go 1.22.1 - -toolchain go1.22.4 +go 1.22 replace github.com/gnolang/gno => ../.. From 599903f2055e448d8b18f2e7f956a35da99479b9 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 11:53:57 +0200 Subject: [PATCH 41/64] chore: rename bl -> cl Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/pkg/browser/client_node.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contribs/gnodev/pkg/browser/client_node.go b/contribs/gnodev/pkg/browser/client_node.go index 979f054bfd6..530f00c950e 100644 --- a/contribs/gnodev/pkg/browser/client_node.go +++ b/contribs/gnodev/pkg/browser/client_node.go @@ -31,7 +31,7 @@ func NewNodeClient(logger *slog.Logger, base gnoclient.BaseTxCfg, client *gnocli } } -func (bl *NodeClient) Call(path, call string) ([]byte, error) { +func (ncl *NodeClient) Call(path, call string) ([]byte, error) { method, args, err := parseMethodToArgs(call) if err != nil { return nil, fmt.Errorf("unable to parse method/args: %w", err) @@ -41,7 +41,7 @@ func (bl *NodeClient) Call(path, call string) ([]byte, error) { args = nil } - cm, err := bl.client.Call(bl.base, gnoclient.MsgCall{ + cm, err := ncl.client.Call(ncl.base, gnoclient.MsgCall{ PkgPath: path, FuncName: method, Args: args, @@ -61,8 +61,8 @@ func (bl *NodeClient) Call(path, call string) ([]byte, error) { return cm.DeliverTx.Data, nil } -func (bl *NodeClient) Funcs(path string) (vm.FunctionSignatures, error) { - res, err := bl.client.Query(gnoclient.QueryCfg{ +func (ncl *NodeClient) Funcs(path string) (vm.FunctionSignatures, error) { + res, err := ncl.client.Query(gnoclient.QueryCfg{ Path: "vm/qfuncs", Data: []byte(path), }) @@ -79,8 +79,8 @@ func (bl *NodeClient) Funcs(path string) (vm.FunctionSignatures, error) { return fsigs, nil } -func (bl *NodeClient) Render(path, args string) ([]byte, error) { - data, res, err := bl.client.Render(path, args) +func (ncl *NodeClient) Render(path, args string) ([]byte, error) { + data, res, err := ncl.client.Render(path, args) if err != nil { return nil, err } From 7e15baa0e3b74354f2e379ed36f354f1b2af9168 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 11:57:27 +0200 Subject: [PATCH 42/64] fix: use remove `Must` unmarshal amino Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/pkg/browser/client_node.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contribs/gnodev/pkg/browser/client_node.go b/contribs/gnodev/pkg/browser/client_node.go index 530f00c950e..d5434d1dd95 100644 --- a/contribs/gnodev/pkg/browser/client_node.go +++ b/contribs/gnodev/pkg/browser/client_node.go @@ -75,7 +75,10 @@ func (ncl *NodeClient) Funcs(path string) (vm.FunctionSignatures, error) { } var fsigs vm.FunctionSignatures - amino.MustUnmarshalJSON(res.Response.Data, &fsigs) + if err := amino.UnmarshalJSON(res.Response.Data, &fsigs); err != nil { + return nil, fmt.Errorf("unable to unmarshal response: %w", err) + } + return fsigs, nil } From 8c6b2c51734ae7c10657482fb01bca195805e53d Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 11:58:52 +0200 Subject: [PATCH 43/64] chore: have return explicit Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/pkg/browser/client_node.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contribs/gnodev/pkg/browser/client_node.go b/contribs/gnodev/pkg/browser/client_node.go index d5434d1dd95..7afcb2d9ba9 100644 --- a/contribs/gnodev/pkg/browser/client_node.go +++ b/contribs/gnodev/pkg/browser/client_node.go @@ -99,14 +99,13 @@ var reMethod = regexp.MustCompile(`([^(]+)\(([^)]*)\)`) func parseMethodToArgs(call string) (method string, args []string, err error) { matches := reMethod.FindStringSubmatch(call) if len(matches) == 0 { - err = fmt.Errorf("invalid call: %w", err) - return + return "", nil, fmt.Errorf("invalid call: %w", err) } method = matches[1] sargs := matches[2] if sargs == "" { - return + return method, args, err } // Splitting arguments by comma @@ -114,5 +113,6 @@ func parseMethodToArgs(call string) (method string, args []string, err error) { for i, arg := range args { args[i] = strings.Trim(strings.TrimSpace(arg), "\"") } - return + + return method, args, err } From 5e8e30ac19526d419b93fa175b088aa948563cbc Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:02:28 +0200 Subject: [PATCH 44/64] chore: use default gnoland prefix const Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/pkg/browser/model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/gnodev/pkg/browser/model.go b/contribs/gnodev/pkg/browser/model.go index fbcdf358965..534f7447d30 100644 --- a/contribs/gnodev/pkg/browser/model.go +++ b/contribs/gnodev/pkg/browser/model.go @@ -44,7 +44,7 @@ const DefaultGnoLandPrefix = "gno.land/" func DefaultConfig() Config { return Config{ Logger: log.NewNoopLogger(), - URLPrefix: "gno.land/", + URLPrefix: DefaultGnoLandPrefix, Renderer: lipgloss.DefaultRenderer(), URLDefaultValue: "gnoland/home", } From b9dfedc205db5628cf271fff0c434339841c11b8 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:09:29 +0200 Subject: [PATCH 45/64] fix: use remote instead of target Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index ae50bec653c..f32737a7528 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -83,9 +83,9 @@ with your realm. func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( &c.remote, - "target", + "remote", defaultBroOptions.remote, - "target gnoland address", + "remote gno.land URL", ) fs.StringVar( From b4801085c863a131bd24762b889a41e43c00c92b Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:10:16 +0200 Subject: [PATCH 46/64] chore: user `port` instead `port1` Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index f32737a7528..01bc5dbd2ba 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -357,10 +357,10 @@ func getDevEndpoint(cfg *broCfg) (string, error) { var err error // use remote address as default - host, port1 := cfg.remote, "8888" + host, port := cfg.remote, "8888" if cfg.devEndpoint != "" { // if any dev endpoint as been set, fallback on this - host, port1, err = net.SplitHostPort(cfg.devEndpoint) + host, port, err = net.SplitHostPort(cfg.devEndpoint) if err != nil { return "", fmt.Errorf("unable to parse dev endpoint: %w", err) } @@ -378,14 +378,14 @@ func getDevEndpoint(cfg *broCfg) (string, error) { } host, _, _ = net.SplitHostPort(devpoint.Host) - if port1 != "" { - devpoint.Host = host + ":" + port1 + if port != "" { + devpoint.Host = host + ":" + port } else { devpoint.Host = host } switch devpoint.Scheme { - case "ws", "wss": + case "ws", "wss": // already good case "https": devpoint.Scheme = "wss" default: From 2bfc1060232b16f44a3f11e41f372f4b3692969d Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:53:40 +0200 Subject: [PATCH 47/64] feat: better handle routine in gnobro main Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 103 +++++++++++++++-------------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 01bc5dbd2ba..f2d1d2de693 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -9,9 +9,9 @@ import ( "net" "net/url" "os" + "os/signal" "path/filepath" "strings" - "sync" "time" tea "github.com/charmbracelet/bubbletea" @@ -22,6 +22,7 @@ import ( "github.com/charmbracelet/wish/activeterm" "github.com/charmbracelet/wish/bubbletea" "github.com/charmbracelet/wish/logging" + "golang.org/x/sync/errgroup" "github.com/gnolang/gno/contribs/gnodev/pkg/browser" "github.com/gnolang/gno/contribs/gnodev/pkg/events" @@ -31,7 +32,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys" - osm "github.com/gnolang/gno/tm2/pkg/os" ) const gnoPrefix = "gno.land" @@ -213,28 +213,21 @@ func execBrowser(cfg *broCfg, args []string, cio commands.IO) error { func runLocal(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg browser.Config, io commands.IO) error { var err error - model := browser.New(bcfg, gnocl) - p := tea.NewProgram(model, - tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" - - tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel - ) - devpoint, err := getDevEndpoint(cfg) if err != nil { return fmt.Errorf("unable to parse dev endpoint: %w", err) } - var wg sync.WaitGroup + ctx, cancel := signal.NotifyContext(ctx, os.Interrupt) + defer cancel() - ctx, cancel := context.WithCancelCause(ctx) - defer cancel(nil) + model := browser.New(bcfg, gnocl) + p := tea.NewProgram(model, + tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" + tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel + ) - // setup trap signal - osm.TrapSignal(func() { - cancel(nil) - p.Quit() - }) + var errgs errgroup.Group if devpoint != "" { var devcl browser.DevClient @@ -248,26 +241,37 @@ func runLocal(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg br return nil } - wg.Add(1) - go func() { - defer wg.Done() + errgs.Go(func() error { + defer cancel() + if err := devcl.Run(ctx, devpoint, nil); err != nil { - cancel(fmt.Errorf("dev connection failed: %w", err)) + return fmt.Errorf("dev connection failed: %w", err) } - }() + + return nil + }) } - wg.Add(1) - go func() { - defer wg.Done() + errgs.Go(func() error { + defer cancel() + _, err := p.Run() - cancel(err) - }() + return err + }) - wg.Wait() + errgs.Go(func() error { + defer p.Quit() + + <-ctx.Done() + return ctx.Err() + }) + + if err := errgs.Wait(); err != nil && !errors.As(err, &context.Canceled) { + return err + } io.Println("Bye!") - return context.Cause(ctx) + return nil } func runServer(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg browser.Config, io commands.IO) error { @@ -324,33 +328,32 @@ func runServer(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg b ), ) - ctx, cancelCause := context.WithCancelCause(ctx) - defer cancelCause(nil) + var errgs errgroup.Group - // setup trap signal - osm.TrapSignal(func() { - logger.Info("stopping SSH server") - if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) { - cancelCause(fmt.Errorf("could not stop server: %w", err)) - } else { - cancelCause(nil) - } + errgs.Go(func() error { + logger.Info("starting SSH server", "addr", sshaddr.String()) + return s.ListenAndServe() }) - go func() { - logger.Info("starting SSH server", "addr", sshaddr.String()) - if err = s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) { - cancelCause(err) - } else { - cancelCause(nil) - } - }() + ctx, cancel := signal.NotifyContext(ctx, os.Interrupt) + defer cancel() - <-ctx.Done() + errgs.Go(func() error { + <-ctx.Done() - io.Println("Bye!") + sctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + logger.Info("stopping SSH server") + return s.Shutdown(sctx) + }) + + if err := errgs.Wait(); err != nil && !errors.Is(err, ssh.ErrServerClosed) { + return err + } - return context.Cause(ctx) + io.Println("Bye!") + return nil } func getDevEndpoint(cfg *broCfg) (string, error) { From 93f0742c423aae64eeeadd3c386c6262a3947592 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:54:44 +0200 Subject: [PATCH 48/64] fix: typo Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index f2d1d2de693..44f153ba792 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -134,7 +134,7 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { &c.banner, "banner", defaultBroOptions.banner, - "if enable, display a banner", + "if enabled, display a banner", ) fs.BoolVar( From 99d55ca3dcf50040bf82ae414c1ab74440e4b79f Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:59:19 +0200 Subject: [PATCH 49/64] fix: path command Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 44f153ba792..9e099903c48 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -292,9 +292,12 @@ func runServer(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg b bcfgCopy.Banner = NewBanner_GnoLand() } - if len(s.Command()) > 1 { + pval := s.Context().Value("path") + if path, ok := pval.(string); ok && len(path) > 0 { // Erase banner on specifc command bcfgCopy.Banner = browser.ModelBanner{} + // Set up url + bcfgCopy.URLDefaultValue = path } bcfgCopy.Logger.Info("session started", @@ -303,6 +306,7 @@ func runServer(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg b "sid", s.Context().SessionID(), "user", s.User()) model := browser.New(bcfgCopy, gnocl) + return model, []tea.ProgramOption{ tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel @@ -320,7 +324,7 @@ func runServer(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg b wish.WithMiddleware( bubbletea.Middleware(teaHandler), activeterm.Middleware(), // ensure PTY - ValidatePathCommandMiddleware(), + ValidatePathCommandMiddleware(bcfg.URLPrefix), logging.StructuredMiddlewareWithLogger( charmlogger, charmlog.DebugLevel, ), @@ -430,7 +434,7 @@ func getSignerForAccount(io commands.IO, address string, kb keys.Keybase, cfg *b return signer, nil } -func ValidatePathCommandMiddleware() wish.Middleware { +func ValidatePathCommandMiddleware(pathPrefix string) wish.Middleware { return func(next ssh.Handler) ssh.Handler { return func(s ssh.Session) { switch cmd := s.Command(); len(cmd) { @@ -439,7 +443,8 @@ func ValidatePathCommandMiddleware() wish.Middleware { return case 1: // check for valid path path := cmd[0] - if filepath.Clean(path) == path { + if strings.HasPrefix(path, pathPrefix) && filepath.Clean(path) == path { + s.Context().SetValue("path", path) next(s) return } From ea8a2a9bf5204fa3a8cb2bec650358029bab4939 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:59:28 +0200 Subject: [PATCH 50/64] chore: cleanup Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 9e099903c48..fb455cbefe6 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -345,10 +345,11 @@ func runServer(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg b errgs.Go(func() error { <-ctx.Done() + logger.Info("stopping SSH server... (5s timeout)") + sctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() - logger.Info("stopping SSH server") return s.Shutdown(sctx) }) @@ -408,7 +409,7 @@ func getSignerForAccount(io commands.IO, address string, kb keys.Keybase, cfg *b signer.Keybase = kb signer.Account = address - signer.ChainID = cfg.chainID // XXX: override this + signer.ChainID = cfg.chainID if ok, err := kb.HasByNameOrAddress(address); !ok || err != nil { if err != nil { From a50d52111822bdd4eaf51a1285845f76003da211 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:59:28 +0200 Subject: [PATCH 51/64] chore: cleanup Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/pkg/browser/model.go | 43 ++++++++++++++++------------ contribs/gnodev/pkg/browser/utils.go | 7 ----- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/contribs/gnodev/pkg/browser/model.go b/contribs/gnodev/pkg/browser/model.go index 534f7447d30..d4c533242af 100644 --- a/contribs/gnodev/pkg/browser/model.go +++ b/contribs/gnodev/pkg/browser/model.go @@ -16,6 +16,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" zone "github.com/lrstanley/bubblezone" + "github.com/muesli/reflow/wordwrap" "github.com/gnolang/gno/gno.land/pkg/gnoclient" "github.com/gnolang/gno/gno.land/pkg/sdk/vm" @@ -251,9 +252,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } else { path := m.getCurrentPath() m.logger.Info("rendering realm", "path", path) - cmd = m.RenderUpdate(m.getCurrentPath()) + cmd = m.RenderUpdate(path) } - return m, tea.Sequence(m.taskLoader.Add(1), cmd) case execCommandRequestMsg: @@ -264,26 +264,33 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case execCommandMsg: m.taskLoader.Done() + // If any error, display it as message. if msg.Error != nil { m.logger.Warn("command exec", "error", msg.Error) - content := fmt.Sprintf( - "%s\n\npress [enter] to dismiss", - warpLine(msg.Error.Error(), m.viewport.Width), - ) - m.viewport.SetContent(content) + + content := wordwrap.NewWriter(m.viewport.Width) + fmt.Fprint(content, msg.Error.Error()) + fmt.Fprintf(content, "\n\npress [enter] to dismiss error\n") + m.viewport.SetContent(content.String()) m.messageDisplay = true - } else { - content := string(msg.Response) - if strings.TrimSpace(content) == "" { - cmd = RefreshRealm() - m.messageDisplay = false - } else { - m.viewport.SetContent(fmt.Sprintf("%s\n\npress [enter] to dismiss", content)) - m.messageDisplay = true - } + return m, nil } - return m, cmd + // If any reponse, display it as message. + if res := bytes.TrimSpace(msg.Response); len(res) > 0 { + m.logger.Info("command exec", "res", string(res)) + + content := wordwrap.NewWriter(m.viewport.Width) + content.Write(res) + fmt.Fprintf(content, "\n\npress [enter] to dismiss message\n") + m.viewport.SetContent(content.String()) + m.messageDisplay = true + return m, nil + } + + // If no error or empty response is returned, simply refresh the page. + m.messageDisplay = false + return m, RefreshRealm() case renderUpdateMsg: m.taskLoader.Done() @@ -313,8 +320,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } m.viewport.SetContent(content) - return m, cmd + case SpinnerTickMsg: if m.taskLoader.Active() { m.taskLoader, cmd = m.taskLoader.Update(msg) diff --git a/contribs/gnodev/pkg/browser/utils.go b/contribs/gnodev/pkg/browser/utils.go index 18398c029b8..b322bea552a 100644 --- a/contribs/gnodev/pkg/browser/utils.go +++ b/contribs/gnodev/pkg/browser/utils.go @@ -5,15 +5,8 @@ import ( "strings" "github.com/gnolang/gno/gno.land/pkg/gnoweb" - "github.com/muesli/reflow/wordwrap" ) -func warpLine(str string, width int) string { - f := wordwrap.NewWriter(width) - f.Write([]byte(str)) - return f.String() -} - func redirectWebPath(path string) string { if alias, ok := gnoweb.Aliases[path]; ok { return alias From faf16a0eafde45f2847171f0a6a050ad68ba38b8 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 8 Aug 2024 17:27:20 +0200 Subject: [PATCH 52/64] chore: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/pkg/browser/model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/gnodev/pkg/browser/model.go b/contribs/gnodev/pkg/browser/model.go index d4c533242af..0a8a80e7562 100644 --- a/contribs/gnodev/pkg/browser/model.go +++ b/contribs/gnodev/pkg/browser/model.go @@ -276,7 +276,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } - // If any reponse, display it as message. + // If any response, display it as message. if res := bytes.TrimSpace(msg.Response); len(res) > 0 { m.logger.Info("command exec", "res", string(res)) From 9c787d61f77dd6ed32533887d87819683b9d4465 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Fri, 9 Aug 2024 09:10:08 +0200 Subject: [PATCH 53/64] fix: use Is instead of As Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index fb455cbefe6..1edb55e60de 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -266,7 +266,7 @@ func runLocal(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg br return ctx.Err() }) - if err := errgs.Wait(); err != nil && !errors.As(err, &context.Canceled) { + if err := errgs.Wait(); err != nil && !errors.Is(err, context.Canceled) { return err } From 94d1d031933dd4033d19d1254552f88ab20b1302 Mon Sep 17 00:00:00 2001 From: Guilhem Fanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:07:50 +0200 Subject: [PATCH 54/64] chore: use `contain` instead of `index` Co-authored-by: deelawn --- contribs/gnodev/cmd/gnobro/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 1edb55e60de..947f692f54d 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -375,7 +375,7 @@ func getDevEndpoint(cfg *broCfg) (string, error) { } // ensure having a (any) protocol scheme - if strings.Index(host, "://") < 0 { + if !strings.Contains(host, "://") { host = "http://" + host } From 0f9b41109c0ba2facbf2cd40a2cf89a10a36e604 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:06:40 +0200 Subject: [PATCH 55/64] chore: fixupy banner name Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/banner.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/banner.go b/contribs/gnodev/cmd/gnobro/banner.go index 103b035160a..311ee9bdb2a 100644 --- a/contribs/gnodev/cmd/gnobro/banner.go +++ b/contribs/gnodev/cmd/gnobro/banner.go @@ -9,12 +9,12 @@ import ( ) //go:embed assets/*.utf8ans -var banner_gnoland embed.FS +var gnoland_banner embed.FS -func NewBanner_GnoLand() browser.ModelBanner { +func NewGnoLandBanner() browser.ModelBanner { const assets = "assets" - entries, err := banner_gnoland.ReadDir(assets) + entries, err := gnoland_banner.ReadDir(assets) if err != nil { panic("unable to banner dir: " + err.Error()) } @@ -25,7 +25,7 @@ func NewBanner_GnoLand() browser.ModelBanner { continue } - frame, err := banner_gnoland.ReadFile(filepath.Join(assets, entry.Name())) + frame, err := gnoland_banner.ReadFile(filepath.Join(assets, entry.Name())) if err != nil { panic("unable to read banner frame: " + err.Error()) } From 253ebaf502eedfabfafdfce74eb99b5e75267036 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:09:23 +0200 Subject: [PATCH 56/64] chore: fix typo Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 947f692f54d..e3cbc65f5b7 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -106,7 +106,7 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { &c.defaultRealm, "default-realm", defaultBroOptions.defaultRealm, - "default realm to display when gnobro start and no argument are provided", + "default realm to display when gnobro starts and no arguments are provided", ) fs.StringVar( From 4fd1f60ecbc3b64098c1ea23d9736b1f911da793 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:11:26 +0200 Subject: [PATCH 57/64] chore: single argument Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index e3cbc65f5b7..ac83c0731f8 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -106,7 +106,7 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { &c.defaultRealm, "default-realm", defaultBroOptions.defaultRealm, - "default realm to display when gnobro starts and no arguments are provided", + "default realm to display when gnobro starts and no argument is provided", ) fs.StringVar( From 7d7775c0b1d2d4686bb8646da68b8af07d0ad66b Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:13:41 +0200 Subject: [PATCH 58/64] chore: update banner method name Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index ac83c0731f8..74cc4af0b96 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -201,7 +201,7 @@ func execBrowser(cfg *broCfg, args []string, cio commands.IO) error { if cfg.sshListener == "" { if cfg.banner { - bcfg.Banner = NewBanner_GnoLand() + bcfg.Banner = NewGnoLandBanner() } return runLocal(ctx, gnocl, cfg, bcfg, cio) @@ -289,7 +289,7 @@ func runServer(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg b bcfgCopy.Renderer = bubbletea.MakeRenderer(s) if cfg.banner { - bcfgCopy.Banner = NewBanner_GnoLand() + bcfgCopy.Banner = NewGnoLandBanner() } pval := s.Context().Value("path") From 0c2ba5df850f7b2f803ffac346badcfa4a0b9049 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:30:14 +0200 Subject: [PATCH 59/64] chore: add dev mode toogle flag Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 35 ++++++++++++++--------- contribs/gnodev/pkg/browser/client_dev.go | 17 ++++------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index 74cc4af0b96..c1ff2d36b92 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -39,7 +39,8 @@ const gnoPrefix = "gno.land" type broCfg struct { readonly bool remote string - devEndpoint string + dev bool + devRemote string chainID string defaultAccount string defaultRealm string @@ -50,7 +51,8 @@ type broCfg struct { var defaultBroOptions = broCfg{ remote: "127.0.0.1:26657", - devEndpoint: "", + dev: true, + devRemote: "", sshListener: "", defaultRealm: "gno.land/r/gnoland/home", chainID: "dev", @@ -123,10 +125,17 @@ func (c *broCfg) RegisterFlags(fs *flag.FlagSet) { "ssh host key path", ) - fs.StringVar( - &c.devEndpoint, + fs.BoolVar( + &c.dev, "dev", - defaultBroOptions.devEndpoint, + defaultBroOptions.dev, + "enable dev mode and connect to gnodev for realtime update", + ) + + fs.StringVar( + &c.devRemote, + "dev-remote", + defaultBroOptions.devRemote, "dev endpoint, if empty will default to `ws://:8888`", ) @@ -213,11 +222,6 @@ func execBrowser(cfg *broCfg, args []string, cio commands.IO) error { func runLocal(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg browser.Config, io commands.IO) error { var err error - devpoint, err := getDevEndpoint(cfg) - if err != nil { - return fmt.Errorf("unable to parse dev endpoint: %w", err) - } - ctx, cancel := signal.NotifyContext(ctx, os.Interrupt) defer cancel() @@ -229,7 +233,12 @@ func runLocal(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg br var errgs errgroup.Group - if devpoint != "" { + if cfg.dev { + devpoint, err := getDevEndpoint(cfg) + if err != nil { + return fmt.Errorf("unable to parse dev endpoint: %w", err) + } + var devcl browser.DevClient devcl.Handler = func(typ events.Type, data any) error { switch typ { @@ -366,9 +375,9 @@ func getDevEndpoint(cfg *broCfg) (string, error) { // use remote address as default host, port := cfg.remote, "8888" - if cfg.devEndpoint != "" { + if cfg.devRemote != "" { // if any dev endpoint as been set, fallback on this - host, port, err = net.SplitHostPort(cfg.devEndpoint) + host, port, err = net.SplitHostPort(cfg.devRemote) if err != nil { return "", fmt.Errorf("unable to parse dev endpoint: %w", err) } diff --git a/contribs/gnodev/pkg/browser/client_dev.go b/contribs/gnodev/pkg/browser/client_dev.go index a158e38373c..3d63b3abba7 100644 --- a/contribs/gnodev/pkg/browser/client_dev.go +++ b/contribs/gnodev/pkg/browser/client_dev.go @@ -14,7 +14,7 @@ import ( "github.com/gorilla/websocket" ) -const MaxElapsedTime = time.Second * 20 +const MaxBackoff = time.Second * 20 var ErrHandlerNotSet = errors.New("handler not set") @@ -22,8 +22,7 @@ type DevClient struct { Logger *slog.Logger Handler func(typ events.Type, data any) error - attempt int - conn *websocket.Conn + conn *websocket.Conn } func (c *DevClient) Run(ctx context.Context, addr string, header http.Header) error { @@ -67,7 +66,6 @@ func (c *DevClient) dialBackoff(ctx context.Context, addr string, header http.He c.Logger.Debug("connecting to dev events endpoint", addr, "addr") c.conn, _, err = dialer.DialContext(ctx, addr, header) - c.attempt++ if ctx.Err() != nil { return context.Cause(ctx) @@ -77,15 +75,10 @@ func (c *DevClient) dialBackoff(ctx context.Context, addr string, header http.He return nil } - // if fail on first attempt return an error early - if c.attempt < 1 { - return fmt.Errorf("unable to connect to dev endpoint: %w", err) - } - switch { - case backoff > MaxElapsedTime: - backoff = MaxElapsedTime - case backoff < MaxElapsedTime: + case backoff > MaxBackoff: + backoff = MaxBackoff + case backoff < MaxBackoff: backoff *= 2 default: } From 339e7bfbb8189eff00119b1b7793b0f898331608 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:30:39 +0200 Subject: [PATCH 60/64] fix: handle app by context Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index c1ff2d36b92..e1264351495 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -227,6 +227,7 @@ func runLocal(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg br model := browser.New(bcfg, gnocl) p := tea.NewProgram(model, + tea.WithContext(ctx), tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel ) @@ -268,13 +269,6 @@ func runLocal(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg br return err }) - errgs.Go(func() error { - defer p.Quit() - - <-ctx.Done() - return ctx.Err() - }) - if err := errgs.Wait(); err != nil && !errors.Is(err, context.Canceled) { return err } From b1c918399dfcfcf26b706a34aabb3ae745d9a875 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:34:55 +0200 Subject: [PATCH 61/64] chore: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/cmd/gnobro/main.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/contribs/gnodev/cmd/gnobro/main.go b/contribs/gnodev/cmd/gnobro/main.go index e1264351495..6bb6bfc2396 100644 --- a/contribs/gnodev/cmd/gnobro/main.go +++ b/contribs/gnodev/cmd/gnobro/main.go @@ -220,8 +220,6 @@ func execBrowser(cfg *broCfg, args []string, cio commands.IO) error { } func runLocal(ctx context.Context, gnocl *gnoclient.Client, cfg *broCfg, bcfg browser.Config, io commands.IO) error { - var err error - ctx, cancel := signal.NotifyContext(ctx, os.Interrupt) defer cancel() From d7af1923668adbdb73ab753ee67e449d288c32d0 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:46:46 +0200 Subject: [PATCH 62/64] fix: simply model logic Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/pkg/browser/model.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/contribs/gnodev/pkg/browser/model.go b/contribs/gnodev/pkg/browser/model.go index 0a8a80e7562..bdd7eac9c82 100644 --- a/contribs/gnodev/pkg/browser/model.go +++ b/contribs/gnodev/pkg/browser/model.go @@ -248,13 +248,14 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case fetchRealmMsg: if msg.realmPath != "" { - cmd = m.moveToRealm(msg.realmPath) - } else { - path := m.getCurrentPath() - m.logger.Info("rendering realm", "path", path) - cmd = m.RenderUpdate(path) + return m, tea.Sequence(m.taskLoader.Add(1), m.moveToRealm(msg.realmPath)) } - return m, tea.Sequence(m.taskLoader.Add(1), cmd) + + // If no realm path is given simply refresh the current realm + path := m.getCurrentPath() + m.logger.Info("rendering realm", "path", path) + + return m, tea.Sequence(m.taskLoader.Add(1), m.RenderUpdate(path)) case execCommandRequestMsg: m.logger.Info("requesting command", "path", msg.Path, "cmd", msg.Command) From c0cdc095da4d5987c8e6e89db93b011b5c87803c Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 22 Aug 2024 18:14:39 +0200 Subject: [PATCH 63/64] chore: lint Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod index 0246544cf8c..80e9867ab27 100644 --- a/contribs/gnodev/go.mod +++ b/contribs/gnodev/go.mod @@ -21,6 +21,7 @@ require ( github.com/sahilm/fuzzy v0.1.1 github.com/stretchr/testify v1.9.0 go.uber.org/zap v1.27.0 + golang.org/x/sync v0.7.0 golang.org/x/term v0.22.0 ) @@ -100,7 +101,6 @@ require ( golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect golang.org/x/mod v0.19.0 // indirect golang.org/x/net v0.27.0 // indirect - golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/tools v0.23.0 // indirect From 3ad927cf6c0e49a610458168faee99c6b4c1cd96 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 22 Aug 2024 19:04:06 +0200 Subject: [PATCH 64/64] fix: signer infos Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/pkg/browser/client_node.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/contribs/gnodev/pkg/browser/client_node.go b/contribs/gnodev/pkg/browser/client_node.go index 7afcb2d9ba9..2ffa26aa08f 100644 --- a/contribs/gnodev/pkg/browser/client_node.go +++ b/contribs/gnodev/pkg/browser/client_node.go @@ -41,10 +41,16 @@ func (ncl *NodeClient) Call(path, call string) ([]byte, error) { args = nil } - cm, err := ncl.client.Call(ncl.base, gnoclient.MsgCall{ - PkgPath: path, - FuncName: method, - Args: args, + infos, err := ncl.client.Signer.Info() + if err != nil { + return nil, fmt.Errorf("unable to get signer infos: %w", err) + } + + cm, err := ncl.client.Call(ncl.base, vm.MsgCall{ + Caller: infos.GetAddress(), + PkgPath: path, + Func: method, + Args: args, }) if err != nil { return nil, err