mirror of
https://github.com/taigrr/wails.git
synced 2026-04-02 13:19:00 -07:00
Compare commits
1 Commits
obfuscatio
...
feature/v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29715b2d57 |
@@ -1,6 +1,7 @@
|
||||
package dev
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -10,14 +11,18 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/process"
|
||||
|
||||
"github.com/wzshiming/ctc"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/process"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
@@ -44,212 +49,262 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
app.PrintBanner()
|
||||
|
||||
// TODO: Check you are in a project directory
|
||||
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
var debugBinaryProcess *process.Process = nil
|
||||
var buildFrontend bool = false
|
||||
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
||||
|
||||
// Setup signal handler
|
||||
quitChannel := make(chan os.Signal, 1)
|
||||
signal.Notify(quitChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
|
||||
debounceQuit := make(chan bool, 1)
|
||||
|
||||
// Do initial build
|
||||
logger.Println("Building application for development...")
|
||||
debugBinaryProcess, err = restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
||||
// logger.Println("event: %+v", event)
|
||||
|
||||
// Check for new directories
|
||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
||||
// If this is a folder, add it to our watch list
|
||||
if fs.DirExists(event.Name) {
|
||||
if !strings.Contains(event.Name, "node_modules") {
|
||||
err := watcher.Add(event.Name)
|
||||
if err != nil {
|
||||
logger.Fatal("%s", err.Error())
|
||||
}
|
||||
logger.Println("Watching directory: %s", event.Name)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Check for file writes
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
|
||||
logger.Println("modified file: %s", event.Name)
|
||||
var rebuild bool = false
|
||||
|
||||
// Iterate all file patterns
|
||||
for _, pattern := range extensionsThatTriggerARebuild {
|
||||
rebuild = strings.HasSuffix(event.Name, pattern)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
if rebuild {
|
||||
// Only build frontend when the file isn't a Go file
|
||||
buildFrontend = !strings.HasSuffix(event.Name, "go")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !rebuild {
|
||||
logger.Println("Filename change: %s did not match extension list (%s)", event.Name, extensions)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Println("Partial build triggered: %s updated", event.Name)
|
||||
|
||||
// Do a rebuild
|
||||
|
||||
// Try and build the app
|
||||
newBinaryProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess)
|
||||
if err != nil {
|
||||
fmt.Printf("Error during build: %s", err.Error())
|
||||
return
|
||||
}
|
||||
// If we have a new process, save it
|
||||
if newBinaryProcess != nil {
|
||||
debugBinaryProcess = newBinaryProcess
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// Get project dir
|
||||
dir, err := os.Getwd()
|
||||
reloader, err := NewReloader(logger, extensionsThatTriggerARebuild, ldflags, compilerCommand)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get all subdirectories
|
||||
dirs, err := fs.GetSubdirectories(dir)
|
||||
// Start
|
||||
err = reloader.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
println("ERRRRRRRRRR: %+v", err)
|
||||
}
|
||||
|
||||
// Setup a watcher for non-node_modules directories
|
||||
dirs.Each(func(dir string) {
|
||||
if strings.Contains(dir, "node_modules") {
|
||||
return
|
||||
}
|
||||
logger.Println("Watching directory: %s", dir)
|
||||
err = watcher.Add(dir)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
})
|
||||
logger.Println("\nDevelopment mode exited")
|
||||
|
||||
// Wait until we get a quit signal
|
||||
quit := false
|
||||
for quit == false {
|
||||
select {
|
||||
case <-quitChannel:
|
||||
println("Caught quit")
|
||||
// Notify debouncer to quit
|
||||
debounceQuit <- true
|
||||
quit = true
|
||||
}
|
||||
}
|
||||
|
||||
// Kill the current program if running
|
||||
if debugBinaryProcess != nil {
|
||||
err := debugBinaryProcess.Kill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logger.Println("Development mode exited")
|
||||
|
||||
return nil
|
||||
return err
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Credit: https://drailing.net/2018/01/debounce-function-for-golang/
|
||||
func debounce(interval time.Duration, input chan fsnotify.Event, quitChannel chan bool, cb func(arg fsnotify.Event)) {
|
||||
var item fsnotify.Event
|
||||
timer := time.NewTimer(interval)
|
||||
exit:
|
||||
type Reloader struct {
|
||||
|
||||
// Main context
|
||||
ctx context.Context
|
||||
|
||||
// Signal context
|
||||
signalContext context.Context
|
||||
|
||||
// notify
|
||||
watcher *fsnotify.Watcher
|
||||
|
||||
// Logger
|
||||
logger *clilogger.CLILogger
|
||||
|
||||
// Extensions to listen for
|
||||
extensionsThatTriggerARebuild []string
|
||||
|
||||
// The binary we are running
|
||||
binary *process.Process
|
||||
|
||||
// options
|
||||
ldflags string
|
||||
compiler string
|
||||
}
|
||||
|
||||
func NewReloader(logger *clilogger.CLILogger, extensionsThatTriggerARebuild []string, ldFlags string, compiler string) (*Reloader, error) {
|
||||
var result Reloader
|
||||
|
||||
// Create context
|
||||
result.ctx = context.Background()
|
||||
|
||||
// Signal context (we don't need cancel)
|
||||
signalContext, _ := signal.NotifyContext(result.ctx, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
result.signalContext = signalContext
|
||||
|
||||
// Create watcher
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.watcher = watcher
|
||||
|
||||
// Logger
|
||||
result.logger = logger
|
||||
|
||||
// Extensions
|
||||
result.extensionsThatTriggerARebuild = extensionsThatTriggerARebuild
|
||||
|
||||
// Options
|
||||
result.ldflags = ldFlags
|
||||
result.compiler = compiler
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (r *Reloader) Start() error {
|
||||
|
||||
err := r.rebuildBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get project dir
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get all subdirectories
|
||||
dirs, err := fs.GetSubdirectories(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup a watcher for non-node_modules directories
|
||||
r.logger.Println("Watching (sub)directories: %s", dir)
|
||||
|
||||
dirs.Each(func(dir string) {
|
||||
if strings.Contains(dir, "node_modules") {
|
||||
return
|
||||
}
|
||||
err = r.watcher.Add(dir)
|
||||
if err != nil {
|
||||
r.logger.Fatal(err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
// Main loop
|
||||
for {
|
||||
select {
|
||||
case item = <-input:
|
||||
timer.Reset(interval)
|
||||
case <-timer.C:
|
||||
if item.Name != "" {
|
||||
cb(item)
|
||||
case <-r.signalContext.Done():
|
||||
if r.binary != nil {
|
||||
println("Binary is not nil - kill")
|
||||
return r.binary.Kill()
|
||||
}
|
||||
return nil
|
||||
case event := <-r.watcher.Events:
|
||||
err := r.processWatcherEvent(event)
|
||||
if err != nil {
|
||||
println("error from processWatcherEvent. Calling cancel()")
|
||||
println("Calling kill")
|
||||
return r.binary.Kill()
|
||||
}
|
||||
case <-quitChannel:
|
||||
break exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, debugBinaryProcess *process.Process) (*process.Process, error) {
|
||||
func (r *Reloader) processWatcherEvent(event fsnotify.Event) error {
|
||||
|
||||
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand)
|
||||
println()
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
return nil, errors.Wrap(err, "Build Failed:")
|
||||
// Check for new directories
|
||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
||||
// If this is a folder, add it to our watch list
|
||||
if fs.DirExists(event.Name) {
|
||||
if !strings.Contains(event.Name, "node_modules") {
|
||||
err := r.watcher.Add(event.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.logger.Println("Watching directory: %s", event.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
logger.Println("Build new binary: %s", appBinary)
|
||||
|
||||
// Kill existing binary if need be
|
||||
if debugBinaryProcess != nil {
|
||||
killError := debugBinaryProcess.Kill()
|
||||
// Check for file writes
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
|
||||
if killError != nil {
|
||||
logger.Fatal("Unable to kill debug binary (PID: %d)!", debugBinaryProcess.PID())
|
||||
var rebuild bool
|
||||
|
||||
// Iterate all file patterns
|
||||
for _, pattern := range r.extensionsThatTriggerARebuild {
|
||||
if strings.HasSuffix(event.Name, pattern) {
|
||||
rebuild = true
|
||||
}
|
||||
}
|
||||
|
||||
debugBinaryProcess = nil
|
||||
if !rebuild {
|
||||
return nil
|
||||
}
|
||||
|
||||
r.logger.Println("\n%s[Build triggered] %s %s", ctc.ForegroundGreen|ctc.ForegroundBright, event.Name, ctc.Reset)
|
||||
|
||||
return r.rebuildBinary()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reloader) rebuildBinary() error {
|
||||
|
||||
// rebuild binary
|
||||
binary, err := r.buildApp()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Generate `backend.js`
|
||||
// Kill current binary if running
|
||||
if r.binary != nil {
|
||||
err = r.binary.Kill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Start up new binary
|
||||
newProcess := process.NewProcess(logger, appBinary)
|
||||
newProcess := process.NewProcess(r.ctx, r.logger, binary)
|
||||
err = newProcess.Start()
|
||||
if err != nil {
|
||||
// Remove binary
|
||||
deleteError := fs.DeleteFile(appBinary)
|
||||
if deleteError != nil {
|
||||
logger.Fatal("Unable to delete app binary: " + appBinary)
|
||||
}
|
||||
logger.Fatal("Unable to start application: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
return newProcess, nil
|
||||
// Ensure process runs correctly
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string) (string, error) {
|
||||
//func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, debugBinaryProcess *process.Process) (*process.Process, error) {
|
||||
//
|
||||
// appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand)
|
||||
// println()
|
||||
// if err != nil {
|
||||
// logger.Fatal(err.Error())
|
||||
// return nil, errors.Wrap(err, "Build Failed:")
|
||||
// }
|
||||
// logger.Println("Build new binary: %s", appBinary)
|
||||
//
|
||||
// // Kill existing binary if need be
|
||||
// if debugBinaryProcess != nil {
|
||||
// killError := debugBinaryProcess.Kill()
|
||||
//
|
||||
// if killError != nil {
|
||||
// logger.Fatal("Unable to kill debug binary (PID: %d)!", debugBinaryProcess.PID())
|
||||
// }
|
||||
//
|
||||
// debugBinaryProcess = nil
|
||||
// }
|
||||
//
|
||||
// // TODO: Generate `backend.js`
|
||||
//
|
||||
// // Start up new binary
|
||||
// newProcess := process.NewProcess(logger, appBinary)
|
||||
// err = newProcess.Start()
|
||||
// if err != nil {
|
||||
// // Remove binary
|
||||
// deleteError := fs.DeleteFile(appBinary)
|
||||
// if deleteError != nil {
|
||||
// logger.Fatal("Unable to delete app binary: " + appBinary)
|
||||
// }
|
||||
// logger.Fatal("Unable to start application: %s", err.Error())
|
||||
// }
|
||||
//
|
||||
// // Check if port is open
|
||||
// timeout := time.Second
|
||||
// conn, err := net.DialTimeout("tcp", net.JoinHostPort("host", port), timeout)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// newProcess.Running
|
||||
// return newProcess, nil
|
||||
//}
|
||||
|
||||
// buildapp attempts to compile the application
|
||||
// It returns the path to the new binary or an error
|
||||
func (r *Reloader) buildApp() (string, error) {
|
||||
|
||||
// Create random output file
|
||||
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
OutputType: outputType,
|
||||
Logger: r.logger,
|
||||
OutputType: "dev",
|
||||
Mode: build.Debug,
|
||||
Pack: false,
|
||||
Platform: runtime.GOOS,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
LDFlags: r.ldflags,
|
||||
Compiler: r.compiler,
|
||||
OutputFile: outputFile,
|
||||
IgnoreFrontend: true,
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/wzshiming/ctc"
|
||||
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/update"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
@@ -19,12 +22,30 @@ func fatal(message string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func col(colour ctc.Color, text string) string {
|
||||
return fmt.Sprintf("%s%s%s", colour, text, ctc.Reset)
|
||||
}
|
||||
|
||||
func Yellow(str string) string {
|
||||
return col(ctc.ForegroundBrightYellow, str)
|
||||
}
|
||||
|
||||
func Red(str string) string {
|
||||
return col(ctc.ForegroundBrightRed, str)
|
||||
}
|
||||
|
||||
func banner(cli *clir.Cli) string {
|
||||
return fmt.Sprintf("%s %s - Go/HTML Application Framework", Yellow("Wails"), Red(version))
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
var err error
|
||||
|
||||
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
||||
|
||||
app.SetBannerFunction(banner)
|
||||
|
||||
build.AddBuildSubcommand(app, os.Stdout)
|
||||
err = initialise.AddSubcommand(app, os.Stdout)
|
||||
if err != nil {
|
||||
|
||||
@@ -20,6 +20,7 @@ require (
|
||||
github.com/tdewolff/minify v2.3.6+incompatible
|
||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
||||
github.com/tdewolff/test v1.0.6 // indirect
|
||||
github.com/wzshiming/ctc v1.2.3 // indirect
|
||||
github.com/xyproto/xpm v1.2.1
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||
|
||||
@@ -78,6 +78,10 @@ github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/wzshiming/ctc v1.2.3 h1:q+hW3IQNsjIlOFBTGZZZeIXTElFM4grF4spW/errh/c=
|
||||
github.com/wzshiming/ctc v1.2.3/go.mod h1:2tVAtIY7SUyraSk0JxvwmONNPFL4ARavPuEsg5+KA28=
|
||||
github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae h1:tpXvBXC3hpQBDCc9OojJZCQMVRAbT3TTdUMP8WguXkY=
|
||||
github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae/go.mod h1:VTAq37rkGeV+WOybvZwjXiJOicICdpLCN8ifpISjK20=
|
||||
github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
|
||||
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -97,6 +101,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
||||
|
||||
@@ -4,8 +4,9 @@ package app
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
)
|
||||
|
||||
// Init initialises the application for a debug environment
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
@@ -9,18 +10,16 @@ import (
|
||||
|
||||
// Process defines a process that can be executed
|
||||
type Process struct {
|
||||
logger *clilogger.CLILogger
|
||||
cmd *exec.Cmd
|
||||
exitChannel chan bool
|
||||
Running bool
|
||||
logger *clilogger.CLILogger
|
||||
cmd *exec.Cmd
|
||||
running bool
|
||||
}
|
||||
|
||||
// NewProcess creates a new process struct
|
||||
func NewProcess(logger *clilogger.CLILogger, cmd string, args ...string) *Process {
|
||||
func NewProcess(ctx context.Context, logger *clilogger.CLILogger, cmd string, args ...string) *Process {
|
||||
result := &Process{
|
||||
logger: logger,
|
||||
cmd: exec.Command(cmd, args...),
|
||||
exitChannel: make(chan bool, 1),
|
||||
logger: logger,
|
||||
cmd: exec.CommandContext(ctx, cmd, args...),
|
||||
}
|
||||
result.cmd.Stdout = os.Stdout
|
||||
result.cmd.Stderr = os.Stderr
|
||||
@@ -35,30 +34,30 @@ func (p *Process) Start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Running = true
|
||||
p.running = true
|
||||
|
||||
go func(cmd *exec.Cmd, running *bool, logger *clilogger.CLILogger, exitChannel chan bool) {
|
||||
logger.Println("Starting process (PID: %d)", cmd.Process.Pid)
|
||||
cmd.Wait()
|
||||
logger.Println("Exiting process (PID: %d)", cmd.Process.Pid)
|
||||
*running = false
|
||||
exitChannel <- true
|
||||
}(p.cmd, &p.Running, p.logger, p.exitChannel)
|
||||
go func(p *Process) {
|
||||
p.logger.Println("Starting process (PID: %d)", p.cmd.Process.Pid)
|
||||
err := p.cmd.Wait()
|
||||
if err != nil {
|
||||
p.running = false
|
||||
p.logger.Println("Process failed to run: %s", err.Error())
|
||||
return
|
||||
}
|
||||
p.logger.Println("Exiting process (PID: %d)", p.cmd.Process.Pid)
|
||||
}(p)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Kill the process
|
||||
func (p *Process) Kill() error {
|
||||
if !p.Running {
|
||||
return nil
|
||||
if p.running {
|
||||
println("Calling kill")
|
||||
p.running = false
|
||||
return p.cmd.Process.Kill()
|
||||
}
|
||||
err := p.cmd.Process.Kill()
|
||||
|
||||
// Wait for command to exit properly
|
||||
<-p.exitChannel
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// PID returns the process PID
|
||||
|
||||
@@ -779,18 +779,18 @@
|
||||
|
||||
function add_css$1() {
|
||||
var style = element("style");
|
||||
style.id = "svelte-1oysp7o-style";
|
||||
style.textContent = ".menu.svelte-1oysp7o.svelte-1oysp7o{padding:3px;background-color:#0008;color:#EEF;border-radius:5px;margin-top:5px;position:absolute;backdrop-filter:blur(3px) saturate(160%) contrast(45%) brightness(140%);border:1px solid rgb(88,88,88);box-shadow:0 0 1px rgb(146,146,148) inset}.menuitem.svelte-1oysp7o.svelte-1oysp7o{display:flex;align-items:center;padding:1px 5px}.menuitem.svelte-1oysp7o.svelte-1oysp7o:hover{display:flex;align-items:center;background-color:rgb(57,131,223);padding:1px 5px;border-radius:5px}.menuitem.svelte-1oysp7o img.svelte-1oysp7o{padding-right:5px}";
|
||||
style.id = "svelte-1ucacnf-style";
|
||||
style.textContent = ".menu.svelte-1ucacnf.svelte-1ucacnf{padding:5px;background-color:#0008;color:#EEF;border-radius:5px;margin-top:5px;position:absolute;backdrop-filter:blur(3px) saturate(160%) contrast(45%) brightness(140%);border:1px solid rgb(88,88,88);box-shadow:0 0 1px rgb(146,146,148) inset}.menuitem.svelte-1ucacnf.svelte-1ucacnf{display:flex;align-items:center;padding:1px 5px}.menuitem.svelte-1ucacnf.svelte-1ucacnf:hover{display:flex;align-items:center;background-color:rgb(57,131,223);padding:1px 5px;border-radius:5px}.menuitem.svelte-1ucacnf img.svelte-1ucacnf{padding-right:5px}.separator.svelte-1ucacnf.svelte-1ucacnf{padding-top:5px;width:100%;padding-bottom:5px}.separator.svelte-1ucacnf.svelte-1ucacnf:hover{background-color:#0000}";
|
||||
append(document.head, style);
|
||||
}
|
||||
|
||||
function get_each_context(ctx, list, i) {
|
||||
const child_ctx = ctx.slice();
|
||||
child_ctx[2] = list[i];
|
||||
child_ctx[3] = list[i];
|
||||
return child_ctx;
|
||||
}
|
||||
|
||||
// (8:0) {#if !hidden}
|
||||
// (14:0) {#if visible}
|
||||
function create_if_block$1(ctx) {
|
||||
let div;
|
||||
let if_block = /*menu*/ ctx[0].Menu && create_if_block_1(ctx);
|
||||
@@ -799,7 +799,7 @@
|
||||
c() {
|
||||
div = element("div");
|
||||
if (if_block) if_block.c();
|
||||
attr(div, "class", "menu svelte-1oysp7o");
|
||||
attr(div, "class", "menu svelte-1ucacnf");
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, div, anchor);
|
||||
@@ -826,7 +826,7 @@
|
||||
};
|
||||
}
|
||||
|
||||
// (10:4) {#if menu.Menu }
|
||||
// (16:4) {#if menu.Menu }
|
||||
function create_if_block_1(ctx) {
|
||||
let each_1_anchor;
|
||||
let each_value = /*menu*/ ctx[0].Menu.Items;
|
||||
@@ -852,7 +852,7 @@
|
||||
insert(target, each_1_anchor, anchor);
|
||||
},
|
||||
p(ctx, dirty) {
|
||||
if (dirty & /*menu*/ 1) {
|
||||
if (dirty & /*click, menu*/ 1) {
|
||||
each_value = /*menu*/ ctx[0].Menu.Items;
|
||||
let i;
|
||||
|
||||
@@ -882,44 +882,41 @@
|
||||
};
|
||||
}
|
||||
|
||||
// (13:12) {#if menuItem.Image }
|
||||
function create_if_block_2(ctx) {
|
||||
// (25:52)
|
||||
function create_if_block_4(ctx) {
|
||||
let div;
|
||||
let img;
|
||||
let img_src_value;
|
||||
|
||||
return {
|
||||
c() {
|
||||
div = element("div");
|
||||
img = element("img");
|
||||
attr(img, "alt", "");
|
||||
if (img.src !== (img_src_value = "data:image/png;base64," + /*menuItem*/ ctx[2].Image)) attr(img, "src", img_src_value);
|
||||
attr(img, "class", "svelte-1oysp7o");
|
||||
div.innerHTML = `<hr/>`;
|
||||
attr(div, "class", "separator svelte-1ucacnf");
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, div, anchor);
|
||||
append(div, img);
|
||||
},
|
||||
p(ctx, dirty) {
|
||||
if (dirty & /*menu*/ 1 && img.src !== (img_src_value = "data:image/png;base64," + /*menuItem*/ ctx[2].Image)) {
|
||||
attr(img, "src", img_src_value);
|
||||
}
|
||||
},
|
||||
p: noop,
|
||||
d(detaching) {
|
||||
if (detaching) detach(div);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// (11:8) {#each menu.Menu.Items as menuItem}
|
||||
function create_each_block(ctx) {
|
||||
// (18:12) {#if menuItem.Type === "Text" }
|
||||
function create_if_block_2(ctx) {
|
||||
let div1;
|
||||
let t0;
|
||||
let div0;
|
||||
let t1_value = /*menuItem*/ ctx[2].Label + "";
|
||||
let t1_value = /*menuItem*/ ctx[3].Label + "";
|
||||
let t1;
|
||||
let t2;
|
||||
let if_block = /*menuItem*/ ctx[2].Image && create_if_block_2(ctx);
|
||||
let mounted;
|
||||
let dispose;
|
||||
let if_block = /*menuItem*/ ctx[3].Image && create_if_block_3(ctx);
|
||||
|
||||
function click_handler() {
|
||||
return /*click_handler*/ ctx[2](/*menuItem*/ ctx[3]);
|
||||
}
|
||||
|
||||
return {
|
||||
c() {
|
||||
@@ -930,7 +927,7 @@
|
||||
t1 = text(t1_value);
|
||||
t2 = space();
|
||||
attr(div0, "class", "menulabel");
|
||||
attr(div1, "class", "menuitem svelte-1oysp7o");
|
||||
attr(div1, "class", "menuitem svelte-1ucacnf");
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, div1, anchor);
|
||||
@@ -939,13 +936,20 @@
|
||||
append(div1, div0);
|
||||
append(div0, t1);
|
||||
append(div1, t2);
|
||||
|
||||
if (!mounted) {
|
||||
dispose = listen(div1, "click", click_handler);
|
||||
mounted = true;
|
||||
}
|
||||
},
|
||||
p(ctx, dirty) {
|
||||
if (/*menuItem*/ ctx[2].Image) {
|
||||
p(new_ctx, dirty) {
|
||||
ctx = new_ctx;
|
||||
|
||||
if (/*menuItem*/ ctx[3].Image) {
|
||||
if (if_block) {
|
||||
if_block.p(ctx, dirty);
|
||||
} else {
|
||||
if_block = create_if_block_2(ctx);
|
||||
if_block = create_if_block_3(ctx);
|
||||
if_block.c();
|
||||
if_block.m(div1, t0);
|
||||
}
|
||||
@@ -954,18 +958,93 @@
|
||||
if_block = null;
|
||||
}
|
||||
|
||||
if (dirty & /*menu*/ 1 && t1_value !== (t1_value = /*menuItem*/ ctx[2].Label + "")) set_data(t1, t1_value);
|
||||
if (dirty & /*menu*/ 1 && t1_value !== (t1_value = /*menuItem*/ ctx[3].Label + "")) set_data(t1, t1_value);
|
||||
},
|
||||
d(detaching) {
|
||||
if (detaching) detach(div1);
|
||||
if (if_block) if_block.d();
|
||||
mounted = false;
|
||||
dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// (20:12) {#if menuItem.Image }
|
||||
function create_if_block_3(ctx) {
|
||||
let div;
|
||||
let img;
|
||||
let img_src_value;
|
||||
|
||||
return {
|
||||
c() {
|
||||
div = element("div");
|
||||
img = element("img");
|
||||
attr(img, "alt", "");
|
||||
if (img.src !== (img_src_value = "data:image/png;base64," + /*menuItem*/ ctx[3].Image)) attr(img, "src", img_src_value);
|
||||
attr(img, "class", "svelte-1ucacnf");
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, div, anchor);
|
||||
append(div, img);
|
||||
},
|
||||
p(ctx, dirty) {
|
||||
if (dirty & /*menu*/ 1 && img.src !== (img_src_value = "data:image/png;base64," + /*menuItem*/ ctx[3].Image)) {
|
||||
attr(img, "src", img_src_value);
|
||||
}
|
||||
},
|
||||
d(detaching) {
|
||||
if (detaching) detach(div);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// (17:8) {#each menu.Menu.Items as menuItem}
|
||||
function create_each_block(ctx) {
|
||||
let if_block_anchor;
|
||||
|
||||
function select_block_type(ctx, dirty) {
|
||||
if (/*menuItem*/ ctx[3].Type === "Text") return create_if_block_2;
|
||||
if (/*menuItem*/ ctx[3].Type === "Separator") return create_if_block_4;
|
||||
}
|
||||
|
||||
let current_block_type = select_block_type(ctx);
|
||||
let if_block = current_block_type && current_block_type(ctx);
|
||||
|
||||
return {
|
||||
c() {
|
||||
if (if_block) if_block.c();
|
||||
if_block_anchor = empty();
|
||||
},
|
||||
m(target, anchor) {
|
||||
if (if_block) if_block.m(target, anchor);
|
||||
insert(target, if_block_anchor, anchor);
|
||||
},
|
||||
p(ctx, dirty) {
|
||||
if (current_block_type === (current_block_type = select_block_type(ctx)) && if_block) {
|
||||
if_block.p(ctx, dirty);
|
||||
} else {
|
||||
if (if_block) if_block.d(1);
|
||||
if_block = current_block_type && current_block_type(ctx);
|
||||
|
||||
if (if_block) {
|
||||
if_block.c();
|
||||
if_block.m(if_block_anchor.parentNode, if_block_anchor);
|
||||
}
|
||||
}
|
||||
},
|
||||
d(detaching) {
|
||||
if (if_block) {
|
||||
if_block.d(detaching);
|
||||
}
|
||||
|
||||
if (detaching) detach(if_block_anchor);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function create_fragment$1(ctx) {
|
||||
let if_block_anchor;
|
||||
let if_block = !/*hidden*/ ctx[1] && create_if_block$1(ctx);
|
||||
let if_block = /*visible*/ ctx[1] && create_if_block$1(ctx);
|
||||
|
||||
return {
|
||||
c() {
|
||||
@@ -977,7 +1056,7 @@
|
||||
insert(target, if_block_anchor, anchor);
|
||||
},
|
||||
p(ctx, [dirty]) {
|
||||
if (!/*hidden*/ ctx[1]) {
|
||||
if (/*visible*/ ctx[1]) {
|
||||
if (if_block) {
|
||||
if_block.p(ctx, dirty);
|
||||
} else {
|
||||
@@ -999,23 +1078,29 @@
|
||||
};
|
||||
}
|
||||
|
||||
function click(id) {
|
||||
console.log("MenuItem", id, "pressed");
|
||||
}
|
||||
|
||||
function instance$1($$self, $$props, $$invalidate) {
|
||||
let { menu } = $$props;
|
||||
let { hidden = true } = $$props;
|
||||
console.log({ menu });
|
||||
let { visible = false } = $$props;
|
||||
const click_handler = menuItem => click(menuItem.ID);
|
||||
|
||||
$$self.$$set = $$props => {
|
||||
if ("menu" in $$props) $$invalidate(0, menu = $$props.menu);
|
||||
if ("hidden" in $$props) $$invalidate(1, hidden = $$props.hidden);
|
||||
if ("visible" in $$props) $$invalidate(1, visible = $$props.visible);
|
||||
};
|
||||
|
||||
return [menu, hidden];
|
||||
return [menu, visible, click_handler];
|
||||
}
|
||||
|
||||
class Menu extends SvelteComponent {
|
||||
constructor(options) {
|
||||
super();
|
||||
if (!document.getElementById("svelte-1oysp7o-style")) add_css$1();
|
||||
init(this, options, instance$1, create_fragment$1, safe_not_equal, { menu: 0, hidden: 1 });
|
||||
if (!document.getElementById("svelte-1ucacnf-style")) add_css$1();
|
||||
init(this, options, instance$1, create_fragment$1, safe_not_equal, { menu: 0, visible: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1030,7 +1115,7 @@
|
||||
append(document_1.head, style);
|
||||
}
|
||||
|
||||
// (47:4) {#if tray.ProcessedMenu }
|
||||
// (48:4) {#if tray.ProcessedMenu }
|
||||
function create_if_block$2(ctx) {
|
||||
let menu;
|
||||
let current;
|
||||
@@ -1038,7 +1123,7 @@
|
||||
menu = new Menu({
|
||||
props: {
|
||||
menu: /*tray*/ ctx[0].ProcessedMenu,
|
||||
hidden: /*hidden*/ ctx[1]
|
||||
visible: /*visible*/ ctx[1]
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1053,7 +1138,7 @@
|
||||
p(ctx, dirty) {
|
||||
const menu_changes = {};
|
||||
if (dirty & /*tray*/ 1) menu_changes.menu = /*tray*/ ctx[0].ProcessedMenu;
|
||||
if (dirty & /*hidden*/ 2) menu_changes.hidden = /*hidden*/ ctx[1];
|
||||
if (dirty & /*visible*/ 2) menu_changes.visible = /*visible*/ ctx[1];
|
||||
menu.$set(menu_changes);
|
||||
},
|
||||
i(local) {
|
||||
@@ -1157,6 +1242,7 @@
|
||||
function clickOutside(node) {
|
||||
const handleClick = event => {
|
||||
if (node && !node.contains(event.target) && !event.defaultPrevented) {
|
||||
console.log("click outside of node");
|
||||
node.dispatchEvent(new CustomEvent("click_outside", node));
|
||||
}
|
||||
};
|
||||
@@ -1171,7 +1257,7 @@
|
||||
}
|
||||
|
||||
function instance$2($$self, $$props, $$invalidate) {
|
||||
let hidden;
|
||||
let visible;
|
||||
let $selectedMenu;
|
||||
component_subscribe($$self, selectedMenu, $$value => $$invalidate(4, $selectedMenu = $$value));
|
||||
let { tray = null } = $$props;
|
||||
@@ -1194,11 +1280,11 @@
|
||||
|
||||
$$self.$$.update = () => {
|
||||
if ($$self.$$.dirty & /*$selectedMenu, tray*/ 17) {
|
||||
$$invalidate(1, hidden = $selectedMenu !== tray);
|
||||
$$invalidate(1, visible = $selectedMenu === tray);
|
||||
}
|
||||
};
|
||||
|
||||
return [tray, hidden, closeMenu, trayClicked, $selectedMenu];
|
||||
return [tray, visible, closeMenu, trayClicked, $selectedMenu];
|
||||
}
|
||||
|
||||
class TrayMenu extends SvelteComponent {
|
||||
|
||||
@@ -2,19 +2,29 @@
|
||||
|
||||
export let menu;
|
||||
|
||||
export let hidden = true;
|
||||
console.log({menu})
|
||||
|
||||
export let visible = false;
|
||||
|
||||
function click(id) {
|
||||
console.log("MenuItem", id, "pressed")
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !hidden}
|
||||
{#if visible}
|
||||
<div class="menu">
|
||||
{#if menu.Menu }
|
||||
{#each menu.Menu.Items as menuItem}
|
||||
<div class="menuitem">
|
||||
{#if menuItem.Type === "Text" }
|
||||
<div class="menuitem" on:click={() => click(menuItem.ID)}>
|
||||
{#if menuItem.Image }
|
||||
<div><img alt="" src="data:image/png;base64,{menuItem.Image}"/></div>
|
||||
{/if}
|
||||
<div class="menulabel">{menuItem.Label}</div>
|
||||
<div class="menulabel">{menuItem.Label}</div>
|
||||
</div>
|
||||
{:else if menuItem.Type === "Separator"}
|
||||
<div class="separator"><hr/></div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
@@ -23,7 +33,7 @@
|
||||
<style>
|
||||
|
||||
.menu {
|
||||
padding: 3px;
|
||||
padding: 5px;
|
||||
background-color: #0008;
|
||||
color: #EEF;
|
||||
border-radius: 5px;
|
||||
@@ -32,7 +42,6 @@
|
||||
backdrop-filter: blur(3px) saturate(160%) contrast(45%) brightness(140%);
|
||||
border: 1px solid rgb(88,88,88);
|
||||
box-shadow: 0 0 1px rgb(146,146,148) inset;
|
||||
|
||||
}
|
||||
|
||||
.menuitem {
|
||||
@@ -53,4 +62,13 @@
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.separator {
|
||||
padding-top: 5px;
|
||||
width: 100%;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.separator:hover {
|
||||
background-color: #0000;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
export let tray = null;
|
||||
|
||||
$: hidden = $selectedMenu !== tray;
|
||||
$: visible = $selectedMenu === tray;
|
||||
|
||||
function closeMenu() {
|
||||
selectedMenu.set(null);
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
const handleClick = event => {
|
||||
if (node && !node.contains(event.target) && !event.defaultPrevented) {
|
||||
console.log("click outside of node")
|
||||
node.dispatchEvent(
|
||||
new CustomEvent('click_outside', node)
|
||||
)
|
||||
@@ -45,7 +46,7 @@
|
||||
<!--{/if}-->
|
||||
<span class="label" on:click={trayClicked}>{tray.Label}</span>
|
||||
{#if tray.ProcessedMenu }
|
||||
<Menu menu="{tray.ProcessedMenu}" {hidden}/>
|
||||
<Menu menu="{tray.ProcessedMenu}" visible="{visible}"/>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
|
||||
@@ -80,4 +80,5 @@ func (m *Manager) Start() {
|
||||
m.logger.Trace("Shutdown")
|
||||
m.wg.Done()
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user