feat: funciones Wails — scaffold, CRUD bindings, build, eventos y streaming
Funciones Go para crear apps Wails: scaffold estructura, bind CRUD genérico, build multiplataforma, emit eventos y stream de datos al frontend. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user