diff --git a/docs/public/sponsors/zsa.png b/docs/public/sponsors/zsa.png
new file mode 100644
index 000000000..507e983fa
Binary files /dev/null and b/docs/public/sponsors/zsa.png differ
diff --git a/docs/src/content/docs/changelog.mdx b/docs/src/content/docs/changelog.mdx
index bc612c4af..91f828eb7 100644
--- a/docs/src/content/docs/changelog.mdx
+++ b/docs/src/content/docs/changelog.mdx
@@ -23,7 +23,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
*/
-
## [Unreleased]
### Added
@@ -34,8 +33,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `init()` method in runtime to allow manual initialisation of the runtime by [@leaanthony](https://github.com/leaanthony)
- Added `WindowDidMoveDebounceMS` option to Window's WindowOptions by [@leaanthony](https://github.com/leaanthony)
- Added Single Instance feature by [@leaanthony](https://github.com/leaanthony). Based on the [v2 PR](https://github.com/wailsapp/wails/pull/2951) by @APshenkin.
-- Added template generation using `wails3 generate template` command by [@leaanthony](https://github.com/leaanthony)
-- Added `wails3 releasenotes` command by [@leaanthony](https://github.com/leaanthony)
+- `wails3 generate template` command by [@leaanthony](https://github.com/leaanthony)
+- `wails3 releasenotes` command by [@leaanthony](https://github.com/leaanthony)
+- `wails3 update cli` command by [@leaanthony](https://github.com/leaanthony)
### Fixed
@@ -64,7 +64,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Removed `Window.Destroy` as this was intended to be an internal function by [@leaanthony](https://github.com/leaanthony)
- Renamed `WindowClose` events to `WindowClosing` by [@leaanthony](https://github.com/leaanthony)
-
## v3.0.0-alpha.8.3 - 2024-12-07
### Changed
diff --git a/docs/src/content/docs/credits.mdx b/docs/src/content/docs/credits.mdx
index bf6717a74..5ed2ed9f4 100644
--- a/docs/src/content/docs/credits.mdx
+++ b/docs/src/content/docs/credits.mdx
@@ -16,7 +16,14 @@ import Contributors from "../../assets/contributors.html";
- [Lyimmi](https://github.com/Lyimmi) - All things Linux
## Sponsors
-
+
+
+
+
`.
+### `update cli`
+Updates the Wails CLI to a new version.
+
+```bash
+wails3 update cli [flags]
+```
+
+#### Flags
+| Flag | Description | Default |
+|-------------|---------------------------------|---------|
+| `-pre` | Update to latest pre-release | `false` |
+| `-version` | Update to specific version | |
+| `-nocolour` | Disable colored output | `false` |
+
+The update cli command allows you to update your Wails CLI installation. By default, it updates to the latest stable release.
+You can use the `-pre` flag to update to the latest pre-release version, or specify a particular version using the `-version` flag.
+
+After updating, remember to update your project's go.mod file to use the same version:
+```bash
+require github.com/wailsapp/wails/v3 v3.x.x
+```
+
### `update build-assets`
Updates the build assets using the given config file.
@@ -413,4 +435,3 @@ Opens the Wails sponsorship page in your default browser.
```bash
wails3 sponsor
-
diff --git a/v3/cmd/wails3/main.go b/v3/cmd/wails3/main.go
index a1684cc33..56017cf49 100644
--- a/v3/cmd/wails3/main.go
+++ b/v3/cmd/wails3/main.go
@@ -57,6 +57,7 @@ func main() {
update := app.NewSubCommand("update", "Update tools")
update.NewSubCommandFunction("build-assets", "Updates the build assets using the given config file", commands.UpdateBuildAssets)
+ update.NewSubCommandFunction("cli", "Updates the Wails CLI", commands.UpdateCLI)
bindgen := generate.NewSubCommand("bindings", "Generate bindings + models")
var bindgenFlags flags.GenerateBindingsOptions
diff --git a/v3/internal/commands/appimage.go b/v3/internal/commands/appimage.go
index b2a148651..7c43ba856 100644
--- a/v3/internal/commands/appimage.go
+++ b/v3/internal/commands/appimage.go
@@ -4,6 +4,7 @@ import (
_ "embed"
"errors"
"fmt"
+ "github.com/wailsapp/wails/v3/internal/term"
"os"
"path/filepath"
"runtime"
@@ -19,7 +20,7 @@ var gtkPlugin []byte
func log(p *pterm.ProgressbarPrinter, message string) {
p.UpdateTitle(message)
- pterm.Info.Println(message)
+ term.Infof(message)
p.Increment()
}
@@ -61,7 +62,7 @@ func GenerateAppImage(options *GenerateAppImageOptions) error {
return err
}
- pterm.Println(pterm.LightYellow("AppImage Generator v1.0.0"))
+ term.Header("AppImage Generator")
return generateAppImage(options)
}
diff --git a/v3/internal/commands/bindings.go b/v3/internal/commands/bindings.go
index 94833f881..89ce70376 100644
--- a/v3/internal/commands/bindings.go
+++ b/v3/internal/commands/bindings.go
@@ -3,28 +3,29 @@ package commands
import (
"errors"
"fmt"
- "os"
"path/filepath"
"github.com/pterm/pterm"
- "golang.org/x/term"
"github.com/wailsapp/wails/v3/internal/flags"
"github.com/wailsapp/wails/v3/internal/generator"
"github.com/wailsapp/wails/v3/internal/generator/config"
+ "github.com/wailsapp/wails/v3/internal/term"
)
func GenerateBindings(options *flags.GenerateBindingsOptions, patterns []string) error {
DisableFooter = true
if options.Silent {
- pterm.DisableOutput()
- defer pterm.EnableOutput()
+ term.DisableOutput()
+ defer term.EnableOutput()
} else if options.Verbose {
pterm.EnableDebugMessages()
defer pterm.DisableDebugMessages()
}
+ term.Header("Generate Bindings")
+
if len(patterns) == 0 {
// No input pattern, load package from current directory.
patterns = []string{"."}
@@ -44,7 +45,7 @@ func GenerateBindings(options *flags.GenerateBindingsOptions, patterns []string)
// Start a spinner for progress messages.
var spinner *pterm.SpinnerPrinter
- if term.IsTerminal(int(os.Stdout.Fd())) && (os.Getenv("CI") != "true") {
+ if term.IsTerminal() {
spinner, _ = pterm.DefaultSpinner.Start("Initialising...")
}
@@ -68,11 +69,11 @@ func GenerateBindings(options *flags.GenerateBindingsOptions, patterns []string)
if spinner != nil {
spinner.Info(resultMessage)
} else {
- pterm.Info.Println(resultMessage)
+ term.Infofln(resultMessage)
}
// Report output directory.
- pterm.Info.Printfln("Output directory: %s", absPath)
+ term.Infofln("Output directory: %s", absPath)
// Process generator error.
if err != nil {
@@ -80,14 +81,14 @@ func GenerateBindings(options *flags.GenerateBindingsOptions, patterns []string)
switch {
case errors.Is(err, generator.ErrNoPackages):
// Convert to warning message.
- pterm.Warning.Println(err)
+ term.Warning(err)
case errors.As(err, &report):
if report.HasErrors() {
// Report error count.
return err
} else if report.HasWarnings() {
// Report warning count.
- pterm.Warning.Println(report)
+ term.Warning(report)
}
default:
// Report error.
diff --git a/v3/internal/commands/generate_webview2.go b/v3/internal/commands/generate_webview2.go
index affbb4519..146f72bff 100644
--- a/v3/internal/commands/generate_webview2.go
+++ b/v3/internal/commands/generate_webview2.go
@@ -3,10 +3,9 @@ package commands
import (
_ "embed"
"fmt"
+ "github.com/wailsapp/wails/v3/internal/term"
"os"
"path/filepath"
-
- "github.com/pterm/pterm"
)
//go:embed webview2/MicrosoftEdgeWebview2Setup.exe
@@ -35,6 +34,6 @@ func GenerateWebView2Bootstrapper(options *GenerateWebView2Options) error {
return fmt.Errorf("failed to write WebView2 bootstrapper: %w", err)
}
- pterm.Success.Printf("Generated WebView2 bootstrapper at: %s\n", targetPath)
+ term.Success("Generated WebView2 bootstrapper at: " + targetPath + "\n")
return nil
}
diff --git a/v3/internal/commands/init.go b/v3/internal/commands/init.go
index c9aa42e4b..8a346f888 100644
--- a/v3/internal/commands/init.go
+++ b/v3/internal/commands/init.go
@@ -3,6 +3,7 @@ package commands
import (
"fmt"
"github.com/go-git/go-git/v5/config"
+ "github.com/wailsapp/wails/v3/internal/term"
"os"
"path/filepath"
"regexp"
@@ -103,12 +104,14 @@ func initGitRepository(projectDir string, gitURL string) error {
func Init(options *flags.Init) error {
if options.List {
+ term.Header("Available templates")
return printTemplates()
}
if options.Quiet {
- pterm.DisableOutput()
+ term.DisableOutput()
}
+ term.Header("Init project")
// Check if the template is a typescript template
isTypescript := false
@@ -158,7 +161,7 @@ func Init(options *flags.Init) error {
return err
}
if !options.Quiet {
- pterm.Info.Printf("Initialized git repository with remote: %s\n", options.Git)
+ term.Infof("Initialized git repository with remote: %s\n", options.Git)
}
}
return nil
@@ -167,8 +170,7 @@ func Init(options *flags.Init) error {
func printTemplates() error {
defaultTemplates := templates.GetDefaultTemplates()
- pterm.DefaultSection.Println("Available templates")
-
+ pterm.Println()
table := pterm.TableData{{"Name", "Description"}}
for _, template := range defaultTemplates {
table = append(table, []string{template.Name, template.Description})
diff --git a/v3/internal/commands/releasenotes.go b/v3/internal/commands/releasenotes.go
index 6dbb5a6cf..a90c7cb1b 100644
--- a/v3/internal/commands/releasenotes.go
+++ b/v3/internal/commands/releasenotes.go
@@ -1,8 +1,8 @@
package commands
import (
- "github.com/pterm/pterm"
"github.com/wailsapp/wails/v3/internal/github"
+ "github.com/wailsapp/wails/v3/internal/term"
"github.com/wailsapp/wails/v3/internal/version"
)
@@ -13,15 +13,22 @@ type ReleaseNotesOptions struct {
func ReleaseNotes(options *ReleaseNotesOptions) error {
if options.NoColour {
- pterm.DisableColor()
+ term.DisableColor()
}
- currentVersion := version.VersionString
+ term.Header("Release Notes")
+
+ if version.IsDev() {
+ term.Println("Release notes are not available for development builds")
+ return nil
+ }
+
+ currentVersion := version.String()
if options.Version != "" {
currentVersion = options.Version
}
releaseNotes := github.GetReleaseNotes(currentVersion, options.NoColour)
- pterm.Println(releaseNotes)
+ term.Println(releaseNotes)
return nil
}
diff --git a/v3/internal/commands/service.go b/v3/internal/commands/service.go
index 8babf87f3..8f5a6bd0a 100644
--- a/v3/internal/commands/service.go
+++ b/v3/internal/commands/service.go
@@ -3,9 +3,8 @@ package commands
import (
"github.com/wailsapp/wails/v3/internal/flags"
"github.com/wailsapp/wails/v3/internal/service"
+ "github.com/wailsapp/wails/v3/internal/term"
"strings"
-
- "github.com/pterm/pterm"
)
func toCamelCasePlugin(s string) string {
@@ -34,7 +33,7 @@ func toCamelCasePlugin(s string) string {
func ServiceInit(options *flags.ServiceInit) error {
if options.Quiet {
- pterm.DisableOutput()
+ term.DisableOutput()
}
if options.PackageName == "" {
diff --git a/v3/internal/commands/task.go b/v3/internal/commands/task.go
index 5b1743134..9672b2f27 100644
--- a/v3/internal/commands/task.go
+++ b/v3/internal/commands/task.go
@@ -3,13 +3,12 @@ package commands
import (
"context"
"fmt"
+ "github.com/wailsapp/wails/v3/internal/term"
"os"
"path/filepath"
"strings"
"time"
- "github.com/pterm/pterm"
-
"github.com/wailsapp/task/v3"
"github.com/wailsapp/task/v3/taskfile/ast"
)
@@ -18,7 +17,7 @@ import (
var BuildSettings = map[string]string{}
func fatal(message string) {
- pterm.Error.Println(message)
+ term.Error(message)
os.Exit(1)
}
diff --git a/v3/internal/commands/task_wrapper.go b/v3/internal/commands/task_wrapper.go
index 896be3550..c4e55855e 100644
--- a/v3/internal/commands/task_wrapper.go
+++ b/v3/internal/commands/task_wrapper.go
@@ -1,9 +1,9 @@
package commands
import (
+ "github.com/wailsapp/wails/v3/internal/term"
"os"
- "github.com/pterm/pterm"
"github.com/wailsapp/wails/v3/internal/flags"
)
@@ -16,7 +16,7 @@ func Package(_ *flags.Package) error {
}
func wrapTask(command string) error {
- pterm.Warning.Printf("`wails3 %s` is an alias for `wails3 task %s`. Use `wails task` for better control and more options.\n", command, command)
+ term.Warningf("`wails3 %s` is an alias for `wails3 task %s`. Use `wails task` for better control and more options.\n", command, command)
os.Args = []string{"wails3", "task", command}
return RunTask(&RunTaskOptions{}, []string{})
}
diff --git a/v3/internal/commands/update_cli.go b/v3/internal/commands/update_cli.go
new file mode 100644
index 000000000..a910622b4
--- /dev/null
+++ b/v3/internal/commands/update_cli.go
@@ -0,0 +1,177 @@
+package commands
+
+import (
+ "fmt"
+ "github.com/pterm/pterm"
+ "github.com/wailsapp/wails/v3/internal/debug"
+ "github.com/wailsapp/wails/v3/internal/github"
+ "github.com/wailsapp/wails/v3/internal/term"
+ "github.com/wailsapp/wails/v3/internal/version"
+ "os"
+ "os/exec"
+ "path/filepath"
+)
+
+type UpdateCLIOptions struct {
+ NoColour bool `name:"n" description:"Disable colour output"`
+ PreRelease bool `name:"pre" description:"Update to the latest pre-release (eg beta)"`
+ Version string `name:"version" description:"Update to a specific version (eg v3.0.0)"`
+ Latest bool `name:"latest" description:"Install the latest stable release"`
+}
+
+func UpdateCLI(options *UpdateCLIOptions) error {
+ if options.NoColour {
+ term.DisableColor()
+ }
+
+ term.Header("Update CLI")
+
+ // Check if this CLI has been installed from vcs
+ if debug.LocalModulePath != "" && !options.Latest {
+ v3Path := filepath.ToSlash(debug.LocalModulePath + "/v3")
+ term.Println("This Wails CLI has been installed from source. To update to the latest stable release, run the following commands in the `" + v3Path + "` directory:")
+ term.Println(" - git pull")
+ term.Println(" - go install")
+ term.Println("")
+ term.Println("If you want to install the latest release, please run `wails update cli -latest`")
+ return nil
+ }
+
+ if options.Latest {
+ latestVersion, err := github.GetLatestStableRelease()
+ if err != nil {
+ return err
+ }
+ return updateToVersion(latestVersion, true, version.String())
+ }
+
+ term.Println("Checking for updates...")
+
+ var desiredVersion *github.SemanticVersion
+ var err error
+ var valid bool
+
+ if len(options.Version) > 0 {
+ // Check if this is a valid version
+ valid, err = github.IsValidTag(options.Version)
+ if err == nil {
+ if !valid {
+ err = fmt.Errorf("version '%s' is invalid", options.Version)
+ } else {
+ desiredVersion, err = github.NewSemanticVersion(options.Version)
+ }
+ }
+ } else {
+ if options.PreRelease {
+ desiredVersion, err = github.GetLatestPreRelease()
+ } else {
+ desiredVersion, err = github.GetLatestStableRelease()
+ if err != nil {
+ pterm.Println("")
+ pterm.Println("No stable release found for this major version. To update to the latest pre-release (eg beta), run:")
+ pterm.Println(" wails update -pre")
+ return nil
+ }
+ }
+ }
+ if err != nil {
+ return err
+ }
+ pterm.Println()
+
+ currentVersion := version.String()
+ pterm.Printf(" Current Version : %s\n", currentVersion)
+
+ if len(options.Version) > 0 {
+ fmt.Printf(" Desired Version : v%s\n", desiredVersion)
+ } else {
+ if options.PreRelease {
+ fmt.Printf(" Latest Prerelease : v%s\n", desiredVersion)
+ } else {
+ fmt.Printf(" Latest Release : v%s\n", desiredVersion)
+ }
+ }
+
+ return updateToVersion(desiredVersion, len(options.Version) > 0, currentVersion)
+}
+
+func updateToVersion(targetVersion *github.SemanticVersion, force bool, currentVersion string) error {
+ targetVersionString := "v" + targetVersion.String()
+
+ if targetVersionString == currentVersion {
+ pterm.Println("\nLooks like you're up to date!")
+ return nil
+ }
+
+ var desiredVersion string
+
+ if !force {
+ compareVersion := currentVersion
+
+ currentVersion, err := github.NewSemanticVersion(compareVersion)
+ if err != nil {
+ return err
+ }
+
+ var success bool
+
+ // Release -> Pre-Release = Massage current version to prerelease format
+ if targetVersion.IsPreRelease() && currentVersion.IsRelease() {
+ testVersion, err := github.NewSemanticVersion(compareVersion + "-0")
+ if err != nil {
+ return err
+ }
+ success, _ = targetVersion.IsGreaterThan(testVersion)
+ }
+ // Pre-Release -> Release = Massage target version to prerelease format
+ if targetVersion.IsRelease() && currentVersion.IsPreRelease() {
+ mainversion := currentVersion.MainVersion()
+ targetVersion, err = github.NewSemanticVersion(targetVersion.String())
+ if err != nil {
+ return err
+ }
+ success, _ = targetVersion.IsGreaterThanOrEqual(mainversion)
+ }
+
+ // Release -> Release = Standard check
+ if (targetVersion.IsRelease() && currentVersion.IsRelease()) ||
+ (targetVersion.IsPreRelease() && currentVersion.IsPreRelease()) {
+ success, _ = targetVersion.IsGreaterThan(currentVersion)
+ }
+
+ // Compare
+ if !success {
+ pterm.Println("Error: The requested version is lower than the current version.")
+ pterm.Printf("If this is what you really want to do, use `wails update -version %s`\n", targetVersionString)
+ return nil
+ }
+
+ desiredVersion = "v" + targetVersion.String()
+ } else {
+ desiredVersion = "v" + targetVersion.String()
+ }
+
+ pterm.Println()
+ pterm.Print("Installing Wails CLI " + desiredVersion + "...")
+
+ // Run command in non module directory
+ homeDir, err := os.UserHomeDir()
+ if err != nil {
+ return fmt.Errorf("cannot find home directory: %w", err)
+ }
+
+ cmd := exec.Command("go", "install", "github.com/wailsapp/wails/v3/cmd/wails@"+desiredVersion)
+ cmd.Dir = homeDir
+ sout, serr := cmd.CombinedOutput()
+ if err := cmd.Run(); err != nil {
+ pterm.Println("Failed.")
+ pterm.Error.Println(string(sout) + "\n" + serr.Error())
+ return err
+ }
+ pterm.Println("Done.")
+ pterm.Println("\nMake sure you update your project go.mod file to use " + desiredVersion + ":")
+ pterm.Println(" require github.com/wailsapp/wails/v3 " + desiredVersion)
+ pterm.Println("\nTo view the release notes, please run `wails3 releasenotes`")
+
+ return nil
+}
diff --git a/v3/internal/commands/version.go b/v3/internal/commands/version.go
index 6ff58879b..65bf66113 100644
--- a/v3/internal/commands/version.go
+++ b/v3/internal/commands/version.go
@@ -9,6 +9,6 @@ type VersionOptions struct{}
func Version(_ *VersionOptions) error {
DisableFooter = true
- println(version.VersionString)
+ println(version.String())
return nil
}
diff --git a/v3/internal/doctor/doctor.go b/v3/internal/doctor/doctor.go
index dcf4dd7ac..234c33ec5 100644
--- a/v3/internal/doctor/doctor.go
+++ b/v3/internal/doctor/doctor.go
@@ -3,6 +3,7 @@ package doctor
import (
"bytes"
"fmt"
+ "github.com/wailsapp/wails/v3/internal/term"
"os/exec"
"path/filepath"
"regexp"
@@ -30,13 +31,7 @@ func Run() (err error) {
}
_ = get
- pterm.DefaultSection = *pterm.DefaultSection.
- WithBottomPadding(0).
- WithStyle(pterm.NewStyle(pterm.FgBlue, pterm.Bold))
-
- pterm.Println() // Spacer
- pterm.DefaultHeader.WithBackgroundStyle(pterm.NewStyle(pterm.BgLightBlue)).WithMargin(10).Println("Wails Doctor")
- pterm.Println() // Spacer
+ term.Header("Wails Doctor")
spinner, _ := pterm.DefaultSpinner.WithRemoveWhenDone().Start("Scanning system - Please wait (this may take a long time)...")
@@ -68,7 +63,7 @@ func Run() (err error) {
// Get system info
info, err := operatingsystem.Info()
if err != nil {
- pterm.Error.Println("Failed to get system information")
+ term.Error("Failed to get system information")
return err
}
@@ -77,7 +72,7 @@ func Run() (err error) {
return dep.Path == "github.com/wailsapp/wails/v3"
})
- wailsVersion := strings.TrimSpace(version.VersionString)
+ wailsVersion := strings.TrimSpace(version.String())
if wailsPackage != nil && wailsPackage.Replace != nil {
wailsVersion = "(local) => " + filepath.ToSlash(wailsPackage.Replace.Path)
// Get the latest commit hash
@@ -99,7 +94,7 @@ func Run() (err error) {
/** Output **/
- pterm.DefaultSection.Println("System")
+ term.Section("System")
systemTabledata := pterm.TableData{
{pterm.Sprint("Name"), info.Name},
@@ -195,7 +190,7 @@ func Run() (err error) {
// Build Environment
- pterm.DefaultSection.Println("Build Environment")
+ term.Section("Build Environment")
tableData := pterm.TableData{
{"Wails CLI", wailsVersion},
@@ -228,7 +223,7 @@ func Run() (err error) {
}
// Dependencies
- pterm.DefaultSection.Println("Dependencies")
+ term.Section("Dependencies")
dependenciesBox := pterm.DefaultBox.WithTitleBottomCenter().WithTitle(pterm.Gray("*") + " - Optional Dependency")
dependencyTableData := pterm.TableData{}
if len(dependencies) == 0 {
@@ -248,11 +243,11 @@ func Run() (err error) {
dependenciesBox.Println(dependenciesTableString)
}
- pterm.DefaultSection.Println("Diagnosis")
+ term.Section("Diagnosis")
if !ok {
- pterm.Warning.Println("There are some items above that need addressing!")
+ term.Warning("There are some items above that need addressing!")
} else {
- pterm.Success.Println("Your system is ready for Wails development!")
+ term.Success("Your system is ready for Wails development!")
}
return nil
diff --git a/v3/internal/templates/templates.go b/v3/internal/templates/templates.go
index 62206d2e3..5d9aa49d2 100644
--- a/v3/internal/templates/templates.go
+++ b/v3/internal/templates/templates.go
@@ -260,7 +260,7 @@ func Install(options *flags.Init) error {
Init: options,
LocalModulePath: localModulePath,
UseTypescript: UseTypescript,
- WailsVersion: version.VersionString,
+ WailsVersion: version.String(),
}
defer func() {
diff --git a/v3/internal/term/term.go b/v3/internal/term/term.go
new file mode 100644
index 000000000..f973bc667
--- /dev/null
+++ b/v3/internal/term/term.go
@@ -0,0 +1,105 @@
+package term
+
+import (
+ "fmt"
+ "github.com/pterm/pterm"
+ "github.com/wailsapp/wails/v3/internal/generator/config"
+ "github.com/wailsapp/wails/v3/internal/version"
+ "golang.org/x/term"
+ "os"
+)
+
+func Header(header string) {
+ // Print Wails with the current version in white on red background with the header in white with a green background
+ pterm.BgLightRed.Print(pterm.LightWhite(" Wails (" + version.String() + ") "))
+ pterm.BgLightGreen.Println(pterm.LightWhite(" " + header + " "))
+}
+
+func Infof(format string, args ...interface{}) {
+ pterm.Info.Printf(format, args...)
+}
+func Infofln(format string, args ...interface{}) {
+ pterm.Info.Printfln(format, args...)
+}
+
+func IsTerminal() bool {
+ return term.IsTerminal(int(os.Stdout.Fd())) && (os.Getenv("CI") != "true")
+}
+
+type Spinner struct {
+ spinner *pterm.SpinnerPrinter
+}
+
+func (s *Spinner) Logger() config.Logger {
+ if s == nil {
+ return nil
+ }
+ return config.DefaultPtermLogger(s.spinner)
+}
+
+func StartSpinner(text string) *Spinner {
+ if !IsTerminal() {
+ return nil
+ }
+ spin, err := pterm.DefaultSpinner.Start(text)
+ if err != nil {
+ return nil
+ }
+ return &Spinner{
+ spinner: spin,
+ }
+}
+
+func StopSpinner(s *Spinner) {
+ if s == nil {
+ return
+ }
+ _ = s.spinner.Stop()
+}
+
+func output(input any, printer pterm.PrefixPrinter, args ...any) {
+ switch v := input.(type) {
+ case string:
+ printer.Println(fmt.Sprintf(input.(string), args...))
+ case error:
+ printer.Println(v.Error())
+ default:
+ printer.Printfln("%v", v)
+ }
+}
+
+func Warning(input any) {
+ output(input, pterm.Warning)
+}
+
+func Warningf(input any, args ...any) {
+ output(input, pterm.Warning, args)
+}
+
+func Error(input any) {
+ output(input, pterm.Error)
+}
+
+func Success(input any) {
+ output(input, pterm.Success)
+}
+
+func Section(s string) {
+ style := pterm.NewStyle(pterm.BgDefault, pterm.FgLightBlue, pterm.Bold)
+ style.Println("\n# " + s + " \n")
+}
+
+func DisableColor() {
+ pterm.DisableColor()
+}
+func EnableOutput() {
+ pterm.EnableOutput()
+}
+
+func DisableOutput() {
+ pterm.DisableOutput()
+}
+
+func Println(s string) {
+ pterm.Println(s)
+}
diff --git a/v3/internal/version/version.go b/v3/internal/version/version.go
index d8b4b0c2b..048fe1378 100644
--- a/v3/internal/version/version.go
+++ b/v3/internal/version/version.go
@@ -2,7 +2,23 @@ package version
import (
_ "embed"
+ "github.com/wailsapp/wails/v3/internal/debug"
)
//go:embed version.txt
-var VersionString string
+var versionString string
+
+func String() string {
+ if !IsDev() {
+ return versionString
+ }
+ return "v3 dev"
+}
+
+func LatestStable() string {
+ return versionString
+}
+
+func IsDev() bool {
+ return debug.LocalModulePath != ""
+}