Add standalone Go build alongside VSCode extension
Ports all five tools to a single 7.5 MB Windows exe with an embedded WebView2 window, file-based config, single-instance focus, and clean shutdown. Adds build.ps1 to build both targets from one command. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
177
standalone/platform.go
Normal file
177
standalone/platform.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type PlecConfig struct {
|
||||
UploadUrl string `json:"uploadUrl"`
|
||||
OriginUrl string `json:"originUrl"`
|
||||
PreviewBase string `json:"previewBase"`
|
||||
SkipExistsCheck bool `json:"skipExistsCheck"`
|
||||
}
|
||||
|
||||
type ApplovinConfig struct {
|
||||
Cookie string `json:"cookie"`
|
||||
}
|
||||
|
||||
type SendToMobileConfig struct {
|
||||
Port int `json:"port"`
|
||||
}
|
||||
|
||||
type DailyUpdateConfig struct {
|
||||
LastProject string `json:"lastProject"`
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
Plec PlecConfig `json:"plec"`
|
||||
Applovin ApplovinConfig `json:"applovin"`
|
||||
SendToMobile SendToMobileConfig `json:"sendToMobile"`
|
||||
DailyUpdate DailyUpdateConfig `json:"dailyUpdate"`
|
||||
}
|
||||
|
||||
var configMu sync.Mutex
|
||||
|
||||
func defaultConfig() AppConfig {
|
||||
return AppConfig{
|
||||
Plec: PlecConfig{
|
||||
UploadUrl: "http://167.99.227.249/src/upload_HTML_Experimental.php",
|
||||
OriginUrl: "http://167.99.227.249",
|
||||
PreviewBase: "https://playable.applovindemo.com/phaser/",
|
||||
SkipExistsCheck: false,
|
||||
},
|
||||
Applovin: ApplovinConfig{Cookie: ""},
|
||||
SendToMobile: SendToMobileConfig{Port: 0},
|
||||
DailyUpdate: DailyUpdateConfig{LastProject: ""},
|
||||
}
|
||||
}
|
||||
|
||||
func appDir() string {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
wd, _ := os.Getwd()
|
||||
return wd
|
||||
}
|
||||
// `go run` puts the exe in a temp dir; fall back to cwd in that case.
|
||||
resolved, err := filepath.EvalSymlinks(exe)
|
||||
if err == nil {
|
||||
exe = resolved
|
||||
}
|
||||
dir := filepath.Dir(exe)
|
||||
if strings.Contains(strings.ToLower(dir), "go-build") {
|
||||
wd, _ := os.Getwd()
|
||||
return wd
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
func configPath() string {
|
||||
return filepath.Join(appDir(), "config.json")
|
||||
}
|
||||
|
||||
func LoadConfig() AppConfig {
|
||||
configMu.Lock()
|
||||
defer configMu.Unlock()
|
||||
cfg := defaultConfig()
|
||||
data, err := os.ReadFile(configPath())
|
||||
if err != nil {
|
||||
return cfg
|
||||
}
|
||||
_ = json.Unmarshal(data, &cfg)
|
||||
return cfg
|
||||
}
|
||||
|
||||
func SaveConfig(cfg AppConfig) error {
|
||||
configMu.Lock()
|
||||
defer configMu.Unlock()
|
||||
data, err := json.MarshalIndent(cfg, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(configPath(), data, 0644)
|
||||
}
|
||||
|
||||
func OpenInBrowser(url string) {
|
||||
// `start` is a cmd builtin. Quoted empty arg is the window title.
|
||||
cmd := exec.Command("cmd", "/c", "start", "", url)
|
||||
_ = cmd.Start()
|
||||
}
|
||||
|
||||
func CopyToClipboard(text string) error {
|
||||
cmd := exec.Command("clip")
|
||||
cmd.Stdin = strings.NewReader(text)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
type FileFilter struct {
|
||||
Name string
|
||||
Extensions []string
|
||||
}
|
||||
|
||||
func psEscape(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
||||
|
||||
func PickFiles(title string, filters []FileFilter, multi bool, initialDir string) []string {
|
||||
filterStr := "All files|*.*"
|
||||
if len(filters) > 0 {
|
||||
parts := make([]string, 0, len(filters))
|
||||
for _, f := range filters {
|
||||
exts := make([]string, 0, len(f.Extensions))
|
||||
for _, e := range f.Extensions {
|
||||
exts = append(exts, "*."+e)
|
||||
}
|
||||
parts = append(parts, fmt.Sprintf("%s|%s", f.Name, strings.Join(exts, ";")))
|
||||
}
|
||||
filterStr = strings.Join(parts, "|")
|
||||
}
|
||||
multiStr := "false"
|
||||
if multi {
|
||||
multiStr = "true"
|
||||
}
|
||||
script := fmt.Sprintf(`
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
$dlg = New-Object System.Windows.Forms.OpenFileDialog
|
||||
$dlg.Title = '%s'
|
||||
$dlg.Filter = '%s'
|
||||
$dlg.Multiselect = $%s
|
||||
if ('%s'.Length -gt 0) { $dlg.InitialDirectory = '%s' }
|
||||
if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
|
||||
$dlg.FileNames | ForEach-Object { Write-Output $_ }
|
||||
}
|
||||
`, psEscape(title), psEscape(filterStr), multiStr, psEscape(initialDir), psEscape(initialDir))
|
||||
out, err := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-STA", "-Command", script).Output()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
result := make([]string, 0, len(lines))
|
||||
for _, l := range lines {
|
||||
t := strings.TrimSpace(l)
|
||||
if t != "" {
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func PickFolder(title, initialDir string) string {
|
||||
script := fmt.Sprintf(`
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
$dlg = New-Object System.Windows.Forms.FolderBrowserDialog
|
||||
$dlg.Description = '%s'
|
||||
if ('%s'.Length -gt 0) { $dlg.SelectedPath = '%s' }
|
||||
if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
|
||||
Write-Output $dlg.SelectedPath
|
||||
}
|
||||
`, psEscape(title), psEscape(initialDir), psEscape(initialDir))
|
||||
out, err := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-STA", "-Command", script).Output()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
Reference in New Issue
Block a user