mirror of
https://github.com/jesseduffield/lazynpm.git
synced 2026-01-09 06:21:11 +08:00
allow adding a package
This commit is contained in:
parent
774e098d47
commit
02f0624909
@ -418,5 +418,9 @@ func FileExists(filename string) bool {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
// should actually do something here
|
||||
return false
|
||||
}
|
||||
return !info.IsDir()
|
||||
}
|
||||
|
||||
@ -22,7 +22,6 @@ type AppConfig struct {
|
||||
UserConfig *viper.Viper
|
||||
UserConfigDir string
|
||||
AppState *AppState
|
||||
IsNewPackage bool
|
||||
}
|
||||
|
||||
// AppConfigurer interface allows individual app config structs to inherit Fields
|
||||
@ -40,8 +39,6 @@ type AppConfigurer interface {
|
||||
WriteToUserConfig(string, interface{}) error
|
||||
SaveAppState() error
|
||||
LoadAppState() error
|
||||
SetIsNewPackage(bool)
|
||||
GetIsNewPackage() bool
|
||||
}
|
||||
|
||||
// NewAppConfig makes a new app config
|
||||
@ -65,7 +62,6 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg
|
||||
UserConfig: userConfig,
|
||||
UserConfigDir: filepath.Dir(userConfigPath),
|
||||
AppState: &AppState{},
|
||||
IsNewPackage: false,
|
||||
}
|
||||
|
||||
if err := appConfig.LoadAppState(); err != nil {
|
||||
@ -75,16 +71,6 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg
|
||||
return appConfig, nil
|
||||
}
|
||||
|
||||
// GetIsNewPackage returns known repo boolean
|
||||
func (c *AppConfig) GetIsNewPackage() bool {
|
||||
return c.IsNewPackage
|
||||
}
|
||||
|
||||
// SetIsNewPackage set if the current repo is known
|
||||
func (c *AppConfig) SetIsNewPackage(isNew bool) {
|
||||
c.IsNewPackage = isNew
|
||||
}
|
||||
|
||||
// GetDebug returns debug flag
|
||||
func (c *AppConfig) GetDebug() bool {
|
||||
return c.Debug
|
||||
|
||||
@ -15,11 +15,24 @@ import (
|
||||
"github.com/jesseduffield/lazynpm/pkg/theme"
|
||||
)
|
||||
|
||||
func (gui *Gui) wrappedConfirmationFunction(function func(*gocui.Gui, *gocui.View) error, returnFocusOnClose bool) func(*gocui.Gui, *gocui.View) error {
|
||||
func (gui *Gui) wrappedConfirmationFunction(function func() error, returnFocusOnClose bool) func(*gocui.Gui, *gocui.View) error {
|
||||
return func(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
if function != nil {
|
||||
if err := function(g, v); err != nil {
|
||||
if err := function(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return gui.closeConfirmationPrompt(g, returnFocusOnClose)
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) wrappedPromptConfirmationFunction(function func(string) error, returnFocusOnClose bool) func(*gocui.Gui, *gocui.View) error {
|
||||
return func(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
if function != nil {
|
||||
if err := function(v.Buffer()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -101,44 +114,68 @@ func (gui *Gui) onNewPopupPanel() {
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) createPopupPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, hasLoader bool, returnFocusOnClose bool, editable bool, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
||||
type createPopupPanelOpts struct {
|
||||
hasLoader bool
|
||||
returnFocusOnClose bool
|
||||
editable bool
|
||||
prompt string
|
||||
handleConfirm func() error
|
||||
handleConfirmPrompt func(string) error
|
||||
handleClose func() error
|
||||
}
|
||||
|
||||
func (gui *Gui) createPopupPanel(currentView *gocui.View, title string, opts createPopupPanelOpts) error {
|
||||
gui.onNewPopupPanel()
|
||||
g.Update(func(g *gocui.Gui) error {
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
// delete the existing confirmation panel if it exists
|
||||
if view, _ := g.View("confirmation"); view != nil {
|
||||
if err := gui.closeConfirmationPrompt(g, true); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
}
|
||||
confirmationView, err := gui.prepareConfirmationPanel(currentView, title, prompt, hasLoader)
|
||||
confirmationView, err := gui.prepareConfirmationPanel(currentView, title, opts.prompt, opts.hasLoader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
confirmationView.Editable = editable
|
||||
if editable {
|
||||
confirmationView.Editable = opts.editable
|
||||
if opts.editable {
|
||||
confirmationView.EditGotoToEndOfLine()
|
||||
}
|
||||
|
||||
gui.renderString("confirmation", prompt)
|
||||
return gui.setKeyBindings(g, handleConfirm, handleClose, returnFocusOnClose)
|
||||
gui.renderString("confirmation", opts.prompt)
|
||||
return gui.setKeyBindings(opts)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) createLoaderPanel(g *gocui.Gui, currentView *gocui.View, prompt string) error {
|
||||
return gui.createPopupPanel(g, currentView, "", prompt, true, true, false, nil, nil)
|
||||
return gui.createPopupPanel(currentView, "", createPopupPanelOpts{
|
||||
prompt: prompt,
|
||||
hasLoader: true,
|
||||
returnFocusOnClose: true,
|
||||
})
|
||||
}
|
||||
|
||||
// it is very important that within this function we never include the original prompt in any error messages, because it may contain e.g. a user password
|
||||
func (gui *Gui) createConfirmationPanel(currentView *gocui.View, returnFocusOnClose bool, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
||||
return gui.createPopupPanel(gui.g, currentView, title, prompt, false, returnFocusOnClose, false, handleConfirm, handleClose)
|
||||
func (gui *Gui) createConfirmationPanel(currentView *gocui.View, returnFocusOnClose bool, title, prompt string, handleConfirm, handleClose func() error) error {
|
||||
return gui.createPopupPanel(currentView, title, createPopupPanelOpts{
|
||||
prompt: prompt,
|
||||
returnFocusOnClose: returnFocusOnClose,
|
||||
handleConfirm: handleConfirm,
|
||||
handleClose: handleClose,
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) createPromptPanel(currentView *gocui.View, title string, initialContent string, handleConfirm func(*gocui.Gui, *gocui.View) error) error {
|
||||
return gui.createPopupPanel(gui.g, currentView, title, initialContent, false, true, true, handleConfirm, nil)
|
||||
func (gui *Gui) createPromptPanel(currentView *gocui.View, title string, initialContent string, handleConfirm func(string) error) error {
|
||||
return gui.createPopupPanel(currentView, title, createPopupPanelOpts{
|
||||
prompt: initialContent,
|
||||
returnFocusOnClose: true,
|
||||
editable: true,
|
||||
handleConfirmPrompt: handleConfirm,
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) setKeyBindings(g *gocui.Gui, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error, returnFocusOnClose bool) error {
|
||||
func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
||||
actions := gui.Tr.TemplateLocalize(
|
||||
"CloseConfirm",
|
||||
Teml{
|
||||
@ -148,27 +185,27 @@ func (gui *Gui) setKeyBindings(g *gocui.Gui, handleConfirm, handleClose func(*go
|
||||
)
|
||||
|
||||
gui.renderString("options", actions)
|
||||
if err := g.SetKeybinding("confirmation", nil, gocui.KeyEnter, gocui.ModNone, gui.wrappedConfirmationFunction(handleConfirm, returnFocusOnClose)); err != nil {
|
||||
return err
|
||||
if opts.handleConfirmPrompt != nil {
|
||||
if err := gui.g.SetKeybinding("confirmation", nil, gocui.KeyEnter, gocui.ModNone, gui.wrappedPromptConfirmationFunction(opts.handleConfirmPrompt, opts.returnFocusOnClose)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := gui.g.SetKeybinding("confirmation", nil, gocui.KeyEnter, gocui.ModNone, gui.wrappedConfirmationFunction(opts.handleConfirm, opts.returnFocusOnClose)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return g.SetKeybinding("confirmation", nil, gocui.KeyEsc, gocui.ModNone, gui.wrappedConfirmationFunction(handleClose, returnFocusOnClose))
|
||||
|
||||
return gui.g.SetKeybinding("confirmation", nil, gocui.KeyEsc, gocui.ModNone, gui.wrappedConfirmationFunction(opts.handleClose, opts.returnFocusOnClose))
|
||||
}
|
||||
|
||||
// createSpecificErrorPanel allows you to create an error popup, specifying the
|
||||
// view to be focused when the user closes the popup, and a boolean specifying
|
||||
// whether we will log the error. If the message may include a user password,
|
||||
// this function is to be used over the more generic createErrorPanel, with
|
||||
// willLog set to false
|
||||
func (gui *Gui) createSpecificErrorPanel(message string, nextView *gocui.View, willLog bool) error {
|
||||
if willLog {
|
||||
go func() {
|
||||
// when reporting is switched on this log call sometimes introduces
|
||||
// a delay on the error panel popping up. Here I'm adding a second wait
|
||||
// so that the error is logged while the user is reading the error message
|
||||
time.Sleep(time.Second)
|
||||
gui.Log.Error(message)
|
||||
}()
|
||||
}
|
||||
func (gui *Gui) createErrorPanel(message string) error {
|
||||
go func() {
|
||||
// when reporting is switched on this log call sometimes introduces
|
||||
// a delay on the error panel popping up. Here I'm adding a second wait
|
||||
// so that the error is logged while the user is reading the error message
|
||||
time.Sleep(time.Second)
|
||||
gui.Log.Error(message)
|
||||
}()
|
||||
|
||||
colorFunction := color.New(color.FgRed).SprintFunc()
|
||||
coloredMessage := colorFunction(strings.TrimSpace(message))
|
||||
@ -176,11 +213,8 @@ func (gui *Gui) createSpecificErrorPanel(message string, nextView *gocui.View, w
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.createConfirmationPanel(nextView, true, gui.Tr.SLocalize("Error"), coloredMessage, nil, nil)
|
||||
}
|
||||
|
||||
func (gui *Gui) createErrorPanel(message string) error {
|
||||
return gui.createSpecificErrorPanel(message, gui.g.CurrentView(), true)
|
||||
// TODO: see if returning to the current view is bad in the case of popup views
|
||||
return gui.createConfirmationPanel(gui.g.CurrentView(), true, gui.Tr.SLocalize("Error"), coloredMessage, nil, nil)
|
||||
}
|
||||
|
||||
func (gui *Gui) surfaceError(err error) error {
|
||||
|
||||
@ -334,44 +334,6 @@ func (gui *Gui) updateRecentPackagesList() error {
|
||||
return errors.New("Must open lazynpm in an npm package")
|
||||
}
|
||||
|
||||
func (gui *Gui) sendPackageToTop(path string) error {
|
||||
// in case we're not already there, chdir to path
|
||||
if err := os.Chdir(path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
recentPackages := gui.Config.GetAppState().RecentPackages
|
||||
isNew, recentPackages := newRecentPackagesList(recentPackages, path)
|
||||
gui.Config.SetIsNewPackage(isNew)
|
||||
gui.Config.GetAppState().RecentPackages = recentPackages
|
||||
return gui.Config.SaveAppState()
|
||||
}
|
||||
|
||||
func (gui *Gui) removePackage(path string) error {
|
||||
recentPackages := gui.Config.GetAppState().RecentPackages
|
||||
index, ok := utils.StringIndex(recentPackages, path)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
recentPackages = append(recentPackages[:index], recentPackages[index+1:]...)
|
||||
gui.Config.GetAppState().RecentPackages = recentPackages
|
||||
return gui.Config.SaveAppState()
|
||||
}
|
||||
|
||||
// newRecentPackagesList returns a new repo list with a new entry but only when it doesn't exist yet
|
||||
func newRecentPackagesList(recentPackages []string, currentPackage string) (bool, []string) {
|
||||
isNew := true
|
||||
newPackages := []string{currentPackage}
|
||||
for _, pkg := range recentPackages {
|
||||
if pkg != currentPackage {
|
||||
newPackages = append(newPackages, pkg)
|
||||
} else {
|
||||
isNew = false
|
||||
}
|
||||
}
|
||||
return isNew, newPackages
|
||||
}
|
||||
|
||||
func (gui *Gui) showInitialPopups(tasks []func(chan struct{}) error) {
|
||||
gui.waitForIntro.Add(len(tasks))
|
||||
done := make(chan struct{})
|
||||
@ -391,7 +353,7 @@ func (gui *Gui) showInitialPopups(tasks []func(chan struct{}) error) {
|
||||
}
|
||||
|
||||
func (gui *Gui) showShamelessSelfPromotionMessage(done chan struct{}) error {
|
||||
onConfirm := func(g *gocui.Gui, v *gocui.View) error {
|
||||
onConfirm := func() error {
|
||||
done <- struct{}{}
|
||||
return gui.Config.WriteToUserConfig("startupPopupVersion", StartupPopupVersion)
|
||||
}
|
||||
@ -400,10 +362,10 @@ func (gui *Gui) showShamelessSelfPromotionMessage(done chan struct{}) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) promptAnonymousReporting(done chan struct{}) error {
|
||||
return gui.createConfirmationPanel(nil, true, gui.Tr.SLocalize("AnonymousReportingTitle"), gui.Tr.SLocalize("AnonymousReportingPrompt"), func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.createConfirmationPanel(nil, true, gui.Tr.SLocalize("AnonymousReportingTitle"), gui.Tr.SLocalize("AnonymousReportingPrompt"), func() error {
|
||||
done <- struct{}{}
|
||||
return gui.Config.WriteToUserConfig("reporting", "on")
|
||||
}, func(g *gocui.Gui, v *gocui.View) error {
|
||||
}, func() error {
|
||||
done <- struct{}{}
|
||||
return gui.Config.WriteToUserConfig("reporting", "off")
|
||||
})
|
||||
|
||||
@ -440,6 +440,12 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.wrappedHandler(gui.handleCheckoutPackage),
|
||||
},
|
||||
{
|
||||
ViewName: "packages",
|
||||
Key: gui.getKey("universal.new"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.wrappedHandler(gui.handleAddPackage),
|
||||
},
|
||||
{
|
||||
ViewName: "packages",
|
||||
Key: gui.getKey("packages.link"),
|
||||
|
||||
@ -2,6 +2,8 @@ package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/go-errors/errors"
|
||||
@ -222,8 +224,22 @@ func (gui *Gui) handleRemovePackage() error {
|
||||
return gui.createErrorPanel("Cannot remove current package")
|
||||
}
|
||||
|
||||
return gui.createConfirmationPanel(gui.getPackagesView(), true, "Remove package", "Do you want to remove this package from the list? It won't actually be removed from the filesystem, but as far as lazynpm is concerned it'll be as good as dead. You won't have to worry about it no more.", func(*gocui.Gui, *gocui.View) error {
|
||||
return gui.createConfirmationPanel(gui.getPackagesView(), true, "Remove package", "Do you want to remove this package from the list? It won't actually be removed from the filesystem, but as far as lazynpm is concerned it'll be as good as dead. You won't have to worry about it no more.", func() error {
|
||||
return gui.removePackage(selectedPkg.Path)
|
||||
},
|
||||
nil)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleAddPackage() error {
|
||||
return gui.createPromptPanel(gui.getPackagesView(), "Add package path to add", "", func(input string) error {
|
||||
configPath := input
|
||||
if !strings.HasSuffix(configPath, "package.json") {
|
||||
configPath = filepath.Join(configPath, "package.json")
|
||||
}
|
||||
if !commands.FileExists(configPath) {
|
||||
return gui.createErrorPanel(fmt.Sprintf("%s not found", configPath))
|
||||
}
|
||||
|
||||
return gui.addPackage(strings.TrimSuffix(input, "package.json"))
|
||||
})
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ func (gui *Gui) quit(v *gocui.View) error {
|
||||
}
|
||||
|
||||
if gui.Config.GetUserConfig().GetBool("confirmOnQuit") {
|
||||
return gui.createConfirmationPanel(v, true, "", gui.Tr.SLocalize("ConfirmQuit"), func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.createConfirmationPanel(v, true, "", gui.Tr.SLocalize("ConfirmQuit"), func() error {
|
||||
return gocui.ErrQuit
|
||||
}, nil)
|
||||
}
|
||||
|
||||
69
pkg/gui/recent_packages.go
Normal file
69
pkg/gui/recent_packages.go
Normal file
@ -0,0 +1,69 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/jesseduffield/lazynpm/pkg/utils"
|
||||
)
|
||||
|
||||
func (gui *Gui) mutateRecentPackages(f func([]string) ([]string, bool)) error {
|
||||
recentPackages := gui.Config.GetAppState().RecentPackages
|
||||
|
||||
recentPackages, changed := f(recentPackages)
|
||||
if !changed {
|
||||
return nil
|
||||
}
|
||||
|
||||
gui.Config.GetAppState().RecentPackages = recentPackages
|
||||
return gui.Config.SaveAppState()
|
||||
|
||||
}
|
||||
|
||||
func (gui *Gui) sendPackageToTop(path string) error {
|
||||
// in case we're not already there, chdir to path
|
||||
if err := os.Chdir(path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.mutateRecentPackages(func(recentPackages []string) ([]string, bool) {
|
||||
updatedRecentPackages := newRecentPackagesList(recentPackages, path)
|
||||
// just unconditionally saying we updated it even if we didn't
|
||||
return updatedRecentPackages, true
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) removePackage(path string) error {
|
||||
return gui.mutateRecentPackages(func(recentPackages []string) ([]string, bool) {
|
||||
index, ok := utils.StringIndex(recentPackages, path)
|
||||
if !ok {
|
||||
// not removing it if it's already been removed
|
||||
return nil, false
|
||||
}
|
||||
updatedRecentPackages := append(recentPackages[:index], recentPackages[index+1:]...)
|
||||
return updatedRecentPackages, true
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) addPackage(path string) error {
|
||||
return gui.mutateRecentPackages(func(recentPackages []string) ([]string, bool) {
|
||||
_, ok := utils.StringIndex(recentPackages, path)
|
||||
if ok {
|
||||
// not adding it if it's already present
|
||||
return nil, false
|
||||
}
|
||||
updatedRecentPackages := append(recentPackages, path)
|
||||
return updatedRecentPackages, true
|
||||
})
|
||||
}
|
||||
|
||||
// newRecentPackagesList returns a new repo list with a new entry but only when it doesn't exist yet
|
||||
// if it already exists, it will be moved to the start of the array
|
||||
func newRecentPackagesList(recentPackages []string, currentPackage string) []string {
|
||||
newPackages := []string{currentPackage}
|
||||
for _, pkg := range recentPackages {
|
||||
if pkg != currentPackage {
|
||||
newPackages = append(newPackages, pkg)
|
||||
}
|
||||
}
|
||||
return newPackages
|
||||
}
|
||||
@ -2,7 +2,6 @@ package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazynpm/pkg/commands"
|
||||
@ -38,10 +37,9 @@ func (gui *Gui) handleScriptSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
func (gui *Gui) handleRunScript() error {
|
||||
script := gui.getSelectedScript()
|
||||
|
||||
return gui.createPromptPanel(gui.getScriptsView(), "run script", fmt.Sprintf("npm run %s ", script.Name), func(g *gocui.Gui, v *gocui.View) error {
|
||||
cmdStr := strings.TrimSpace(v.Buffer())
|
||||
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
||||
if err := gui.newPtyTask("main", cmd, cmdStr); err != nil {
|
||||
return gui.createPromptPanel(gui.getScriptsView(), "run script", fmt.Sprintf("npm run %s ", script.Name), func(input string) error {
|
||||
cmd := gui.OSCommand.ExecutableFromString(input)
|
||||
if err := gui.newPtyTask("main", cmd, input); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
return nil
|
||||
@ -51,7 +49,7 @@ func (gui *Gui) handleRunScript() error {
|
||||
func (gui *Gui) handleRemoveScript() error {
|
||||
script := gui.getSelectedScript()
|
||||
|
||||
return gui.createConfirmationPanel(gui.getScriptsView(), true, "Remove script", fmt.Sprintf("are you sure you want to remove script `%s`?", script.Name), func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.createConfirmationPanel(gui.getScriptsView(), true, "Remove script", fmt.Sprintf("are you sure you want to remove script `%s`?", script.Name), func() error {
|
||||
return gui.surfaceError(
|
||||
gui.NpmManager.RemoveScript(script.Name, gui.currentPackage().ConfigPath()),
|
||||
)
|
||||
|
||||
@ -6,7 +6,7 @@ func (gui *Gui) showUpdatePrompt(newVersion string) error {
|
||||
title := "New version available!"
|
||||
message := "Download latest version? (enter/esc)"
|
||||
currentView := gui.g.CurrentView()
|
||||
return gui.createConfirmationPanel(currentView, true, title, message, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.createConfirmationPanel(currentView, true, title, message, func() error {
|
||||
gui.startUpdating(newVersion)
|
||||
return nil
|
||||
}, nil)
|
||||
@ -57,7 +57,7 @@ func (gui *Gui) onUpdateFinish(err error) error {
|
||||
func (gui *Gui) createUpdateQuitConfirmation(g *gocui.Gui, v *gocui.View) error {
|
||||
title := "Currently Updating"
|
||||
message := "An update is in progress. Are you sure you want to quit?"
|
||||
return gui.createConfirmationPanel(v, true, title, message, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.createConfirmationPanel(v, true, title, message, func() error {
|
||||
return gocui.ErrQuit
|
||||
}, nil)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user