feat(setup): add global defaults, light/dark mode, and UI improvements

- Add global defaults config stored in ~/.config/wails/defaults.yaml
- Add light/dark mode toggle with theme persistence
- Add PKGBUILD support to Linux build formats display
- Add macOS signing clarification (public identifiers vs Keychain storage)
- Fix spinner animation using CSS animate-spin
- Add signing defaults for macOS and Windows code signing
- Compact defaults page layout with 2-column design
- Add Wails logo with proper light/dark theme variants

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Lea Anthony 2025-12-07 17:40:53 +11:00
commit 431869bf84
19 changed files with 1351 additions and 166 deletions

View file

@ -10,6 +10,7 @@ import (
"strings"
"github.com/go-git/go-git/v5/config"
"github.com/wailsapp/wails/v3/internal/defaults"
"github.com/wailsapp/wails/v3/internal/term"
"github.com/go-git/go-git/v5"
@ -98,6 +99,39 @@ func initGitRepository(projectDir string, gitURL string) error {
return nil
}
// applyGlobalDefaults applies global defaults to init options if they are using default values
func applyGlobalDefaults(options *flags.Init, globalDefaults defaults.GlobalDefaults) {
// Apply template default if using the built-in default
if options.TemplateName == "vanilla" && globalDefaults.Project.DefaultTemplate != "" {
options.TemplateName = globalDefaults.Project.DefaultTemplate
}
// Apply company default if using the built-in default
if options.ProductCompany == "My Company" && globalDefaults.Author.Company != "" {
options.ProductCompany = globalDefaults.Author.Company
}
// Apply copyright from global defaults if using the built-in default
if options.ProductCopyright == "\u00a9 now, My Company" {
options.ProductCopyright = globalDefaults.GenerateCopyright()
}
// Apply product identifier from global defaults if not explicitly set
if options.ProductIdentifier == "" && globalDefaults.Project.ProductIdentifierPrefix != "" {
options.ProductIdentifier = globalDefaults.GenerateProductIdentifier(options.ProjectName)
}
// Apply description from global defaults if using the built-in default
if options.ProductDescription == "My Product Description" && globalDefaults.Project.DescriptionTemplate != "" {
options.ProductDescription = globalDefaults.GenerateDescription(options.ProjectName)
}
// Apply version from global defaults if using the built-in default
if options.ProductVersion == "0.1.0" && globalDefaults.Project.DefaultVersion != "" {
options.ProductVersion = globalDefaults.GetDefaultVersion()
}
}
func Init(options *flags.Init) error {
if options.List {
term.Header("Available templates")
@ -121,6 +155,15 @@ func Init(options *flags.Init) error {
options.ProjectName = sanitizeFileName(options.ProjectName)
// Load and apply global defaults
globalDefaults, err := defaults.Load()
if err != nil {
// Log warning but continue - global defaults are optional
term.Warningf("Could not load global defaults: %v\n", err)
} else {
applyGlobalDefaults(options, globalDefaults)
}
if options.ModulePath == "" {
if options.Git == "" {
options.ModulePath = "changeme"
@ -129,7 +172,7 @@ func Init(options *flags.Init) error {
}
}
err := templates.Install(options)
err = templates.Install(options)
if err != nil {
return err
}

View file

@ -0,0 +1,219 @@
// Package defaults provides functionality for loading and saving global default settings
// for Wails projects. Settings are stored in ~/.config/wails/defaults.yaml
package defaults
import (
"os"
"path/filepath"
"time"
"gopkg.in/yaml.v3"
)
// GlobalDefaults represents the user's default project settings
// These are stored in ~/.config/wails/defaults.yaml and used when creating new projects
type GlobalDefaults struct {
// Author information
Author AuthorDefaults `json:"author" yaml:"author"`
// Default project settings
Project ProjectDefaults `json:"project" yaml:"project"`
}
// AuthorDefaults contains the author's information
type AuthorDefaults struct {
Name string `json:"name" yaml:"name"`
Company string `json:"company" yaml:"company"`
}
// ProjectDefaults contains default project settings
type ProjectDefaults struct {
// ProductIdentifierPrefix is the prefix for app identifiers (e.g., "com.mycompany")
ProductIdentifierPrefix string `json:"productIdentifierPrefix" yaml:"productIdentifierPrefix"`
// DefaultTemplate is the default frontend template to use
DefaultTemplate string `json:"defaultTemplate" yaml:"defaultTemplate"`
// Copyright template - can include {year} and {company} placeholders
CopyrightTemplate string `json:"copyrightTemplate" yaml:"copyrightTemplate"`
// Description template for new projects - can include {name} placeholder
DescriptionTemplate string `json:"descriptionTemplate" yaml:"descriptionTemplate"`
// Default product version for new projects
DefaultVersion string `json:"defaultVersion" yaml:"defaultVersion"`
}
// Default returns sensible defaults for first-time users
func Default() GlobalDefaults {
return GlobalDefaults{
Author: AuthorDefaults{
Name: "",
Company: "",
},
Project: ProjectDefaults{
ProductIdentifierPrefix: "com.example",
DefaultTemplate: "vanilla",
CopyrightTemplate: "© {year}, {company}",
DescriptionTemplate: "A {name} application",
DefaultVersion: "0.1.0",
},
}
}
// GetConfigDir returns the path to the Wails config directory
func GetConfigDir() (string, error) {
// Use XDG_CONFIG_HOME if set, otherwise use ~/.config
configHome := os.Getenv("XDG_CONFIG_HOME")
if configHome == "" {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", err
}
configHome = filepath.Join(homeDir, ".config")
}
return filepath.Join(configHome, "wails"), nil
}
// GetDefaultsPath returns the path to the defaults.yaml file
func GetDefaultsPath() (string, error) {
configDir, err := GetConfigDir()
if err != nil {
return "", err
}
return filepath.Join(configDir, "defaults.yaml"), nil
}
// Load loads the global defaults from the config file
// Returns default values if the file doesn't exist
func Load() (GlobalDefaults, error) {
defaults := Default()
path, err := GetDefaultsPath()
if err != nil {
return defaults, err
}
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return defaults, nil
}
return defaults, err
}
if err := yaml.Unmarshal(data, &defaults); err != nil {
return Default(), err
}
return defaults, nil
}
// Save saves the global defaults to the config file
func Save(defaults GlobalDefaults) error {
path, err := GetDefaultsPath()
if err != nil {
return err
}
// Ensure the config directory exists
configDir := filepath.Dir(path)
if err := os.MkdirAll(configDir, 0755); err != nil {
return err
}
data, err := yaml.Marshal(&defaults)
if err != nil {
return err
}
return os.WriteFile(path, data, 0644)
}
// GenerateCopyright generates a copyright string from the template
func (d *GlobalDefaults) GenerateCopyright() string {
template := d.Project.CopyrightTemplate
if template == "" {
template = "© {year}, {company}"
}
year := time.Now().Format("2006")
company := d.Author.Company
if company == "" {
company = "My Company"
}
result := template
result = replaceAll(result, "{year}", year)
result = replaceAll(result, "{company}", company)
return result
}
// GenerateProductIdentifier generates a product identifier from prefix and project name
func (d *GlobalDefaults) GenerateProductIdentifier(projectName string) string {
prefix := d.Project.ProductIdentifierPrefix
if prefix == "" {
prefix = "com.example"
}
return prefix + "." + sanitizeIdentifier(projectName)
}
// GenerateDescription generates a description string from the template
func (d *GlobalDefaults) GenerateDescription(projectName string) string {
template := d.Project.DescriptionTemplate
if template == "" {
template = "A {name} application"
}
return replaceAll(template, "{name}", projectName)
}
// GetDefaultVersion returns the default version or the fallback
func (d *GlobalDefaults) GetDefaultVersion() string {
if d.Project.DefaultVersion != "" {
return d.Project.DefaultVersion
}
return "0.1.0"
}
// replaceAll replaces all occurrences of old with new in s
func replaceAll(s, old, new string) string {
result := s
for {
newResult := replaceOnce(result, old, new)
if newResult == result {
break
}
result = newResult
}
return result
}
func replaceOnce(s, old, new string) string {
for i := 0; i <= len(s)-len(old); i++ {
if s[i:i+len(old)] == old {
return s[:i] + new + s[i+len(old):]
}
}
return s
}
// sanitizeIdentifier creates a valid identifier from a project name
func sanitizeIdentifier(name string) string {
var result []byte
for i := 0; i < len(name); i++ {
c := name[i]
if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') {
result = append(result, c)
}
}
if len(result) == 0 {
return "app"
}
// Lowercase the result
for i := range result {
if result[i] >= 'A' && result[i] <= 'Z' {
result[i] = result[i] + 32
}
}
return string(result)
}

View file

@ -0,0 +1,30 @@
package setupwizard
import (
"github.com/wailsapp/wails/v3/internal/defaults"
)
// Re-export types for convenience
type GlobalDefaults = defaults.GlobalDefaults
type AuthorDefaults = defaults.AuthorDefaults
type ProjectDefaults = defaults.ProjectDefaults
// DefaultGlobalDefaults returns sensible defaults for first-time users
func DefaultGlobalDefaults() GlobalDefaults {
return defaults.Default()
}
// GetDefaultsPath returns the path to the defaults.yaml file
func GetDefaultsPath() (string, error) {
return defaults.GetDefaultsPath()
}
// LoadGlobalDefaults loads the global defaults from the config file
func LoadGlobalDefaults() (GlobalDefaults, error) {
return defaults.Load()
}
// SaveGlobalDefaults saves the global defaults to the config file
func SaveGlobalDefaults(d GlobalDefaults) error {
return defaults.Save(d)
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 413 327" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,0,223.853)">
<path d="M87.462,32.114L98.283,32.114L97.244,55.145L104.388,32.114L113.131,32.114L113.025,55.145L119.235,32.114L130.057,32.114L116.356,71.033L106.36,71.033L106.467,46.695L98.87,71.033L88.901,71.033L87.462,32.114Z" style="fill:rgb(30,30,30);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,0,-170.731)">
<path d="M195.198,248.963L226.365,240.838L201.513,256.893L195.198,248.963Z" style="fill:white;"/>
</g>
<g transform="matrix(1,0,0,1,0,223.853)">
<path d="M170.125,57.438L174.47,57.438L174.603,43.017L170.125,57.438ZM154.985,71.033L169.619,32.115L182.44,32.115L185.185,71.033L174.203,71.033L174.257,65.301L167.832,65.301L166.126,71.033L154.985,71.033Z" style="fill:rgb(30,30,30);fill-rule:nonzero;"/>
</g>
<g transform="matrix(0.15626,-0.987716,-0.987716,-0.15626,242.8,508.084)">
<path d="M206.842,56.945L246.244,56.945L247.943,46.203L208.537,46.23L206.842,56.945Z" style="fill:rgb(30,30,30);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,0,223.853)">
<path d="M270.363,71.033L276.547,32.115L287.449,32.115L282.811,61.383L290.781,61.383L289.262,71.033L270.363,71.033Z" style="fill:rgb(30,30,30);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,0,223.96)">
<path d="M324.467,58.956C325.676,59.97 326.946,60.752 328.28,61.303C329.612,61.854 330.891,62.129 332.117,62.129C333.201,62.129 334.063,61.831 334.703,61.236C335.342,60.641 335.663,59.828 335.663,58.797C335.663,57.909 335.396,57.024 334.863,56.145C334.33,55.265 333.299,54.097 331.771,52.64C329.922,50.845 328.652,49.263 327.959,47.895C327.266,46.527 326.92,45.016 326.92,43.364C326.92,39.65 328.097,36.699 330.452,34.514C332.806,32.328 336,31.235 340.034,31.235C341.669,31.235 343.223,31.417 344.699,31.782C346.173,32.146 347.675,32.71 349.204,33.474L347.657,42.964C346.484,42.147 345.343,41.525 344.233,41.098C343.121,40.671 342.06,40.458 341.047,40.458C340.141,40.458 339.425,40.693 338.901,41.163C338.377,41.635 338.114,42.279 338.114,43.097C338.114,44.199 339.119,45.745 341.127,47.735C341.375,47.984 341.571,48.179 341.714,48.321C343.739,50.312 345.072,52.017 345.712,53.44C346.352,54.861 346.671,56.504 346.671,58.371C346.671,62.529 345.387,65.808 342.819,68.206C340.252,70.605 336.737,71.805 332.277,71.805C330.375,71.805 328.577,71.579 326.879,71.126C325.182,70.671 323.756,70.045 322.601,69.246L324.467,58.956Z" style="fill:rgb(30,30,30);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-124.95,3.42849,3.42849,124.95,110.553,125.737)">
<path d="M0.883,-0.081L0.121,0.081L0.256,-0.063L0.883,-0.081Z" style="fill:url(#_Linear1);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-79.8325,-12.0502,-12.0502,79.8325,321.143,141.025)">
<path d="M0.878,-0.285L-0.073,0.71L-1.186,0.542L0.015,0.207L-0.846,0.077L0.355,-0.258L-0.505,-0.388L0.649,-0.71L0.878,-0.285Z" style="fill:url(#_Linear2);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-85.8631,-121.806,-121.806,85.8631,249.969,214.353)">
<path d="M0.44,-0.04L0.44,-0.04L0.44,-0.04L0.265,-0.056L0.177,0.437L-0.311,-0.255L0.262,-0.437L0.568,-0.437L0.44,-0.04Z" style="fill:url(#_Linear3);fill-rule:nonzero;"/>
</g>
<g transform="matrix(46.2689,44.1068,44.1068,-46.2689,193.973,135.309)">
<path d="M0.5,0L0.5,-0L0.5,0L0.5,0Z" style="fill:url(#_Linear4);fill-rule:nonzero;"/>
</g>
<g transform="matrix(178.598,224.167,224.167,-178.598,85.1346,-112.899)">
<path d="M0.622,-0.115L0.761,-0.115L0.806,-0.013L0.826,0.182L0.622,-0.115Z" style="fill:url(#_Linear5);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-277.147,-73.0588,-73.0588,277.147,436.785,70.5207)">
<path d="M0.467,0.005L0.49,0.062L0.271,-0.062L0.467,0.005Z" style="fill:url(#_Linear6);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-372.117,-40.4813,-40.4813,372.117,275.916,93.8142)">
<path d="M0.2,0.001L0.219,-0.018L0.614,0.012L0.519,0.089L0.282,0.068L0.2,0.135L0.463,0.194L0.374,0.266L0.138,0.186L0.138,0.186L0.138,0.186L0.047,0.033L-0.131,-0.266L0.2,0.001Z" style="fill:url(#_Linear7);fill-rule:nonzero;"/>
</g>
<g transform="matrix(138.807,132.321,132.321,-138.807,115.085,60.1115)">
<path d="M0.735,-0L0.735,-0L0.735,0L0.735,-0Z" style="fill:url(#_Linear8);fill-rule:nonzero;"/>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-5.1153e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-8.16759e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,5.55112e-17,5.55112e-17,-1,0,5.48218e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-3.25937e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear5" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-0.801909,-0.597446,-0.597446,0.801909,1.3495,0.447446)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear6" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-2.6378e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear7" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,1.38778e-17,1.38778e-17,-1,0,-6.47319e-07)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear8" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-3.82755e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 413 327" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,0,223.853)">
<path d="M87.462,32.114L98.283,32.114L97.244,55.145L104.388,32.114L113.131,32.114L113.025,55.145L119.235,32.114L130.057,32.114L116.356,71.033L106.36,71.033L106.467,46.695L98.87,71.033L88.901,71.033L87.462,32.114Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,0,-170.731)">
<path d="M195.198,248.963L226.365,240.838L201.513,256.893L195.198,248.963Z" style="fill:white;"/>
</g>
<g transform="matrix(1,0,0,1,0,223.853)">
<path d="M170.125,57.438L174.47,57.438L174.603,43.017L170.125,57.438ZM154.985,71.033L169.619,32.115L182.44,32.115L185.185,71.033L174.203,71.033L174.257,65.301L167.832,65.301L166.126,71.033L154.985,71.033Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(0.15626,-0.987716,-0.987716,-0.15626,242.8,508.084)">
<path d="M206.842,56.945L246.244,56.945L247.943,46.203L208.537,46.23L206.842,56.945Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,0,223.853)">
<path d="M270.363,71.033L276.547,32.115L287.449,32.115L282.811,61.383L290.781,61.383L289.262,71.033L270.363,71.033Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,0,223.96)">
<path d="M324.467,58.956C325.676,59.97 326.946,60.752 328.28,61.303C329.612,61.854 330.891,62.129 332.117,62.129C333.201,62.129 334.063,61.831 334.703,61.236C335.342,60.641 335.663,59.828 335.663,58.797C335.663,57.909 335.396,57.024 334.863,56.145C334.33,55.265 333.299,54.097 331.771,52.64C329.922,50.845 328.652,49.263 327.959,47.895C327.266,46.527 326.92,45.016 326.92,43.364C326.92,39.65 328.097,36.699 330.452,34.514C332.806,32.328 336,31.235 340.034,31.235C341.669,31.235 343.223,31.417 344.699,31.782C346.173,32.146 347.675,32.71 349.204,33.474L347.657,42.964C346.484,42.147 345.343,41.525 344.233,41.098C343.121,40.671 342.06,40.458 341.047,40.458C340.141,40.458 339.425,40.693 338.901,41.163C338.377,41.635 338.114,42.279 338.114,43.097C338.114,44.199 339.119,45.745 341.127,47.735C341.375,47.984 341.571,48.179 341.714,48.321C343.739,50.312 345.072,52.017 345.712,53.44C346.352,54.861 346.671,56.504 346.671,58.371C346.671,62.529 345.387,65.808 342.819,68.206C340.252,70.605 336.737,71.805 332.277,71.805C330.375,71.805 328.577,71.579 326.879,71.126C325.182,70.671 323.756,70.045 322.601,69.246L324.467,58.956Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(-124.95,3.42849,3.42849,124.95,110.553,125.737)">
<path d="M0.883,-0.081L0.121,0.081L0.256,-0.063L0.883,-0.081Z" style="fill:url(#_Linear1);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-79.8325,-12.0502,-12.0502,79.8325,321.143,141.025)">
<path d="M0.878,-0.285L-0.073,0.71L-1.186,0.542L0.015,0.207L-0.846,0.077L0.355,-0.258L-0.505,-0.388L0.649,-0.71L0.878,-0.285Z" style="fill:url(#_Linear2);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-85.8631,-121.806,-121.806,85.8631,249.969,214.353)">
<path d="M0.44,-0.04L0.44,-0.04L0.44,-0.04L0.265,-0.056L0.177,0.437L-0.311,-0.255L0.262,-0.437L0.568,-0.437L0.44,-0.04Z" style="fill:url(#_Linear3);fill-rule:nonzero;"/>
</g>
<g transform="matrix(46.2689,44.1068,44.1068,-46.2689,193.973,135.309)">
<path d="M0.5,0L0.5,-0L0.5,0L0.5,0Z" style="fill:url(#_Linear4);fill-rule:nonzero;"/>
</g>
<g transform="matrix(178.598,224.167,224.167,-178.598,85.1346,-112.899)">
<path d="M0.622,-0.115L0.761,-0.115L0.806,-0.013L0.826,0.182L0.622,-0.115Z" style="fill:url(#_Linear5);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-277.147,-73.0588,-73.0588,277.147,436.785,70.5207)">
<path d="M0.467,0.005L0.49,0.062L0.271,-0.062L0.467,0.005Z" style="fill:url(#_Linear6);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-372.117,-40.4813,-40.4813,372.117,275.916,93.8142)">
<path d="M0.2,0.001L0.219,-0.018L0.614,0.012L0.519,0.089L0.282,0.068L0.2,0.135L0.463,0.194L0.374,0.266L0.138,0.186L0.138,0.186L0.138,0.186L0.047,0.033L-0.131,-0.266L0.2,0.001Z" style="fill:url(#_Linear7);fill-rule:nonzero;"/>
</g>
<g transform="matrix(138.807,132.321,132.321,-138.807,115.085,60.1115)">
<path d="M0.735,-0L0.735,-0L0.735,0L0.735,-0Z" style="fill:url(#_Linear8);fill-rule:nonzero;"/>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-5.1153e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-8.16759e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,5.55112e-17,5.55112e-17,-1,0,5.48218e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-3.25937e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear5" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-0.801909,-0.597446,-0.597446,0.801909,1.3495,0.447446)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear6" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-2.6378e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear7" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,1.38778e-17,1.38778e-17,-1,0,-6.47319e-07)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear8" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-3.82755e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

View file

@ -7,8 +7,8 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<script type="module" crossorigin src="/assets/index-D7iWlEVX.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Nqr58SLv.css">
<script type="module" crossorigin src="/assets/index-C9VCVRfM.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CCNHCwJO.css">
</head>
<body>
<div id="root"></div>

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
import type { WizardState, DependencyStatus, DockerStatus, UserConfig, WailsConfig } from './types';
import type { WizardState, DependencyStatus, DockerStatus, UserConfig, WailsConfig, GlobalDefaults } from './types';
const API_BASE = '/api';
@ -22,6 +22,17 @@ export async function buildDockerImage(): Promise<{ status: string }> {
return response.json();
}
export interface DockerStartBackgroundResponse {
started: boolean;
reason?: string;
status: DockerStatus;
}
export async function startDockerBuildBackground(): Promise<DockerStartBackgroundResponse> {
const response = await fetch(`${API_BASE}/docker/start-background`, { method: 'POST' });
return response.json();
}
export async function detectConfig(): Promise<Partial<UserConfig>> {
const response = await fetch(`${API_BASE}/config/detect`);
return response.json();
@ -73,3 +84,17 @@ export async function installDependency(command: string): Promise<InstallResult>
});
return response.json();
}
export async function getDefaults(): Promise<GlobalDefaults> {
const response = await fetch(`${API_BASE}/defaults`);
return response.json();
}
export async function saveDefaults(defaults: GlobalDefaults): Promise<{ status: string; path: string }> {
const response = await fetch(`${API_BASE}/defaults`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(defaults),
});
return response.json();
}

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 413 327" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,0,223.853)">
<path d="M87.462,32.114L98.283,32.114L97.244,55.145L104.388,32.114L113.131,32.114L113.025,55.145L119.235,32.114L130.057,32.114L116.356,71.033L106.36,71.033L106.467,46.695L98.87,71.033L88.901,71.033L87.462,32.114Z" style="fill:rgb(30,30,30);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,0,-170.731)">
<path d="M195.198,248.963L226.365,240.838L201.513,256.893L195.198,248.963Z" style="fill:white;"/>
</g>
<g transform="matrix(1,0,0,1,0,223.853)">
<path d="M170.125,57.438L174.47,57.438L174.603,43.017L170.125,57.438ZM154.985,71.033L169.619,32.115L182.44,32.115L185.185,71.033L174.203,71.033L174.257,65.301L167.832,65.301L166.126,71.033L154.985,71.033Z" style="fill:rgb(30,30,30);fill-rule:nonzero;"/>
</g>
<g transform="matrix(0.15626,-0.987716,-0.987716,-0.15626,242.8,508.084)">
<path d="M206.842,56.945L246.244,56.945L247.943,46.203L208.537,46.23L206.842,56.945Z" style="fill:rgb(30,30,30);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,0,223.853)">
<path d="M270.363,71.033L276.547,32.115L287.449,32.115L282.811,61.383L290.781,61.383L289.262,71.033L270.363,71.033Z" style="fill:rgb(30,30,30);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,0,223.96)">
<path d="M324.467,58.956C325.676,59.97 326.946,60.752 328.28,61.303C329.612,61.854 330.891,62.129 332.117,62.129C333.201,62.129 334.063,61.831 334.703,61.236C335.342,60.641 335.663,59.828 335.663,58.797C335.663,57.909 335.396,57.024 334.863,56.145C334.33,55.265 333.299,54.097 331.771,52.64C329.922,50.845 328.652,49.263 327.959,47.895C327.266,46.527 326.92,45.016 326.92,43.364C326.92,39.65 328.097,36.699 330.452,34.514C332.806,32.328 336,31.235 340.034,31.235C341.669,31.235 343.223,31.417 344.699,31.782C346.173,32.146 347.675,32.71 349.204,33.474L347.657,42.964C346.484,42.147 345.343,41.525 344.233,41.098C343.121,40.671 342.06,40.458 341.047,40.458C340.141,40.458 339.425,40.693 338.901,41.163C338.377,41.635 338.114,42.279 338.114,43.097C338.114,44.199 339.119,45.745 341.127,47.735C341.375,47.984 341.571,48.179 341.714,48.321C343.739,50.312 345.072,52.017 345.712,53.44C346.352,54.861 346.671,56.504 346.671,58.371C346.671,62.529 345.387,65.808 342.819,68.206C340.252,70.605 336.737,71.805 332.277,71.805C330.375,71.805 328.577,71.579 326.879,71.126C325.182,70.671 323.756,70.045 322.601,69.246L324.467,58.956Z" style="fill:rgb(30,30,30);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-124.95,3.42849,3.42849,124.95,110.553,125.737)">
<path d="M0.883,-0.081L0.121,0.081L0.256,-0.063L0.883,-0.081Z" style="fill:url(#_Linear1);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-79.8325,-12.0502,-12.0502,79.8325,321.143,141.025)">
<path d="M0.878,-0.285L-0.073,0.71L-1.186,0.542L0.015,0.207L-0.846,0.077L0.355,-0.258L-0.505,-0.388L0.649,-0.71L0.878,-0.285Z" style="fill:url(#_Linear2);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-85.8631,-121.806,-121.806,85.8631,249.969,214.353)">
<path d="M0.44,-0.04L0.44,-0.04L0.44,-0.04L0.265,-0.056L0.177,0.437L-0.311,-0.255L0.262,-0.437L0.568,-0.437L0.44,-0.04Z" style="fill:url(#_Linear3);fill-rule:nonzero;"/>
</g>
<g transform="matrix(46.2689,44.1068,44.1068,-46.2689,193.973,135.309)">
<path d="M0.5,0L0.5,-0L0.5,0L0.5,0Z" style="fill:url(#_Linear4);fill-rule:nonzero;"/>
</g>
<g transform="matrix(178.598,224.167,224.167,-178.598,85.1346,-112.899)">
<path d="M0.622,-0.115L0.761,-0.115L0.806,-0.013L0.826,0.182L0.622,-0.115Z" style="fill:url(#_Linear5);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-277.147,-73.0588,-73.0588,277.147,436.785,70.5207)">
<path d="M0.467,0.005L0.49,0.062L0.271,-0.062L0.467,0.005Z" style="fill:url(#_Linear6);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-372.117,-40.4813,-40.4813,372.117,275.916,93.8142)">
<path d="M0.2,0.001L0.219,-0.018L0.614,0.012L0.519,0.089L0.282,0.068L0.2,0.135L0.463,0.194L0.374,0.266L0.138,0.186L0.138,0.186L0.138,0.186L0.047,0.033L-0.131,-0.266L0.2,0.001Z" style="fill:url(#_Linear7);fill-rule:nonzero;"/>
</g>
<g transform="matrix(138.807,132.321,132.321,-138.807,115.085,60.1115)">
<path d="M0.735,-0L0.735,-0L0.735,0L0.735,-0Z" style="fill:url(#_Linear8);fill-rule:nonzero;"/>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-5.1153e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-8.16759e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,5.55112e-17,5.55112e-17,-1,0,5.48218e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-3.25937e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear5" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-0.801909,-0.597446,-0.597446,0.801909,1.3495,0.447446)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear6" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-2.6378e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear7" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,1.38778e-17,1.38778e-17,-1,0,-6.47319e-07)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear8" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-3.82755e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 413 327" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,0,223.853)">
<path d="M87.462,32.114L98.283,32.114L97.244,55.145L104.388,32.114L113.131,32.114L113.025,55.145L119.235,32.114L130.057,32.114L116.356,71.033L106.36,71.033L106.467,46.695L98.87,71.033L88.901,71.033L87.462,32.114Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,0,-170.731)">
<path d="M195.198,248.963L226.365,240.838L201.513,256.893L195.198,248.963Z" style="fill:white;"/>
</g>
<g transform="matrix(1,0,0,1,0,223.853)">
<path d="M170.125,57.438L174.47,57.438L174.603,43.017L170.125,57.438ZM154.985,71.033L169.619,32.115L182.44,32.115L185.185,71.033L174.203,71.033L174.257,65.301L167.832,65.301L166.126,71.033L154.985,71.033Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(0.15626,-0.987716,-0.987716,-0.15626,242.8,508.084)">
<path d="M206.842,56.945L246.244,56.945L247.943,46.203L208.537,46.23L206.842,56.945Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,0,223.853)">
<path d="M270.363,71.033L276.547,32.115L287.449,32.115L282.811,61.383L290.781,61.383L289.262,71.033L270.363,71.033Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,0,223.96)">
<path d="M324.467,58.956C325.676,59.97 326.946,60.752 328.28,61.303C329.612,61.854 330.891,62.129 332.117,62.129C333.201,62.129 334.063,61.831 334.703,61.236C335.342,60.641 335.663,59.828 335.663,58.797C335.663,57.909 335.396,57.024 334.863,56.145C334.33,55.265 333.299,54.097 331.771,52.64C329.922,50.845 328.652,49.263 327.959,47.895C327.266,46.527 326.92,45.016 326.92,43.364C326.92,39.65 328.097,36.699 330.452,34.514C332.806,32.328 336,31.235 340.034,31.235C341.669,31.235 343.223,31.417 344.699,31.782C346.173,32.146 347.675,32.71 349.204,33.474L347.657,42.964C346.484,42.147 345.343,41.525 344.233,41.098C343.121,40.671 342.06,40.458 341.047,40.458C340.141,40.458 339.425,40.693 338.901,41.163C338.377,41.635 338.114,42.279 338.114,43.097C338.114,44.199 339.119,45.745 341.127,47.735C341.375,47.984 341.571,48.179 341.714,48.321C343.739,50.312 345.072,52.017 345.712,53.44C346.352,54.861 346.671,56.504 346.671,58.371C346.671,62.529 345.387,65.808 342.819,68.206C340.252,70.605 336.737,71.805 332.277,71.805C330.375,71.805 328.577,71.579 326.879,71.126C325.182,70.671 323.756,70.045 322.601,69.246L324.467,58.956Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(-124.95,3.42849,3.42849,124.95,110.553,125.737)">
<path d="M0.883,-0.081L0.121,0.081L0.256,-0.063L0.883,-0.081Z" style="fill:url(#_Linear1);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-79.8325,-12.0502,-12.0502,79.8325,321.143,141.025)">
<path d="M0.878,-0.285L-0.073,0.71L-1.186,0.542L0.015,0.207L-0.846,0.077L0.355,-0.258L-0.505,-0.388L0.649,-0.71L0.878,-0.285Z" style="fill:url(#_Linear2);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-85.8631,-121.806,-121.806,85.8631,249.969,214.353)">
<path d="M0.44,-0.04L0.44,-0.04L0.44,-0.04L0.265,-0.056L0.177,0.437L-0.311,-0.255L0.262,-0.437L0.568,-0.437L0.44,-0.04Z" style="fill:url(#_Linear3);fill-rule:nonzero;"/>
</g>
<g transform="matrix(46.2689,44.1068,44.1068,-46.2689,193.973,135.309)">
<path d="M0.5,0L0.5,-0L0.5,0L0.5,0Z" style="fill:url(#_Linear4);fill-rule:nonzero;"/>
</g>
<g transform="matrix(178.598,224.167,224.167,-178.598,85.1346,-112.899)">
<path d="M0.622,-0.115L0.761,-0.115L0.806,-0.013L0.826,0.182L0.622,-0.115Z" style="fill:url(#_Linear5);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-277.147,-73.0588,-73.0588,277.147,436.785,70.5207)">
<path d="M0.467,0.005L0.49,0.062L0.271,-0.062L0.467,0.005Z" style="fill:url(#_Linear6);fill-rule:nonzero;"/>
</g>
<g transform="matrix(-372.117,-40.4813,-40.4813,372.117,275.916,93.8142)">
<path d="M0.2,0.001L0.219,-0.018L0.614,0.012L0.519,0.089L0.282,0.068L0.2,0.135L0.463,0.194L0.374,0.266L0.138,0.186L0.138,0.186L0.138,0.186L0.047,0.033L-0.131,-0.266L0.2,0.001Z" style="fill:url(#_Linear7);fill-rule:nonzero;"/>
</g>
<g transform="matrix(138.807,132.321,132.321,-138.807,115.085,60.1115)">
<path d="M0.735,-0L0.735,-0L0.735,0L0.735,-0Z" style="fill:url(#_Linear8);fill-rule:nonzero;"/>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-5.1153e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-8.16759e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,5.55112e-17,5.55112e-17,-1,0,5.48218e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-3.25937e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear5" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-0.801909,-0.597446,-0.597446,0.801909,1.3495,0.447446)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear6" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-2.6378e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear7" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,1.38778e-17,1.38778e-17,-1,0,-6.47319e-07)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear8" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-3.82755e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

View file

@ -1,15 +1,21 @@
import wailsLogoWhite from '../assets/wails-logo-white-text.svg';
import wailsLogoBlack from '../assets/wails-logo-black-text.svg';
interface WailsLogoProps {
className?: string;
size?: number;
theme?: 'light' | 'dark';
}
export default function WailsLogo({ className = '', size = 240 }: WailsLogoProps) {
export default function WailsLogo({ className = '', size = 240, theme = 'dark' }: WailsLogoProps) {
// White text for dark mode, black text for light mode
const logoSrc = theme === 'dark' ? wailsLogoWhite : wailsLogoBlack;
return (
<img
src="/wails-logo.png"
src={logoSrc}
alt="Wails"
width={size}
height={size}
className={`object-contain ${className}`}
style={{
filter: 'drop-shadow(0 0 60px rgba(239, 68, 68, 0.4))',

View file

@ -62,4 +62,35 @@ export interface WizardState {
startTime: string;
}
export type Step = 'splash' | 'welcome' | 'dependencies' | 'docker' | 'config' | 'wails-config' | 'complete';
export type Step = 'splash' | 'welcome' | 'dependencies' | 'docker' | 'defaults' | 'config' | 'wails-config' | 'complete';
export interface AuthorDefaults {
name: string;
company: string;
}
export interface ProjectDefaults {
productIdentifierPrefix: string;
defaultTemplate: string;
copyrightTemplate: string;
descriptionTemplate: string;
defaultVersion: string;
}
export interface SigningDefaults {
macOS: {
developerID: string; // e.g., "Developer ID Application: John Doe (TEAMID)"
appleID: string; // Apple ID for notarization
teamID: string; // Apple Team ID
};
windows: {
certificatePath: string; // Path to .pfx certificate
timestampServer: string; // e.g., "http://timestamp.digicert.com"
};
}
export interface GlobalDefaults {
author: AuthorDefaults;
project: ProjectDefaults;
signing?: SigningDefaults;
}

View file

@ -0,0 +1,6 @@
/// <reference types="vite/client" />
declare module '*.svg' {
const content: string;
export default content;
}

View file

@ -4,6 +4,7 @@ export default {
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
darkMode: 'class',
theme: {
extend: {
colors: {

View file

@ -162,7 +162,9 @@ func (w *Wizard) setupRoutes(mux *http.ServeMux) {
mux.HandleFunc("/api/dependencies/install", w.handleInstallDependency)
mux.HandleFunc("/api/docker/status", w.handleDockerStatus)
mux.HandleFunc("/api/docker/build", w.handleDockerBuild)
mux.HandleFunc("/api/docker/start-background", w.handleDockerStartBackground)
mux.HandleFunc("/api/wails-config", w.handleWailsConfig)
mux.HandleFunc("/api/defaults", w.handleDefaults)
mux.HandleFunc("/api/complete", w.handleComplete)
mux.HandleFunc("/api/close", w.handleClose)
@ -438,6 +440,98 @@ func (w *Wizard) handleDockerBuild(rw http.ResponseWriter, r *http.Request) {
json.NewEncoder(rw).Encode(map[string]string{"status": "started"})
}
// handleDockerStartBackground checks if Docker is available and starts building in background
// This is called early in the wizard flow to get a head start on the image build
func (w *Wizard) handleDockerStartBackground(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
// Check Docker status first
status := w.checkDocker()
w.dockerMu.Lock()
w.dockerStatus = status
w.dockerMu.Unlock()
// Only start build if Docker is installed, running, and image not built yet
if !status.Installed || !status.Running || status.ImageBuilt {
json.NewEncoder(rw).Encode(map[string]interface{}{
"started": false,
"reason": getDockerNotStartedReason(status),
"status": status,
})
return
}
// Check if already building
w.dockerMu.RLock()
alreadyBuilding := w.dockerStatus.PullStatus == "pulling"
w.dockerMu.RUnlock()
if alreadyBuilding {
json.NewEncoder(rw).Encode(map[string]interface{}{
"started": false,
"reason": "already_building",
"status": status,
})
return
}
// Start building in background
w.dockerMu.Lock()
w.dockerStatus.PullStatus = "pulling"
w.dockerStatus.PullProgress = 0
w.dockerMu.Unlock()
// Build the Docker image in background
go func() {
cmd := exec.Command("wails3", "task", "setup:docker")
err := cmd.Run()
w.dockerMu.Lock()
if err != nil {
w.dockerStatus.PullStatus = "error"
w.dockerStatus.PullError = err.Error()
} else {
w.dockerStatus.PullStatus = "complete"
w.dockerStatus.ImageBuilt = true
}
w.dockerStatus.PullProgress = 100
w.dockerMu.Unlock()
}()
// Simulate progress updates while building
go func() {
for i := 0; i < 90; i += 5 {
time.Sleep(2 * time.Second)
w.dockerMu.Lock()
if w.dockerStatus.PullStatus != "pulling" {
w.dockerMu.Unlock()
return
}
w.dockerStatus.PullProgress = i
w.dockerMu.Unlock()
}
}()
json.NewEncoder(rw).Encode(map[string]interface{}{
"started": true,
"status": status,
})
}
func getDockerNotStartedReason(status DockerStatus) string {
if !status.Installed {
return "not_installed"
}
if !status.Running {
return "not_running"
}
if status.ImageBuilt {
return "already_built"
}
return "unknown"
}
// InstallRequest represents a request to install a dependency
type InstallRequest struct {
Command string `json:"command"`
@ -492,3 +586,43 @@ func (w *Wizard) handleInstallDependency(rw http.ResponseWriter, r *http.Request
Output: string(output),
})
}
func (w *Wizard) handleDefaults(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
switch r.Method {
case http.MethodGet:
defaults, err := LoadGlobalDefaults()
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
// Try to pre-populate author info from git config if empty
if defaults.Author.Name == "" {
if name, err := execCommand("git", "config", "--global", "user.name"); err == nil && name != "" {
defaults.Author.Name = name
}
}
json.NewEncoder(rw).Encode(defaults)
case http.MethodPost:
var defaults GlobalDefaults
if err := json.NewDecoder(r.Body).Decode(&defaults); err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}
if err := SaveGlobalDefaults(defaults); err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
path, _ := GetDefaultsPath()
json.NewEncoder(rw).Encode(map[string]string{"status": "saved", "path": path})
default:
http.Error(rw, "Method not allowed", http.StatusMethodNotAllowed)
}
}