package infra import ( "context" "fmt" "os" "os/exec" "path/filepath" "strings" "text/template" ) // ScaffoldWailsAppConfig configura la generación del proyecto Wails. type ScaffoldWailsAppConfig struct { Name string // Nombre del proyecto Dir string // Directorio destino Title string // Título de la ventana Width int // Ancho de la ventana (default 1024) Height int // Alto de la ventana (default 768) Author string // Nombre del autor FrontendLib string // Path a la frontend library (default ~/.local_agentes/frontend/frontend) } const mainGoTpl = `package main import ( "embed" "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist var assets embed.FS func main() { app := NewApp() err := wails.Run(&options.App{ Title: "{{.Title}}", Width: {{.Width}}, Height: {{.Height}}, AssetServer: &assetserver.Options{ Assets: assets, }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ app, }, }) if err != nil { println("Error:", err.Error()) } } ` const appGoTpl = `package main import ( "context" "github.com/wailsapp/wails/v2/pkg/runtime" ) // App struct — cada método público se expone como binding IPC al frontend. type App struct { ctx context.Context } func NewApp() *App { return &App{} } func (a *App) startup(ctx context.Context) { a.ctx = ctx } // EmitEvent emite un evento al frontend. func (a *App) EmitEvent(eventName string, data interface{}) { runtime.EventsEmit(a.ctx, eventName, data) } // Ping verifica que el IPC funciona. func (a *App) Ping() string { return "pong" } ` const wailsJSONTpl = `{ "$schema": "https://wails.io/schemas/config.v2.json", "name": "{{.Name}}", "outputfilename": "{{.Name}}", "frontend:dir": "./frontend", "frontend:install": "pnpm install", "frontend:build": "pnpm run build", "frontend:dev:watcher": "pnpm run dev", "frontend:dev:serverUrl": "auto", "wailsjsdir": "./frontend/src/wailsjs", "author": { "name": "{{.Author}}" } } ` const goModTpl = `module {{.Name}} go 1.23 require github.com/wailsapp/wails/v2 v2.11.0 ` // ScaffoldWailsApp genera la estructura base de un proyecto Wails con frontend vinculado. func ScaffoldWailsApp(ctx context.Context, cfg ScaffoldWailsAppConfig) error { if cfg.Name == "" { return fmt.Errorf("name is required") } if cfg.Dir == "" { cfg.Dir = cfg.Name } if cfg.Title == "" { cfg.Title = cfg.Name } if cfg.Width == 0 { cfg.Width = 1024 } if cfg.Height == 0 { cfg.Height = 768 } if cfg.Author == "" { cfg.Author = "Agent" } if cfg.FrontendLib == "" { home, _ := os.UserHomeDir() cfg.FrontendLib = filepath.Join(home, ".local_agentes", "frontend", "frontend") } // Crear directorio if err := os.MkdirAll(cfg.Dir, 0755); err != nil { return fmt.Errorf("creating dir: %w", err) } // Generar archivos Go files := map[string]string{ "main.go": mainGoTpl, "app.go": appGoTpl, "wails.json": wailsJSONTpl, "go.mod": goModTpl, } for name, tpl := range files { path := filepath.Join(cfg.Dir, name) t, err := template.New(name).Parse(tpl) if err != nil { return fmt.Errorf("parsing template %s: %w", name, err) } f, err := os.Create(path) if err != nil { return fmt.Errorf("creating %s: %w", name, err) } if err := t.Execute(f, cfg); err != nil { f.Close() return fmt.Errorf("executing template %s: %w", name, err) } f.Close() } // Crear frontend como link simbólico o copiar template frontendDir := filepath.Join(cfg.Dir, "frontend") createProjectScript := filepath.Join(filepath.Dir(cfg.FrontendLib), "..", "scripts", "create-project.sh") if _, err := os.Stat(createProjectScript); err == nil { // Usar el script de Frontend_Library cmd := exec.CommandContext(ctx, "bash", createProjectScript, cfg.Name, frontendDir, "--wails") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return fmt.Errorf("creating frontend: %w", err) } } else { // Fallback: crear frontend mínimo if err := os.MkdirAll(filepath.Join(frontendDir, "src"), 0755); err != nil { return fmt.Errorf("creating frontend dir: %w", err) } pkgJSON := fmt.Sprintf(`{"name":"%s-frontend","private":true,"scripts":{"dev":"vite","build":"vite build"}}`, cfg.Name) os.WriteFile(filepath.Join(frontendDir, "package.json"), []byte(pkgJSON), 0644) } // go mod tidy cmd := exec.CommandContext(ctx, "go", "mod", "tidy") cmd.Dir = cfg.Dir cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr _ = cmd.Run() // No fatal si falla fmt.Printf("Wails app scaffolded at %s\n", cfg.Dir) return nil } // GenerateAppBinding genera el código Go para un método de binding. func GenerateAppBinding(name string, params []string, returnType string, body string) string { var sb strings.Builder sb.WriteString(fmt.Sprintf("func (a *App) %s(", name)) sb.WriteString(strings.Join(params, ", ")) sb.WriteString(")") if returnType != "" { sb.WriteString(" " + returnType) } sb.WriteString(" {\n") sb.WriteString("\t" + body + "\n") sb.WriteString("}\n") return sb.String() }