mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
- 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>
219 lines
5.7 KiB
Go
219 lines
5.7 KiB
Go
// 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)
|
|
}
|