Merge branch 'master' into feature/1521_Support_Tray_Menus

This commit is contained in:
Lea Anthony 2022-10-22 20:35:23 +11:00 committed by GitHub
commit 7969419052
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
273 changed files with 13735 additions and 525 deletions

View file

@ -1,36 +0,0 @@
name: Build
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
jobs:
build-v2:
name: v2 Build
if: github.repository == 'wailsapp/wails'
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Checkout
uses: actions/checkout@v2
with:
ref: "master"
submodules: true
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: ">=1.18.0"
- name: Build Wails CLI
run: |
cd ./v2/cmd/wails
go install
wails -help

View file

@ -17,7 +17,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install linux dependencies
if: matrix.os == 'ubuntu-latest'
@ -43,7 +43,7 @@ jobs:
go-version: [ 1.18, 1.19 ]
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v3

View file

@ -9,14 +9,15 @@ jobs:
if: github.repository == 'wailsapp/wails'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Verify Changed files
uses: tj-actions/verify-changed-files@v11.1
id: verify-changed-files
with:
files: |
v2/website/*.mdx
website/**/*.mdx
website/**/*.md
- name: Run step only when files change.
if: steps.verify-changed-files.outputs.files_changed != 'true'

View file

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'wailsapp/wails'
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set node
uses: actions/setup-node@v2

View file

@ -1,16 +0,0 @@
name: 'issue-translator'
on:
issue_comment:
types: [ created ]
issues:
types: [ opened ]
jobs:
build:
runs-on: ubuntu-latest
if: github.repository == 'wailsapp/wails'
steps:
- uses: tomsun28/issues-translate-action@v2.6
with:
IS_MODIFY_TITLE: false
CUSTOM_BOT_NOTE: The issue body's language is not English, translating it automatically...

View file

@ -28,7 +28,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install linux dependencies
if: matrix.os == 'ubuntu-latest'

38
.github/workflows/push-to-crowdin.yml vendored Normal file
View file

@ -0,0 +1,38 @@
name: Push files to Crowdin
on:
push:
branches: [master]
workflow_dispatch:
jobs:
docs:
name: Push files to Crowdin
if: github.repository == 'wailsapp/wails'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Verify Changed files
uses: tj-actions/verify-changed-files@v11.1
id: verify-changed-files
with:
files: |
website/**/*.mdx
website/**/*.md
website/**/*.json
- name: Set node
if: steps.verify-changed-files.outputs.files_changed != 'true'
uses: actions/setup-node@v2
with:
node-version: 16.x
- name: Push files
env:
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
run: |
cd website
corepack enable
pnpm install
pnpm run crowdin push -b master

View file

@ -10,7 +10,7 @@ jobs:
name: Rebuild the runtime
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v2
with:
node-version: 14.17.6

View file

@ -93,6 +93,7 @@ type devFlags struct {
frontendDevServerURL string
skipFrontend bool
noColour bool
}
// AddSubcommand adds the `dev` command for the Wails application
@ -108,6 +109,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
command.StringFlag("reloaddirs", "Additional directories to trigger reloads (comma separated)", &flags.reloadDirs)
command.BoolFlag("browser", "Open application in browser", &flags.openBrowser)
command.BoolFlag("noreload", "Disable reload on asset change", &flags.noReload)
command.BoolFlag("nocolour", "Turn off colour cli output", &flags.noColour)
command.BoolFlag("skipbindings", "Skip bindings generation", &flags.skipBindings)
command.StringFlag("wailsjsdir", "Directory to generate the Wails JS modules", &flags.wailsjsdir)
command.StringFlag("tags", "Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated", &flags.tags)
@ -123,6 +125,10 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
command.BoolFlag("s", "Skips building the frontend", &flags.skipFrontend)
command.Action(func() error {
if flags.noColour {
colour.ColourEnabled = false
}
// Create logger
logger := clilogger.New(w)
app.PrintBanner()
@ -170,6 +176,11 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
buildOptions.UserTags = userTags
err = build.CreateEmbedDirectories(cwd, buildOptions)
if err != nil {
return err
}
if !buildOptions.SkipBindings {
if flags.verbosity == build.VERBOSE {
LogGreen("Generating Bindings...")
@ -193,7 +204,6 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
// Build the frontend if requested, but ignore building the application itself.
ignoreFrontend := buildOptions.IgnoreFrontend
if !ignoreFrontend {
logger.Println("Building frontend for development...")
buildOptions.IgnoreApplication = true
if _, err := build.Build(buildOptions); err != nil {
return err

View file

@ -1 +1 @@
v2.0.0
v2.1.0

View file

@ -29,9 +29,9 @@ require (
github.com/tkrajina/go-reflector v0.5.5
github.com/wailsapp/mimetype v1.4.1
github.com/wzshiming/ctc v1.2.3
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
)
require (
@ -45,6 +45,7 @@ require (
github.com/samber/lo v1.27.1
github.com/stretchr/testify v1.7.1
golang.org/x/image v0.0.0-20201208152932-35266b937fa6
golang.org/x/tools v0.1.12
)
require (
@ -79,12 +80,11 @@ require (
github.com/valyala/fasttemplate v1.2.1 // indirect
github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae // indirect
github.com/xanzy/ssh-agent v0.3.0 // indirect
github.com/yuin/goldmark v1.4.4 // indirect
github.com/yuin/goldmark v1.4.13 // indirect
github.com/yuin/goldmark-emoji v1.0.1 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View file

@ -175,8 +175,9 @@ github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae/go.mod h1:VTAq37r
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.4 h1:zNWRjYUW32G9KirMXYHQHVNFkXvMI7LpgNW2AgYAoIs=
github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -188,14 +189,14 @@ golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI=
golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 h1:LQmS1nU0twXLA96Kt7U9qtHJEbBk3z6Q0V4UXjZkpr4=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -212,18 +213,19 @@ golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -7,77 +7,133 @@ import (
"github.com/wzshiming/ctc"
)
var ColourEnabled = true
func Col(col ctc.Color, text string) string {
if !ColourEnabled {
return text
}
return fmt.Sprintf("%s%s%s", col, text, ctc.Reset)
}
func Yellow(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundBrightYellow, text)
}
func Red(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundBrightRed, text)
}
func Blue(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundBrightBlue, text)
}
func Green(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundBrightGreen, text)
}
func Cyan(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundBrightCyan, text)
}
func Magenta(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundBrightMagenta, text)
}
func White(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundBrightWhite, text)
}
func Black(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundBrightBlack, text)
}
func DarkYellow(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundYellow, text)
}
func DarkRed(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundRed, text)
}
func DarkBlue(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundBlue, text)
}
func DarkGreen(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundGreen, text)
}
func DarkCyan(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundCyan, text)
}
func DarkMagenta(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundMagenta, text)
}
func DarkWhite(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundWhite, text)
}
func DarkBlack(text string) string {
if !ColourEnabled {
return text
}
return Col(ctc.ForegroundBlack, text)
}
var rainbowCols = []func(string) string{Red, Yellow, Green, Cyan, Blue, Magenta}
func Rainbow(text string) string {
if !ColourEnabled {
return text
}
var builder strings.Builder
for i := 0; i < len(text); i++ {

View file

@ -0,0 +1,237 @@
package combridge
import (
"fmt"
"runtime"
"sync"
"sync/atomic"
)
var (
comIfcePointersL sync.RWMutex
comIfcePointers = map[uintptr]*comObject{} // Map from ComInterfacePointer to the Go ComObject
)
// Resolve the GoInterface of the specified ComInterfacePointer
func Resolve[T IUnknown](ifceP uintptr) T {
comIfcePointersL.RLock()
comObj := comIfcePointers[ifceP]
comIfcePointersL.RUnlock()
var n T
if comObj != nil {
t := comObj.resolve(ifceP)
if t != nil {
n = t.(T)
}
}
return n
}
// New returns a new ComObject which implements the specified Com Interface, com calls will be redirected
// to the specified go interface.
func New[T IUnknown](obj T) *ComObject[T] {
cObj := new(
ifceDef[T]{obj},
)
return newComObject[T](cObj)
}
// New2 returns a new ComObject which implements the two specified Com Interfaces, com calls will be redirected
// to those interfaces accordingly.
// This is needed if a ComObject should implement two interfaces that are not descendants of each other,
// then you get multiple inheritance.
func New2[T IUnknown, T2 IUnknown](obj T, obj2 T2) *ComObject[T] {
cObj := new(
ifceDef[T]{obj},
ifceDef[T2]{obj2},
)
return newComObject[T](cObj)
}
// new returns a new ComObject which implements multiple specified Com Interfaces, com calls will be redirected
// to the specified go interfaces accordingly.
// This is needed if a ComObject should implement multiple interfaces that are not descendants of each other,
// then you get multiple inheritance.
func new(impls ...ifceImpl) *comObject {
impls = append([]ifceImpl{ifceDef[IUnknown]{}}, impls...)
cObj := &comObject{
refCount: 1,
ifces: map[string]int{},
ifcesImpl: make([]comInterfaceDesc, len(impls)),
}
for i, ifceDef := range impls {
vtable, err := ifceDef.ifce()
if err != nil {
panic(err)
}
needsImplement := false
for table := vtable; table != nil; table = table.Parent {
guid := table.ComGUID
if i, found := cObj.ifces[guid]; found {
// This Interface is already implemented
if guid == iUnknownGUID {
// IUnknown is a special interface and never has an user specific implementation
} else if cObj.ifcesImpl[i].impl != ifceDef.impl() {
panic(fmt.Sprintf("Interface '%s' is already implemented by another object", table.Name))
}
break
}
needsImplement = true
cObj.ifces[guid] = i
}
if !needsImplement {
continue
}
ifceP, ifcePSlice := allocUintptrObject(1)
ifcePSlice[0] = vtable.ComVTable
cObj.ifcesImpl[i] = comInterfaceDesc{ifceP, ifceDef.impl()}
}
comIfcePointersL.Lock()
for _, ifceImpl := range cObj.ifcesImpl {
comIfcePointers[ifceImpl.ref] = cObj
}
comIfcePointersL.Unlock()
return cObj
}
func newComObject[T IUnknown](comObj *comObject) *ComObject[T] {
c := &ComObject[T]{obj: comObj}
// Make sure to async release since release needs locks and might block the finalizer goroutine for a longer period
runtime.SetFinalizer(c, func(obj *ComObject[T]) { obj.close(true) })
return c
}
// ComObject describes an exported go instance to be used as a ComObject which implements
// the specified Interface.
type ComObject[T IUnknown] struct {
obj *comObject
closed int32
}
// Ref returns the native uintptr that points to the ComObject that is an interface pointer to T.
// This can be used in native calls. If the object has been closed this function will panic.
func (o *ComObject[T]) Ref() uintptr {
if atomic.LoadInt32(&o.closed) != 0 {
panic("ComObject has been released")
}
return o.obj.queryInterface(guidOf[T](), false)
}
// Close releases the native com object from the go side. It will only be destroyed if the ref counter
// reaches zero.
// After closing `Ref()` will panic.
func (o *ComObject[T]) Close() error {
o.close(false)
return nil
}
// close releases the native com object from the go side. It will only be destroyed if the ref counter
// reaches zero.
// After closing `Ref()` will panic.
func (o *ComObject[T]) close(asyncRelease bool) {
if atomic.CompareAndSwapInt32(&o.closed, 0, 1) {
runtime.SetFinalizer(o, nil)
if asyncRelease {
go o.obj.release()
} else {
o.obj.release()
}
}
}
type comInterfaceDesc struct {
ref uintptr // The native Com InterfacePointer
impl any // The golang target object
}
type comObject struct {
l sync.Mutex
refCount int32
ifces map[string]int // Map of ComInterfaceGUID to Interface Slots
ifcesImpl []comInterfaceDesc // Slots with InterfaceDescriptors
}
func (c *comObject) queryInterface(ifceGUID string, withAddRef bool) uintptr {
c.l.Lock()
defer c.l.Unlock()
if c.refCount <= 0 {
panic("call on released com object")
}
i, found := c.ifces[ifceGUID]
if !found {
return 0
}
if withAddRef {
c.refCount++
}
return c.ifcesImpl[i].ref
}
func (c *comObject) resolve(ifceP uintptr) any {
c.l.Lock()
defer c.l.Unlock()
if c.refCount <= 0 {
panic("call on destroyed com object")
}
for _, ifce := range c.ifcesImpl {
if ifce.ref != ifceP {
continue
}
return ifce.impl
}
return nil
}
func (c *comObject) addRef() int32 {
c.l.Lock()
defer c.l.Unlock()
if c.refCount <= 0 {
panic("call on destroyed com object")
}
c.refCount++
return c.refCount
}
func (c *comObject) release() int32 {
c.l.Lock()
defer c.l.Unlock()
if c.refCount <= 0 {
panic("call on destroyed com object")
}
if c.refCount--; c.refCount == 0 {
comIfcePointersL.Lock()
for _, ref := range c.ifcesImpl {
delete(comIfcePointers, ref.ref)
}
comIfcePointersL.Unlock()
for _, impl := range c.ifcesImpl {
ref := impl.ref
if ref == 0 {
continue
}
globalFree(ref)
}
}
return c.refCount
}

View file

@ -0,0 +1,54 @@
package combridge
import (
"golang.org/x/sys/windows"
)
const iUnknownGUID = "{00000000-0000-0000-C000-000000000046}"
func init() {
registerVTableInternal[IUnknown, IUnknown](
iUnknownGUID,
true,
iUnknownQueryInterface,
iUnknownAddRef,
iUnknownRelease,
)
}
type IUnknown interface{}
func iUnknownQueryInterface(this uintptr, refiid *windows.GUID, ppvObject *uintptr) uintptr {
if refiid == nil || ppvObject == nil {
return uintptr(windows.E_INVALIDARG)
}
comIfcePointersL.RLock()
obj := comIfcePointers[this]
comIfcePointersL.RUnlock()
ref := obj.queryInterface(refiid.String(), true)
if ref != 0 {
*ppvObject = ref
return windows.NO_ERROR
}
*ppvObject = 0
return uintptr(windows.E_NOINTERFACE)
}
func iUnknownAddRef(this uintptr) uintptr {
comIfcePointersL.RLock()
obj := comIfcePointers[this]
comIfcePointersL.RUnlock()
return uintptr(obj.addRef())
}
func iUnknownRelease(this uintptr) uintptr {
comIfcePointersL.RLock()
obj := comIfcePointers[this]
comIfcePointersL.RUnlock()
return uintptr(obj.release())
}

View file

@ -0,0 +1,72 @@
package combridge
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
// IUnknownFromPointer cast a generic pointer into a IUnknownImpl pointer
func IUnknownFromPointer(ref unsafe.Pointer) *IUnknownImpl {
return (*IUnknownImpl)(ref)
}
// IUnknownFromPointer cast native pointer into a IUnknownImpl pointer
func IUnknownFromUintptr(ref uintptr) *IUnknownImpl {
return IUnknownFromPointer(unsafe.Pointer(ref))
}
type IUnknownVtbl struct {
queryInterface uintptr
addRef uintptr
release uintptr
}
func (i *IUnknownVtbl) QueryInterface(this unsafe.Pointer, refiid *windows.GUID, ppvObject **IUnknownImpl) error {
r, _, _ := syscall.SyscallN(
i.queryInterface,
uintptr(this),
uintptr(unsafe.Pointer(refiid)),
uintptr(unsafe.Pointer(ppvObject)),
)
if r != uintptr(windows.S_OK) {
return syscall.Errno(r)
}
return nil
}
func (i *IUnknownVtbl) AddRef(this unsafe.Pointer) uint32 {
r, _, _ := syscall.SyscallN(
i.addRef,
uintptr(this),
)
return uint32(r)
}
func (i *IUnknownVtbl) Release(this unsafe.Pointer) uint32 {
r, _, _ := syscall.SyscallN(
i.release,
uintptr(this),
)
return uint32(r)
}
type IUnknownImpl struct {
vtbl *IUnknownVtbl
}
func (i *IUnknownImpl) QueryInterface(refiid *windows.GUID, ppvObject **IUnknownImpl) error {
return i.vtbl.QueryInterface(unsafe.Pointer(i), refiid, ppvObject)
}
func (i *IUnknownImpl) AddRef() uint32 {
return i.vtbl.AddRef(unsafe.Pointer(i))
}
func (i *IUnknownImpl) Release() uint32 {
return i.vtbl.Release(unsafe.Pointer(i))
}

View file

@ -0,0 +1,37 @@
package combridge
import (
"unsafe"
"golang.org/x/sys/windows"
)
var (
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procGlobalAlloc = modkernel32.NewProc("GlobalAlloc")
procGlobalFree = modkernel32.NewProc("GlobalFree")
uintptrSize = unsafe.Sizeof(uintptr(0))
)
func allocUintptrObject(size int) (uintptr, []uintptr) {
v := globalAlloc(uintptr(size) * uintptrSize)
slice := unsafe.Slice((*uintptr)(unsafe.Pointer(v)), size)
return v, slice
}
func globalAlloc(dwBytes uintptr) uintptr {
ret, _, _ := procGlobalAlloc.Call(uintptr(0), dwBytes)
if ret == 0 {
panic("globalAlloc failed")
}
return ret
}
func globalFree(data uintptr) {
ret, _, _ := procGlobalFree.Call(data)
if ret != 0 {
panic("globalFree failed")
}
}

View file

@ -0,0 +1,145 @@
package combridge
import (
"fmt"
"reflect"
"sync"
"golang.org/x/sys/windows"
)
var (
vTablesL sync.Mutex
vTables = make(map[string]*vTable)
)
// RegisterVTable registers the vtable trampoline methods for the specified ComInterface
// TBase is the base interface of T, and must be another ComInterface which roots in IUnknown or IUnknown itself.
// The first paramter of the fn is always the uintptr of the ComObject and the GoObject can be resolved with Resolve().
// After having resolved the GoObject the call must be redirected to the GoObject.
// Typically a trampoline FN looks like this.
//
// func _ICoreWebView2NavigationCompletedEventHandlerInvoke(this uintptr, sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs) uintptr {
// return combridge.Resolve[_ICoreWebView2NavigationCompletedEventHandler](this).NavigationCompleted(sender, args)
// }
//
// The order of registration must be in the correct order as specified in the IDL of the interface.
func RegisterVTable[TParent, T IUnknown](guid string, fns ...interface{}) {
registerVTableInternal[TParent, T](guid, false, fns...)
}
type vTable struct {
Parent *vTable
Name string
ComGUID string
ComVTable uintptr
ComProcs []uintptr
}
func registerVTableInternal[TParent, T IUnknown](guid string, isInternal bool, fns ...interface{}) {
vTablesL.Lock()
defer vTablesL.Unlock()
t, tName := typeInterfaceToString[T]()
tParent, tParentName := typeInterfaceToString[TParent]()
if !t.Implements(tParent) {
panic(fmt.Errorf("RegisterVTable '%s': '%s' must implement '%s'", tName, tName, tParentName))
}
if !isInternal {
if t == reflect.TypeOf((*IUnknown)(nil)).Elem() {
panic(fmt.Errorf("RegisterVTable '%s' IUnknown can't be registered", tName))
}
if t == tParent {
panic(fmt.Errorf("RegisterVTable '%s': T and TParent can't be the same type", tName))
}
}
var parent *vTable
var parentProcs []uintptr
var parentProcsCount int
if t != tParent {
parent = vTables[tParentName]
if parent == nil {
panic(fmt.Errorf("RegisterVTable '%s': Parent VTable '%s' not registered", tName, tParentName))
}
parentProcs = parent.ComProcs
parentProcsCount = len(parentProcs)
}
comGuid, err := windows.GUIDFromString(guid)
if err != nil {
panic(fmt.Errorf("RegisterVTable '%s': invalid guid: %s", tName, err))
}
vTable := &vTable{
Parent: parent,
Name: tName,
ComGUID: comGuid.String(),
}
vTable.ComVTable, vTable.ComProcs = allocUintptrObject(parentProcsCount + len(fns))
for i, proc := range parentProcs {
vTable.ComProcs[i] = proc
}
for i, fn := range fns {
vTable.ComProcs[parentProcsCount+i] = windows.NewCallback(fn)
}
vTables[tName] = vTable
}
func typeInterfaceToString[T any]() (reflect.Type, string) {
t := reflect.TypeOf((*T)(nil))
if t.Kind() != reflect.Pointer {
panic("must be a (*yourInterfaceType)(nil)")
}
t = t.Elem()
return t, t.PkgPath() + "/" + t.Name()
}
func typeInterfaceToStringOnly[T any]() string {
_, nane := typeInterfaceToString[T]()
return nane
}
func guidOf[T any]() string {
vtable := vTableOf[T]()
if vtable == nil {
return ""
}
return vtable.ComGUID
}
func vTableOf[T any]() *vTable {
name := typeInterfaceToStringOnly[T]()
vTablesL.Lock()
defer vTablesL.Unlock()
return vTables[name]
}
type ifceImpl interface {
impl() any
ifce() (*vTable, error)
}
type ifceDef[T any] struct {
objImpl any
}
func (i ifceDef[T]) impl() any {
return i.objImpl
}
func (i ifceDef[T]) ifce() (*vTable, error) {
vtable := vTableOf[T]()
if vtable == nil {
return nil, fmt.Errorf("Unable to find vTable for %s", typeInterfaceToStringOnly[T]())
}
return vtable, nil
}

View file

@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"sync/atomic"
"syscall"
"unsafe"
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32"
@ -86,34 +87,18 @@ func (e *Chromium) Embed(hwnd uintptr) bool {
dataPath = filepath.Join(os.Getenv("AppData"), currentExeName)
}
var browserPathPtr *uint16 = nil
if e.BrowserPath != "" {
if _, err := os.Stat(e.BrowserPath); !errors.Is(err, os.ErrNotExist) {
browserPathPtr, err = windows.UTF16PtrFromString(e.BrowserPath)
if err != nil {
log.Printf("Error calling UTF16PtrFromString for %s: %v", e.BrowserPath, err)
return false
}
} else {
if _, err := os.Stat(e.BrowserPath); errors.Is(err, os.ErrNotExist) {
log.Printf("Browser path %s does not exist", e.BrowserPath)
return false
}
}
dataPathPtr, err := windows.UTF16PtrFromString(dataPath)
if err != nil {
log.Printf("Error calling UTF16PtrFromString for %s: %v", dataPath, err)
if err := createCoreWebView2EnvironmentWithOptions(e.BrowserPath, dataPath, e.envCompleted); err != nil {
log.Printf("Error calling Webview2Loader: %v", err)
return false
}
res, err := createCoreWebView2EnvironmentWithOptions(browserPathPtr, dataPathPtr, 0, e.envCompleted)
if err != nil {
log.Printf("Error calling Webview2Loader: %v", err)
return false
} else if res != 0 {
log.Printf("Result: %08x", res)
return false
}
var msg w32.Msg
for {
if atomic.LoadUintptr(&e.inited) != 0 {
@ -185,8 +170,8 @@ func (e *Chromium) Release() uintptr {
}
func (e *Chromium) EnvironmentCompleted(res uintptr, env *ICoreWebView2Environment) uintptr {
if int64(res) < 0 {
log.Fatalf("Creating environment failed with %08x", res)
if int32(res) < 0 {
log.Fatalf("Creating environment failed with %08x: %s", res, syscall.Errno(res))
}
env.vtbl.AddRef.Call(uintptr(unsafe.Pointer(env)))
e.environment = env
@ -200,8 +185,8 @@ func (e *Chromium) EnvironmentCompleted(res uintptr, env *ICoreWebView2Environme
}
func (e *Chromium) CreateCoreWebView2ControllerCompleted(res uintptr, controller *ICoreWebView2Controller) uintptr {
if int64(res) < 0 {
log.Fatalf("Creating controller failed with %08x", res)
if int32(res) < 0 {
log.Fatalf("Creating controller failed with %08x: %s", res, syscall.Errno(res))
}
controller.vtbl.AddRef.Call(uintptr(unsafe.Pointer(controller)))
e.controller = controller

View file

@ -11,7 +11,6 @@ import (
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32"
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/webviewloader"
"golang.org/x/sys/windows"
)
@ -48,15 +47,6 @@ const (
CoreWebView2PermissionStateDeny
)
func createCoreWebView2EnvironmentWithOptions(browserExecutableFolder, userDataFolder *uint16, environmentOptions uintptr, environmentCompletedHandle *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) (uintptr, error) {
return webviewloader.CreateCoreWebView2EnvironmentWithOptions(
browserExecutableFolder,
userDataFolder,
environmentOptions,
uintptr(unsafe.Pointer(environmentCompletedHandle)),
)
}
// ComProc stores a COM procedure.
type ComProc uintptr
@ -65,8 +55,9 @@ func NewComProc(fn interface{}) ComProc {
return ComProc(windows.NewCallback(fn))
}
//go:uintptrescapes
// Call calls a COM procedure.
//
//go:uintptrescapes
func (p ComProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
// The magic uintptrescapes comment is needed to prevent moving uintptr(unsafe.Pointer(p)) so calls to .Call() also
// satisfy the unsafe.Pointer rule "(4) Conversion of a Pointer to a uintptr when calling syscall.Syscall."

View file

@ -0,0 +1,28 @@
//go:build exp_gowebview2loader
package edge
import (
"unsafe"
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/webviewloader"
)
func createCoreWebView2EnvironmentWithOptions(browserExecutableFolder, userDataFolder string, environmentCompletedHandle *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) error {
e := &environmentCreatedHandler{environmentCompletedHandle}
return webviewloader.CreateCoreWebView2EnvironmentWithOptions(
e,
webviewloader.WithBrowserExecutableFolder(browserExecutableFolder),
webviewloader.WithUserDataFolder(userDataFolder),
)
}
type environmentCreatedHandler struct {
originalHandler *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler
}
func (r *environmentCreatedHandler) EnvironmentCompleted(errorCode webviewloader.HRESULT, createdEnvironment *webviewloader.ICoreWebView2Environment) webviewloader.HRESULT {
env := (*ICoreWebView2Environment)(unsafe.Pointer(createdEnvironment))
res := r.originalHandler.impl.EnvironmentCompleted(uintptr(errorCode), env)
return webviewloader.HRESULT(res)
}

View file

@ -0,0 +1,41 @@
//go:build !exp_gowebview2loader
package edge
import (
"fmt"
"syscall"
"unsafe"
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/webviewloader"
"golang.org/x/sys/windows"
)
func createCoreWebView2EnvironmentWithOptions(browserExecutableFolder, userDataFolder string, environmentCompletedHandle *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) error {
browserPathPtr, err := windows.UTF16PtrFromString(browserExecutableFolder)
if err != nil {
return fmt.Errorf("Error calling UTF16PtrFromString for %s: %v", browserExecutableFolder, err)
}
userPathPtr, err := windows.UTF16PtrFromString(userDataFolder)
if err != nil {
return fmt.Errorf("Error calling UTF16PtrFromString for %s: %v", userDataFolder, err)
}
hr, err := webviewloader.CreateCoreWebView2EnvironmentWithOptions(
browserPathPtr,
userPathPtr,
0,
uintptr(unsafe.Pointer(environmentCompletedHandle)),
)
if hr != 0 {
if err == nil || err == windows.ERROR_SUCCESS {
err = syscall.Errno(hr)
}
return err
}
return nil
}

View file

@ -1,15 +0,0 @@
# GoWebView2Loader
GoWebView2Loader is a port of [OpenWebView2Loader](https://github.com/jchv/OpenWebView2Loader) to Go.
It is intended to be feature-complete in the near future with the original WebView2Loader distributed with
the WebView2 NuGet package.
## Status
- [ ] CompareBrowserVersions
- [ ] CreateCoreWebView2Environment
- [ ] CreateCoreWebView2EnvironmentWithOptions
- [ ] GetAvailableCoreWebView2BrowserVersionString
- [ ] Feature Complete
- [x] Fixed Runtime support

View file

@ -0,0 +1,19 @@
# Webviewloader
Webviewloader is a port of [OpenWebView2Loader](https://github.com/jchv/OpenWebView2Loader) to Go.
It is intended to be feature-complete with the original WebView2Loader distributed with
the WebView2 NuGet package, but some features are intentionally not implemented.
## Status
- [x] CompareBrowserVersions
- [x] CreateCoreWebView2Environment
- [x] CreateCoreWebView2EnvironmentWithOptions
- [x] GetAvailableCoreWebView2BrowserVersionString
## Not implemented features
- Registry Overrides of Parameters
- Env Variable Overrides of Parameters
- Does not incorporate `GetCurrentPackageInfo` to search for an installed runtime

View file

@ -0,0 +1,159 @@
//go:build exp_gowebview2loader
package webviewloader
import (
"fmt"
"path/filepath"
"syscall"
"unsafe"
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/pkg/combridge"
"golang.org/x/sys/windows"
)
func init() {
fmt.Println("DEB | Using experimental go webview2loader")
}
type webView2RunTimeType int32
const (
webView2RunTimeTypeInstalled webView2RunTimeType = 0x00
webView2RunTimeTypeRedistributable webView2RunTimeType = 0x01
)
// CreateCoreWebView2Environment creates an evergreen WebView2 Environment using the installed WebView2 Runtime version.
//
// This is equivalent to running CreateCoreWebView2EnvironmentWithOptions without any options.
// For more information, see CreateCoreWebView2EnvironmentWithOptions.
func CreateCoreWebView2Environment(environmentCompletedHandler ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) error {
return CreateCoreWebView2EnvironmentWithOptions(environmentCompletedHandler)
}
// CreateCoreWebView2EnvironmentWithOptions creates an environment with a custom version of WebView2 Runtime,
// user data folder, and with or without additional options.
//
// See https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?#createcorewebview2environmentwithoptions
func CreateCoreWebView2EnvironmentWithOptions(environmentCompletedHandler ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, opts ...option) error {
var params environmentOptions
for _, opt := range opts {
opt(&params)
}
var err error
var dllPath string
var runtimeType webView2RunTimeType
if browserExecutableFolder := params.browserExecutableFolder; browserExecutableFolder != "" {
runtimeType = webView2RunTimeTypeRedistributable
dllPath, err = findEmbeddedClientDll(browserExecutableFolder)
} else {
runtimeType = webView2RunTimeTypeInstalled
dllPath, _, err = findInstalledClientDll(params.preferCanary)
}
if err != nil {
return err
}
return createWebViewEnvironmentWithClientDll(dllPath, runtimeType, params.userDataFolder,
&params, environmentCompletedHandler)
}
func createWebViewEnvironmentWithClientDll(lpLibFileName string, runtimeType webView2RunTimeType, userDataFolder string,
envOptions *environmentOptions, envCompletedHandler ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) error {
if !filepath.IsAbs(lpLibFileName) {
return fmt.Errorf("lpLibFileName must be absolute")
}
dll, err := windows.LoadDLL(lpLibFileName)
if err != nil {
return fmt.Errorf("Loading DLL failed: %w", err)
}
defer func() {
canUnloadProc, err := dll.FindProc("DllCanUnloadNow")
if err != nil {
return
}
if r1, _, _ := canUnloadProc.Call(); r1 != windows.NO_ERROR {
return
}
dll.Release()
}()
createProc, err := dll.FindProc("CreateWebViewEnvironmentWithOptionsInternal")
if err != nil {
return fmt.Errorf("Unable to find CreateWebViewEnvironmentWithOptionsInternal entrypoint: %w", err)
}
userDataPtr, err := windows.UTF16PtrFromString(userDataFolder)
if err != nil {
return err
}
envOptionsCom := combridge.New2[iCoreWebView2EnvironmentOptions, iCoreWebView2EnvironmentOptions2](
envOptions, envOptions)
defer envOptionsCom.Close()
envCompletedHandler = &environmentCreatedHandler{envCompletedHandler}
envCompletedCom := combridge.New[iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler](envCompletedHandler)
defer envCompletedCom.Close()
const unknown = 1
hr, _, err := createProc.Call(
uintptr(unknown),
uintptr(runtimeType),
uintptr(unsafe.Pointer(userDataPtr)),
uintptr(envOptionsCom.Ref()),
uintptr(envCompletedCom.Ref()))
if hr != 0 {
if err == nil || err == windows.ERROR_SUCCESS {
err = syscall.Errno(hr)
}
return err
}
return nil
}
type environmentCreatedHandler struct {
originalHandler ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler
}
func (r *environmentCreatedHandler) EnvironmentCompleted(errorCode HRESULT, createdEnvironment *ICoreWebView2Environment) HRESULT {
// The OpenWebview2Loader has some retry logic and retries once, didn't encounter any case when this would have been
// needed during the development: https://github.com/jchv/OpenWebView2Loader/blob/master/Source/WebView2Loader.cpp#L202
if createdEnvironment != nil {
// May or may not be necessary, but the official WebView2Loader seems to do it.
iidICoreWebView2Environment := windows.GUID{
Data1: 0xb96d755e,
Data2: 0x0319,
Data3: 0x4e92,
Data4: [8]byte{0xa2, 0x96, 0x23, 0x43, 0x6f, 0x46, 0xa1, 0xfc},
}
if err := createdEnvironment.QueryInterface(&iidICoreWebView2Environment, &createdEnvironment); err != nil {
createdEnvironment = nil
errNo, ok := err.(syscall.Errno)
if !ok {
errNo = syscall.Errno(windows.E_FAIL)
}
errorCode = HRESULT(errNo)
}
}
r.originalHandler.EnvironmentCompleted(errorCode, createdEnvironment)
if createdEnvironment != nil {
createdEnvironment.Release()
}
return HRESULT(windows.S_OK)
}

View file

@ -0,0 +1,42 @@
//go:build exp_gowebview2loader
package webviewloader
import (
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/pkg/combridge"
)
// HRESULT
//
// See https://docs.microsoft.com/en-us/windows/win32/seccrypto/common-hresult-values
type HRESULT int32
// ICoreWebView2Environment Represents the WebView2 Environment
//
// See https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environment
type ICoreWebView2Environment = combridge.IUnknownImpl
// ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler receives the WebView2Environment created using CreateCoreWebView2Environment.
type ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler interface {
// EnvironmentCompleted is invoked to receive the created WebView2Environment
//
// See https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2createcorewebview2environmentcompletedhandler?#invoke
EnvironmentCompleted(errorCode HRESULT, createdEnvironment *ICoreWebView2Environment) HRESULT
}
type iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler interface {
combridge.IUnknown
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler
}
func init() {
combridge.RegisterVTable[combridge.IUnknown, iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler](
"{4e8a3389-c9d8-4bd2-b6b5-124fee6cc14d}",
_iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerInvoke,
)
}
func _iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerInvoke(this uintptr, errorCode HRESULT, env *combridge.IUnknownImpl) uintptr {
res := combridge.Resolve[iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler](this).EnvironmentCompleted(errorCode, env)
return uintptr(res)
}

View file

@ -0,0 +1,276 @@
//go:build exp_gowebview2loader
package webviewloader
import (
"unicode/utf16"
"unsafe"
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/pkg/combridge"
"golang.org/x/sys/windows"
)
// WithBrowserExecutableFolder to specify whether WebView2 controls use a fixed or installed version
// of the WebView2 Runtime that exists on a user machine.
//
// To use a fixed version of the WebView2 Runtime,
// pass the folder path that contains the fixed version of the WebView2 Runtime.
// BrowserExecutableFolder supports both relative (to the application's executable) and absolute files paths.
// To create WebView2 controls that use the installed version of the WebView2 Runtime that exists on user
// machines, pass a empty string to WithBrowserExecutableFolder. In this scenario, the API tries to find a
// compatible version of the WebView2 Runtime that is installed on the user machine (first at the machine level,
// and then per user) using the selected channel preference. The path of fixed version of the WebView2 Runtime
// should not contain \Edge\Application\. When such a path is used, the API fails with HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED).
func WithBrowserExecutableFolder(folder string) option {
return func(wvep *environmentOptions) {
wvep.browserExecutableFolder = folder
}
}
// WithUserDataFolder specifies to user data folder location for WebView2
//
// You may specify the userDataFolder to change the default user data folder location for WebView2.
// The path is either an absolute file path or a relative file path that is interpreted as relative
// to the compiled code for the current process.
// Dhe default user data ({Executable File Name}.WebView2) folder is created in the same directory
// next to the compiled code for the app. WebView2 creation fails if the compiled code is running
// in a directory in which the process does not have permission to create a new directory.
// The app is responsible to clean up the associated user data folder when it is done.
func WithUserDataFolder(folder string) option {
return func(wvep *environmentOptions) {
wvep.userDataFolder = folder
}
}
// WithAdditionalBrowserArguments changes the behavior of the WebView.
//
// The arguments are passed to the
// browser process as part of the command. For more information about
// using command-line switches with Chromium browser processes, navigate to
// [Run Chromium with Flags][ChromiumDevelopersHowTosRunWithFlags].
// The value appended to a switch is appended to the browser process, for
// example, in `--edge-webview-switches=xxx` the value is `xxx`. If you
// specify a switch that is important to WebView functionality, it is
// ignored, for example, `--user-data-dir`. Specific features are disabled
// internally and blocked from being enabled. If a switch is specified
// multiple times, only the last instance is used.
//
// \> [!NOTE]\n\> A merge of the different values of the same switch is not attempted,
// except for disabled and enabled features. The features specified by
// `--enable-features` and `--disable-features` are merged with simple
// logic.\n\> * The features is the union of the specified features
// and built-in features. If a feature is disabled, it is removed from the
// enabled features list.
//
// If you specify command-line switches and use the
// `additionalBrowserArguments` parameter, the `--edge-webview-switches`
// value takes precedence and is processed last. If a switch fails to
// parse, the switch is ignored. The default state for the operation is
// to run the browser process with no extra flags.
//
// [ChromiumDevelopersHowTosRunWithFlags]: https://www.chromium.org/developers/how-tos/run-chromium-with-flags "Run Chromium with flags | The Chromium Projects"
func WithAdditionalBrowserArguments(args string) option {
return func(wvep *environmentOptions) {
wvep.additionalBrowserArguments = args
}
}
// WithLanguage sets the default display language for WebView.
//
// It applies to browser UI such as
// context menu and dialogs. It also applies to the `accept-languages` HTTP
// header that WebView sends to websites. It is in the format of
//
// `language[-country]` where `language` is the 2-letter code from
// [ISO 639][ISO639LanguageCodesHtml]
// and `country` is the
// 2-letter code from
// [ISO 3166][ISOStandard72482Html].
//
// [ISO639LanguageCodesHtml]: https://www.iso.org/iso-639-language-codes.html "ISO 639 | ISO"
// [ISOStandard72482Html]: https://www.iso.org/standard/72482.html "ISO 3166-1:2020 | ISO"
func WithLanguage(lang string) option {
return func(wvep *environmentOptions) {
wvep.language = lang
}
}
// WithTargetCompatibleBrowserVersion secifies the version of the WebView2 Runtime binaries required to be
// compatible with your app.
//
// This defaults to the WebView2 Runtime version
// that corresponds with the version of the SDK the app is using. The
// format of this value is the same as the format of the
// `BrowserVersionString` property and other `BrowserVersion` values. Only
// the version part of the `BrowserVersion` value is respected. The channel
// suffix, if it exists, is ignored. The version of the WebView2 Runtime
// binaries actually used may be different from the specified
// `TargetCompatibleBrowserVersion`. The binaries are only guaranteed to be
// compatible. Verify the actual version on the `BrowserVersionString`
// property on the `ICoreWebView2Environment`.
func WithTargetCompatibleBrowserVersion(version string) option {
return func(wvep *environmentOptions) {
wvep.targetCompatibleBrowserVersion = version
}
}
// WithAllowSingleSignOnUsingOSPrimaryAccount is used to enable
// single sign on with Azure Active Directory (AAD) and personal Microsoft
// Account (MSA) resources inside WebView. All AAD accounts, connected to
// Windows and shared for all apps, are supported. For MSA, SSO is only enabled
// for the account associated for Windows account login, if any.
// Default is disabled. Universal Windows Platform apps must also declare
// `enterpriseCloudSSO`
// [Restricted capabilities][WindowsUwpPackagingAppCapabilityDeclarationsRestrictedCapabilities]
// for the single sign on (SSO) to work.
//
// [WindowsUwpPackagingAppCapabilityDeclarationsRestrictedCapabilities]: /windows/uwp/packaging/app-capability-declarations\#restricted-capabilities "Restricted capabilities - App capability declarations | Microsoft Docs"
func WithAllowSingleSignOnUsingOSPrimaryAccount(allow bool) option {
return func(wvep *environmentOptions) {
wvep.allowSingleSignOnUsingOSPrimaryAccount = allow
}
}
// WithExclusiveUserDataFolderAccess specifies that the WebView environment
// obtains exclusive access to the user data folder.
//
// If the user data folder is already being used by another WebView environment with a
// different value for `ExclusiveUserDataFolderAccess` property, the creation of a WebView2Controller
// using the environment object will fail with `HRESULT_FROM_WIN32(ERROR_INVALID_STATE)`.
// When set as TRUE, no other WebView can be created from other processes using WebView2Environment
// objects with the same UserDataFolder. This prevents other processes from creating WebViews
// which share the same browser process instance, since sharing is performed among
// WebViews that have the same UserDataFolder. When another process tries to create a
// WebView2Controller from an WebView2Environment object created with the same user data folder,
// it will fail with `HRESULT_FROM_WIN32(ERROR_INVALID_STATE)`.
func WithExclusiveUserDataFolderAccess(exclusive bool) option {
return func(wvep *environmentOptions) {
wvep.exclusiveUserDataFolderAccess = exclusive
}
}
type option func(*environmentOptions)
var _ iCoreWebView2EnvironmentOptions = &environmentOptions{}
var _ iCoreWebView2EnvironmentOptions2 = &environmentOptions{}
type environmentOptions struct {
browserExecutableFolder string
userDataFolder string
preferCanary bool
additionalBrowserArguments string
language string
targetCompatibleBrowserVersion string
allowSingleSignOnUsingOSPrimaryAccount bool
exclusiveUserDataFolderAccess bool
}
func (o *environmentOptions) AdditionalBrowserArguments() string {
return o.additionalBrowserArguments
}
func (o *environmentOptions) Language() string {
return o.language
}
func (o *environmentOptions) TargetCompatibleBrowserVersion() string {
v := o.targetCompatibleBrowserVersion
if v == "" {
v = kMinimumCompatibleVersion
}
return v
}
func (o *environmentOptions) AllowSingleSignOnUsingOSPrimaryAccount() bool {
return o.allowSingleSignOnUsingOSPrimaryAccount
}
func (o *environmentOptions) ExclusiveUserDataFolderAccess() bool {
return o.exclusiveUserDataFolderAccess
}
type iCoreWebView2EnvironmentOptions interface {
combridge.IUnknown
AdditionalBrowserArguments() string
Language() string
TargetCompatibleBrowserVersion() string
AllowSingleSignOnUsingOSPrimaryAccount() bool
}
type iCoreWebView2EnvironmentOptions2 interface {
combridge.IUnknown
ExclusiveUserDataFolderAccess() bool
}
func init() {
combridge.RegisterVTable[combridge.IUnknown, iCoreWebView2EnvironmentOptions](
"{2fde08a8-1e9a-4766-8c05-95a9ceb9d1c5}",
_iCoreWebView2EnvironmentOptionsAdditionalBrowserArguments,
_iCoreWebView2EnvironmentOptionsNOP,
_iCoreWebView2EnvironmentOptionsLanguage,
_iCoreWebView2EnvironmentOptionsNOP,
_iCoreWebView2EnvironmentTargetCompatibleBrowserVersion,
_iCoreWebView2EnvironmentOptionsNOP,
_iCoreWebView2EnvironmentOptionsAllowSingleSignOnUsingOSPrimaryAccount,
_iCoreWebView2EnvironmentOptionsNOP,
)
combridge.RegisterVTable[combridge.IUnknown, iCoreWebView2EnvironmentOptions2](
"{ff85c98a-1ba7-4a6b-90c8-2b752c89e9e2}",
_iCoreWebView2EnvironmentOptions2ExclusiveUserDataFolderAccess,
_iCoreWebView2EnvironmentOptionsNOP,
)
}
func _iCoreWebView2EnvironmentOptionsNOP(this uintptr) uintptr {
return uintptr(windows.S_FALSE)
}
func _iCoreWebView2EnvironmentOptionsAdditionalBrowserArguments(this uintptr, value **uint16) uintptr {
v := combridge.Resolve[iCoreWebView2EnvironmentOptions](this).AdditionalBrowserArguments()
*value = stringToOleString(v)
return uintptr(windows.S_OK)
}
func _iCoreWebView2EnvironmentOptionsLanguage(this uintptr, value **uint16) uintptr {
args := combridge.Resolve[iCoreWebView2EnvironmentOptions](this).Language()
*value = stringToOleString(args)
return uintptr(windows.S_OK)
}
func _iCoreWebView2EnvironmentTargetCompatibleBrowserVersion(this uintptr, value **uint16) uintptr {
args := combridge.Resolve[iCoreWebView2EnvironmentOptions](this).TargetCompatibleBrowserVersion()
*value = stringToOleString(args)
return uintptr(windows.S_OK)
}
func _iCoreWebView2EnvironmentOptionsAllowSingleSignOnUsingOSPrimaryAccount(this uintptr, value *int32) uintptr {
v := combridge.Resolve[iCoreWebView2EnvironmentOptions](this).AllowSingleSignOnUsingOSPrimaryAccount()
*value = boolToInt(v)
return uintptr(windows.S_OK)
}
func _iCoreWebView2EnvironmentOptions2ExclusiveUserDataFolderAccess(this uintptr, value *int32) uintptr {
v := combridge.Resolve[iCoreWebView2EnvironmentOptions2](this).ExclusiveUserDataFolderAccess()
*value = boolToInt(v)
return uintptr(windows.S_OK)
}
func stringToOleString(v string) *uint16 {
wstr := utf16.Encode([]rune(v + "\x00"))
lwstr := len(wstr)
ptr := (*uint16)(coTaskMemAlloc(2 * lwstr))
copy(unsafe.Slice(ptr, lwstr), wstr)
return ptr
}
func boolToInt(v bool) int32 {
if v {
return 1
}
return 0
}

View file

@ -1,25 +1,18 @@
package webview2loader
package webviewloader
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"golang.org/x/sys/windows/registry"
)
// GetAvailableCoreWebView2BrowserVersionString get the browser version info including channel name.
func GetAvailableCoreWebView2BrowserVersionString(browserExecutableFolder string) (string, error) {
if browserExecutableFolder != "" {
clientPath, err := findEmbeddedClientDll(browserExecutableFolder)
if err != nil {
return "", err
}
return findEmbeddedBrowserVersion(clientPath)
}
return "", fmt.Errorf("not implemented yet for empty browserExecutableFolder ")
}
var (
errNoClientDLLFound = errors.New("no webview2 found")
)
func findEmbeddedBrowserVersion(filename string) (string, error) {
block, err := getFileVersionInfo(filename)
@ -63,7 +56,17 @@ func findClientDllInFolder(folder string) (string, error) {
dllPath := filepath.Join(folder, "EBWebView", arch, "EmbeddedBrowserWebView.dll")
if _, err := os.Stat(dllPath); err != nil {
return "", err
return "", mapFindErr(err)
}
return dllPath, nil
}
func mapFindErr(err error) error {
if errors.Is(err, registry.ErrNotExist) {
return errNoClientDLLFound
}
if errors.Is(err, os.ErrNotExist) {
return errNoClientDLLFound
}
return err
}

View file

@ -0,0 +1,94 @@
//go:build exp_gowebview2loader
package webviewloader
import (
"path/filepath"
"golang.org/x/sys/windows/registry"
)
const (
kNumChannels = 4
kInstallKeyPath = "Software\\Microsoft\\EdgeUpdate\\ClientState\\"
kMinimumCompatibleVersion = "86.0.616.0"
)
var (
kChannelName = [kNumChannels]string{
"", "beta", "dev", "canary", // "internal"
}
kChannelUuid = [kNumChannels]string{
"{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}",
"{2CD8A007-E189-409D-A2C8-9AF4EF3C72AA}",
"{0D50BFEC-CD6A-4F9A-964C-C7416E3ACB10}",
"{65C35B14-6C1D-4122-AC46-7148CC9D6497}",
//"{BE59E8FD-089A-411B-A3B0-051D9E417818}",
}
minimumCompatibleVersion, _ = parseVersion(kMinimumCompatibleVersion)
)
func findInstalledClientDll(preferCanary bool) (clientPath string, version *version, err error) {
for i := 0; i < kNumChannels; i++ {
channel := i
if preferCanary {
channel = (kNumChannels - 1) - i
}
key := kInstallKeyPath + kChannelUuid[channel]
for _, checkSystem := range []bool{true, false} {
clientPath, version, err := findInstalledClientDllForChannel(key, checkSystem)
if err == errNoClientDLLFound {
continue
}
if err != nil {
return "", nil, err
}
version.channel = kChannelName[channel]
return clientPath, version, nil
}
}
return "", nil, errNoClientDLLFound
}
func findInstalledClientDllForChannel(subKey string, system bool) (clientPath string, clientVersion *version, err error) {
key := registry.LOCAL_MACHINE
if !system {
key = registry.CURRENT_USER
}
regKey, err := registry.OpenKey(key, subKey, registry.READ|registry.WOW64_32KEY)
if err != nil {
return "", nil, mapFindErr(err)
}
defer regKey.Close()
embeddedEdgeSubFolder, _, err := regKey.GetStringValue("EBWebView")
if err != nil {
return "", nil, mapFindErr(err)
}
if embeddedEdgeSubFolder == "" {
return "", nil, errNoClientDLLFound
}
versionString := filepath.Base(embeddedEdgeSubFolder)
version, err := parseVersion(versionString)
if err != nil {
return "", nil, errNoClientDLLFound
}
if version.compare(minimumCompatibleVersion) < 0 {
return "", nil, errNoClientDLLFound
}
dllPath, err := findEmbeddedClientDll(embeddedEdgeSubFolder)
if err != nil {
return "", nil, mapFindErr(err)
}
return dllPath, &version, nil
}

View file

@ -1,3 +1,5 @@
//go:build !exp_gowebview2loader
package webviewloader
import (
@ -8,7 +10,6 @@ import (
"unsafe"
"github.com/jchv/go-winloader"
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/webview2loader"
"golang.org/x/sys/windows"
)
@ -63,20 +64,23 @@ func CompareBrowserVersions(v1 string, v2 string) (int, error) {
return int(result), nil
}
// GetWebviewVersion returns version of the webview2 runtime.
// GetAvailableCoreWebView2BrowserVersionString returns version of the webview2 runtime.
// If path is empty, it will try to find installed webview2 is the system.
// If there is no version installed, a blank string is returned.
func GetWebviewVersion(path string) (string, error) {
func GetAvailableCoreWebView2BrowserVersionString(path string) (string, error) {
if path != "" {
// The default implementation fails if CGO and a fixed browser path is used. It's caused by the go-winloader
// which loads the native DLL from memory.
// Use the new GoWebView2Loader in this case, in the future we will make GoWebView2Loader
// feature-complete and remove the use of the native DLL and go-winloader.
version, err := webview2loader.GetAvailableCoreWebView2BrowserVersionString(path)
if errors.Is(err, os.ErrNotExist) {
version, err := goGetAvailableCoreWebView2BrowserVersionString(path)
if errors.Is(err, errNoClientDLLFound) {
// Webview2 is not found
return "", nil
} else if err != nil {
return "", err
}
return version, nil
}
@ -158,3 +162,12 @@ func preventEnvAndRegistryOverrides(browserFolder, userDataFolder *uint16) {
os.Setenv("WEBVIEW2_BROWSER_EXECUTABLE_FOLDER", windows.UTF16PtrToString(browserFolder))
os.Setenv("WEBVIEW2_USER_DATA_FOLDER", windows.UTF16PtrToString(userDataFolder))
}
func goGetAvailableCoreWebView2BrowserVersionString(browserExecutableFolder string) (string, error) {
clientPath, err := findEmbeddedClientDll(browserExecutableFolder)
if err != nil {
return "", err
}
return findEmbeddedBrowserVersion(clientPath)
}

View file

@ -1,4 +1,4 @@
package webview2loader
package webviewloader
import (
"fmt"
@ -18,6 +18,9 @@ var (
procGetFileVersionInfoSize = modversion.NewProc("GetFileVersionInfoSizeW")
procGetFileVersionInfo = modversion.NewProc("GetFileVersionInfoW")
procVerQueryValue = modversion.NewProc("VerQueryValueW")
modole32 = windows.NewLazySystemDLL("ole32.dll")
procCoTaskMemAlloc = modole32.NewProc("CoTaskMemAlloc")
)
func getFileVersionInfo(path string) ([]byte, error) {
@ -126,3 +129,13 @@ func maskErrorSuccess(err error) error {
}
return err
}
func coTaskMemAlloc(size int) unsafe.Pointer {
ret, _, _ := procCoTaskMemAlloc.Call(
uintptr(size))
if ret == 0 {
panic("coTaskMemAlloc failed")
}
return unsafe.Pointer(ret)
}

View file

@ -0,0 +1,147 @@
//go:build exp_gowebview2loader
package webviewloader
import (
"errors"
"fmt"
"strconv"
"strings"
)
// CompareBrowserVersions will compare the 2 given versions and return:
//
// -1 = v1 < v2
// 0 = v1 == v2
// 1 = v1 > v2
func CompareBrowserVersions(v1 string, v2 string) (int, error) {
v, err := parseVersion(v1)
if err != nil {
return 0, fmt.Errorf("v1 invalid: %w", err)
}
w, err := parseVersion(v2)
if err != nil {
return 0, fmt.Errorf("v2 invalid: %w", err)
}
return v.compare(w), nil
}
// GetAvailableCoreWebView2BrowserVersionString get the browser version info including channel name
// if it is the WebView2 Runtime.
// Channel names are Beta, Dev, and Canary.
func GetAvailableCoreWebView2BrowserVersionString(browserExecutableFolder string) (string, error) {
if browserExecutableFolder != "" {
clientPath, err := findEmbeddedClientDll(browserExecutableFolder)
if errors.Is(err, errNoClientDLLFound) {
// Webview2 is not found
return "", nil
} else if err != nil {
return "", err
}
return findEmbeddedBrowserVersion(clientPath)
}
_, version, err := findInstalledClientDll(false)
if errors.Is(err, errNoClientDLLFound) {
return "", nil
} else if err != nil {
return "", err
}
return version.String(), nil
}
type version struct {
major int
minor int
patch int
build int
channel string
}
func (v version) String() string {
vv := fmt.Sprintf("%d.%d.%d.%d", v.major, v.minor, v.patch, v.build)
if v.channel != "" {
vv += " " + v.channel
}
return vv
}
func (v version) compare(o version) int {
if c := compareInt(v.major, o.major); c != 0 {
return c
}
if c := compareInt(v.minor, o.minor); c != 0 {
return c
}
if c := compareInt(v.patch, o.patch); c != 0 {
return c
}
return compareInt(v.build, o.build)
}
func parseVersion(v string) (version, error) {
var p version
// Split away channel information...
if i := strings.Index(v, " "); i > 0 {
p.channel = v[i+1:]
v = v[:i]
}
vv := strings.Split(v, ".")
if len(vv) > 4 {
return p, fmt.Errorf("too many version parts")
}
var err error
vv, p.major, err = parseInt(vv)
if err != nil {
return p, fmt.Errorf("bad major version: %w", err)
}
vv, p.minor, err = parseInt(vv)
if err != nil {
return p, fmt.Errorf("bad minor version: %w", err)
}
vv, p.patch, err = parseInt(vv)
if err != nil {
return p, fmt.Errorf("bad patch version: %w", err)
}
_, p.build, err = parseInt(vv)
if err != nil {
return p, fmt.Errorf("bad build version: %w", err)
}
return p, nil
}
func parseInt(v []string) ([]string, int, error) {
if len(v) == 0 {
return nil, 0, nil
}
p, err := strconv.ParseInt(v[0], 10, 32)
if err != nil {
return nil, 0, err
}
return v[1:], int(p), nil
}
func compareInt(v1, v2 int) int {
if v1 == v2 {
return 0
}
if v1 < v2 {
return -1
} else {
return +1
}
}

View file

@ -87,7 +87,11 @@ window.addEventListener('mouseup', () => {
});
let dragTest = function (e) {
return window.getComputedStyle(e.target).getPropertyValue(window.wails.flags.cssDragProperty) === window.wails.flags.cssDragValue;
var val = window.getComputedStyle(e.target).getPropertyValue(window.wails.flags.cssDragProperty);
if (val) {
val = val.trim();
}
return val === window.wails.flags.cssDragValue;
};
window.wails.setCSSDragProperties = function (property, value) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,76 @@
package staticanalysis
import (
"go/ast"
"golang.org/x/tools/go/packages"
"path/filepath"
"strings"
)
type EmbedDetails struct {
BaseDir string
EmbedPath string
All bool
}
func (e *EmbedDetails) GetFullPath() string {
return filepath.Join(e.BaseDir, e.EmbedPath)
}
func GetEmbedDetails(sourcePath string) ([]*EmbedDetails, error) {
// read in project files and determine which directories are used for embedding
// return a list of directories
absPath, err := filepath.Abs(sourcePath)
if err != nil {
return nil, err
}
pkgs, err := packages.Load(&packages.Config{
Mode: packages.NeedName | packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedCompiledGoFiles,
Dir: absPath,
}, "./...")
if err != nil {
return nil, err
}
var result []*EmbedDetails
for _, pkg := range pkgs {
for index, file := range pkg.Syntax {
baseDir := filepath.Dir(pkg.CompiledGoFiles[index])
embedPaths := GetEmbedDetailsForFile(file, baseDir)
if len(embedPaths) > 0 {
result = append(result, embedPaths...)
}
}
}
return result, nil
}
func GetEmbedDetailsForFile(file *ast.File, baseDir string) []*EmbedDetails {
var result []*EmbedDetails
for _, comment := range file.Comments {
for _, c := range comment.List {
if strings.HasPrefix(c.Text, "//go:embed") {
sl := strings.Split(c.Text, " ")
path := ""
all := false
if len(sl) == 1 {
continue
}
embedPath := strings.TrimSpace(sl[1])
switch true {
case strings.HasPrefix(embedPath, "all:"):
path = strings.TrimPrefix(embedPath, "all:")
all = true
default:
path = embedPath
}
result = append(result, &EmbedDetails{
EmbedPath: path,
All: all,
BaseDir: baseDir,
})
}
}
}
return result
}

View file

@ -0,0 +1,46 @@
package staticanalysis
import (
"github.com/stretchr/testify/require"
"testing"
)
func TestGetEmbedDetails(t *testing.T) {
type args struct {
sourcePath string
}
tests := []struct {
name string
args args
want []*EmbedDetails
wantErr bool
}{
{
name: "GetEmbedDetails",
args: args{
sourcePath: "test/standard",
},
want: []*EmbedDetails{
{
EmbedPath: "frontend/dist",
All: true,
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetEmbedDetails(tt.args.sourcePath)
if (err != nil) != tt.wantErr {
t.Errorf("GetEmbedDetails() error = %v, wantErr %v", err, tt.wantErr)
return
}
require.Equal(t, len(tt.want), len(got))
for index, g := range got {
require.Equal(t, tt.want[index].EmbedPath, g.EmbedPath)
require.Equal(t, tt.want[index].All, g.All)
}
})
}
}

View file

@ -0,0 +1,3 @@
build/bin
node_modules
frontend/dist

View file

@ -0,0 +1,19 @@
# README
## About
This is the official Wails Vanilla template.
You can configure the project by editing `wails.json`. More information about the project settings can be found
here: https://wails.io/docs/reference/project-config
## Live Development
To run in live development mode, run `wails dev` in the project directory. This will run a Vite development
server that will provide very fast hot reload of your frontend changes. If you want to develop in a browser
and have access to your Go methods, there is also a dev server that runs on http://localhost:34115. Connect
to this in your browser, and you can call your Go code from devtools.
## Building
To build a redistributable, production mode package, use `wails build`.

View file

@ -0,0 +1,27 @@
package main
import (
"context"
"fmt"
)
// App struct
type App struct {
ctx context.Context
}
// NewApp creates a new App application struct
func NewApp() *App {
return &App{}
}
// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}
// Greet returns a greeting for the given name
func (a *App) Greet(name string) string {
return fmt.Sprintf("Hello %s, It's show time!", name)
}

View file

@ -0,0 +1,33 @@
module changeme
go 1.18
require github.com/wailsapp/wails/v2 v2.0.0
require (
github.com/bep/debounce v1.2.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/labstack/echo/v4 v4.9.1 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/leaanthony/go-ansi-parser v1.6.0 // indirect
github.com/leaanthony/gosod v1.0.3 // indirect
github.com/leaanthony/slicer v1.6.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.2 // indirect
github.com/tkrajina/go-reflector v0.5.6 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a // indirect
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 // indirect
golang.org/x/text v0.3.8 // indirect
)
// replace github.com/wailsapp/wails/v2 v2.0.0 => C:\Users\leaan

View file

@ -0,0 +1,109 @@
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/labstack/echo/v4 v4.7.2 h1:Kv2/p8OaQ+M6Ex4eGimg9b9e6icoxA42JSlOR3msKtI=
github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y=
github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo=
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4=
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
github.com/leaanthony/go-ansi-parser v1.6.0 h1:T8TuMhFB6TUMIUm0oRrSbgJudTFw9csT3ZK09w0t4Pg=
github.com/leaanthony/go-ansi-parser v1.6.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js=
github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 h1:acNfDZXmm28D2Yg/c3ALnZStzNaZMSagpbr96vY6Zjc=
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ=
github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
github.com/wailsapp/wails/v2 v2.0.0 h1:EsiIMQYi3XeAJvvJNYTWVSyaEYwuBsSFU9ACSq7Ly4s=
github.com/wailsapp/wails/v2 v2.0.0/go.mod h1:z3os4dBFCOmPhFHjjEKY4KygTVFKAGECkS9/mKm1wEU=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg=
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4=
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View file

@ -0,0 +1,33 @@
package main
import (
"embed"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
)
//go:embed all:frontend/dist
var assets embed.FS
func main() {
// Create an instance of the app structure
app := NewApp()
// Create application with options
err := wails.Run(&options.App{
Title: "staticanalysis",
Width: 1024,
Height: 768,
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())
}
}

View file

@ -0,0 +1,12 @@
{
"name": "staticanalysis",
"outputfilename": "staticanalysis",
"frontend:install": "npm install",
"frontend:build": "npm run build",
"frontend:dev:watcher": "npm run dev",
"frontend:dev:serverUrl": "auto",
"author": {
"name": "Lea Anthony",
"email": "lea.anthony@gmail.com"
}
}

View file

@ -28,7 +28,7 @@ func (i *Info) discover() error {
}
func checkWebView2() *packagemanager.Dependency {
version, _ := webviewloader.GetWebviewVersion("")
version, _ := webviewloader.GetAvailableCoreWebView2BrowserVersionString("")
installed := version != ""
return &packagemanager.Dependency{

View file

@ -4,6 +4,7 @@ package wv2installer
import (
"fmt"
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/webviewloader"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/windows"
@ -32,7 +33,7 @@ func Process(appoptions *options.App) (string, error) {
webviewPath = opts.WebviewBrowserPath
}
installedVersion, err := webviewloader.GetWebviewVersion(webviewPath)
installedVersion, err := webviewloader.GetAvailableCoreWebView2BrowserVersionString(webviewPath)
if err != nil {
return "", err
}

View file

@ -4,58 +4,32 @@ The build directory is used to house all the build files and assets for your app
The structure is:
* bin - Output directory
* dialog - Icons for dialogs
* tray - Icons for the system tray
* mac - MacOS specific files
* linux - Linux specific files
* windows - Windows specific files
## Dialog Icons
Place any PNG file in this directory to be able to use them in message dialogs.
The files should have names in the following format: `name[-(light|dark)][2x].png`
Examples:
* `mypic.png` - Standard definition icon with ID `mypic`
* `mypic-light.png` - Standard definition icon with ID `mypic`, used when system theme is light
* `mypic-dark.png` - Standard definition icon with ID `mypic`, used when system theme is dark
* `mypic2x.png` - High definition icon with ID `mypic`
* `mypic-light2x.png` - High definition icon with ID `mypic`, used when system theme is light
* `mypic-dark2x.png` - High definition icon with ID `mypic`, used when system theme is dark
### Order of preference
Icons are selected with the following order of preference:
For High Definition displays:
* name-(theme)2x.png
* name2x.png
* name-(theme).png
* name.png
For Standard Definition displays:
* name-(theme).png
* name.png
## Tray
Place any PNG file in this directory to be able to use them as tray icons.
The name of the filename will be the ID to reference the image.
Example:
* `mypic.png` - May be referenced using `runtime.Tray.SetIcon("mypic")`
* bin - Output directory
* darwin - macOS specific files
* windows - Windows specific files
## Mac
The `darwin` directory holds files specific to Mac builds, such as `Info.plist`.
These may be customised and used as part of the build. To return these files to the default state, simply delete them and
build with the `-package` flag.
The `darwin` directory holds files specific to Mac builds.
These may be customised and used as part of the build. To return these files to the default state, simply delete them
and
build with `wails build`.
## Windows
The directory contains the following files:
The `windows` directory contains the manifest and rc files used when building with the `-package` flag.
- `Info.plist` - the main plist file used for Mac builds. It is used when building using `wails build`.
- `Info.dev.plist` - same as the main plist file but used when building using `wails dev`.
## Windows
The `windows` directory contains the manifest and rc files used when building with `wails build`.
These may be customised for your application. To return these files to the default state, simply delete them and
build with the `-package` flag.
build with `wails build`.
- `icon.ico` - The icon used for the application. This is used when building using `wails build`. If you wish to
use a different icon, simply replace this file with your own. If it is missing, a new `icon.ico` file
will be created using the `appicon.png` file in the build directory.
- `installer/*` - The files used to create the Windows installer. These are used when building using `wails build`.
- `info.json` - Application details used for Windows builds. The data here will be used by the Windows installer,
as well as the application itself (right click the exe -> properties -> details)
- `wails.exe.manifest` - The main application manifest file.

View file

@ -2,13 +2,17 @@ package build
import (
"fmt"
"github.com/wailsapp/wails/v2/pkg/commands/bindings"
"log"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/samber/lo"
"github.com/wailsapp/wails/v2/internal/colour"
"github.com/wailsapp/wails/v2/internal/staticanalysis"
"github.com/wailsapp/wails/v2/pkg/commands/bindings"
"github.com/wailsapp/wails/v2/internal/fs"
"github.com/wailsapp/wails/v2/internal/shell"
@ -130,6 +134,11 @@ func Build(options *Options) (string, error) {
}
}
// Create embed directories if they don't exist
if err := CreateEmbedDirectories(cwd, options); err != nil {
return "", err
}
// Generate bindings
if !options.SkipBindings {
err = GenerateBindings(options)
@ -163,6 +172,35 @@ func Build(options *Options) (string, error) {
return compileBinary, nil
}
func CreateEmbedDirectories(cwd string, buildOptions *Options) error {
path := cwd
if buildOptions.ProjectData != nil {
path = buildOptions.ProjectData.Path
}
embedDetails, err := staticanalysis.GetEmbedDetails(path)
if err != nil {
return err
}
for _, embedDetail := range embedDetails {
fullPath := embedDetail.GetFullPath()
if _, err := os.Stat(fullPath); os.IsNotExist(err) {
err := os.MkdirAll(fullPath, 0755)
if err != nil {
return err
}
f, err := os.Create(filepath.Join(fullPath, "gitkeep"))
if err != nil {
return err
}
_ = f.Close()
}
}
return nil
}
func GenerateBindings(buildOptions *Options) error {
obfuscated := buildOptions.Obfuscated
@ -286,6 +324,20 @@ func execBuildApplication(builder Builder, options *Options) (string, error) {
outputLogger.Println("Done.")
}
if options.Platform == "windows" {
const expWebView2Loader = "exp_gowebview2loader"
message := ""
tags := options.UserTags
if lo.Contains(tags, expWebView2Loader) {
message = "Thanks for testing the new experimental Go native WebView2Loader. Please report your feedback and any bugs you think might be related to using the new loader: https://github.com/wailsapp/wails/issues/2004"
} else {
tags = append(tags, expWebView2Loader)
message = fmt.Sprintf("An experimental Go native WebView2Loader is available. We would love to hear your feedback about it and invite you to test it by building with `-tags %s`", strings.Join(tags, ","))
}
println(colour.Green(" - " + message))
}
return options.CompiledBinary, nil
}

View file

@ -38,7 +38,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:
@ -125,7 +125,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:
@ -203,7 +203,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:
@ -351,7 +351,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:

View file

@ -84,6 +84,10 @@ Example:
`wails build -clean -o myproject.exe`
:::Info
On Mac, the application will be bundled with `Info.plist`, not `Info.dev.plist`.
:::
:::info UPX on Apple Silicon
There are [issues](https://github.com/upx/upx/issues/446) with using UPX with Apple Silicon.
@ -163,7 +167,7 @@ Your system is ready for Wails development!
- On macOS, it will bundle the application into a `.app` file and run it. It will use a `build/darwin/Info.dev.plist` for development.
| Flag | Description | Default |
| :--------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------- |
|:-----------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------|
| -assetdir "./path/to/assets" | Serve assets from the given directory instead of using the provided asset FS | Value in `wails.json` |
| -browser | Opens a browser to `http://localhost:34115` on startup | |
| -compiler "compiler" | Use a different go compiler to build, eg go1.15beta1 | go |
@ -173,6 +177,7 @@ Your system is ready for Wails development!
| -tags "extra tags" | Build tags to pass to compiler (quoted and space separated) | |
| -loglevel "loglevel" | Loglevel to use - Trace, Debug, Info, Warning, Error | Debug |
| -noreload | Disable automatic reload when assets change | |
| -nocolour | Turn off colour cli output | false |
| -nogen | Disable generate module | |
| -v | Verbosity level (0 - silent, 1 - standard, 2 - verbose) | 1 |
| -wailsjsdir | The directory to generate the generated Wails JS modules | Value in `wails.json` |

View file

@ -363,21 +363,6 @@ Indicates what value the `CSSDragProperty` style should have to drag the window.
Name: CSSDragValue<br/>
Type: `string`
### ZoomFactor
Name: ZoomFactor<br/>
Type: `float64`
This defines the zoom factor for the WebView2. This is the option matching the Edge user activated zoom in or out.
### IsZoomControlEnabled
Name: IsZoomControlEnabled<br/>
Type: `bool`
This enables the zoom factor to be changed by the user. Please note that the zoom factor can be set in the options while
disallowing the user to change it at runtime (f.e. for a kiosk application or similar).
### Bind
A slice of struct instances defining methods that need to be bound to the frontend.
@ -473,6 +458,21 @@ Important information about distribution of fixed version runtime:
Name: WebviewBrowserPath<br/>
Type: `string`
### ZoomFactor
Name: ZoomFactor<br/>
Type: `float64`
This defines the zoom factor for the WebView2. This is the option matching the Edge user activated zoom in or out.
### IsZoomControlEnabled
Name: IsZoomControlEnabled<br/>
Type: `bool`
This enables the zoom factor to be changed by the user. Please note that the zoom factor can be set in the options while
disallowing the user to change it at runtime (f.e. for a kiosk application or similar).
#### Theme
Minimum Windows Version: Windows 10 2004/20H1

View file

@ -0,0 +1,38 @@
{
"version.label": {
"message": "v2.1.0",
"description": "The label for version v2.1.0"
},
"sidebar.docs.category.Getting Started": {
"message": "Getting Started",
"description": "The label for category Getting Started in sidebar docs"
},
"sidebar.docs.category.Reference": {
"message": "Reference",
"description": "The label for category Reference in sidebar docs"
},
"sidebar.docs.category.Runtime": {
"message": "Runtime",
"description": "The label for category Runtime in sidebar docs"
},
"sidebar.docs.category.Community": {
"message": "Community",
"description": "The label for category Community in sidebar docs"
},
"sidebar.docs.category.Showcase": {
"message": "Showcase",
"description": "The label for category Showcase in sidebar docs"
},
"sidebar.docs.category.Guides": {
"message": "Guides",
"description": "The label for category Guides in sidebar docs"
},
"sidebar.docs.category.Tutorials": {
"message": "Tutorials",
"description": "The label for category Tutorials in sidebar docs"
},
"sidebar.docs.link.Contributing": {
"message": "Contributing",
"description": "The label for link Contributing in sidebar docs, linking to /community-guide#ways-of-contributing"
}
}

View file

@ -24,7 +24,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:
@ -107,7 +107,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:
@ -182,7 +182,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:
@ -327,7 +327,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:

View file

@ -106,7 +106,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:
@ -181,7 +181,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:
@ -326,7 +326,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:

View file

@ -106,7 +106,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:
@ -181,7 +181,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:
@ -326,7 +326,7 @@ jobs:
go-version: [1.18]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v2
with:

View file

@ -0,0 +1,38 @@
{
"version.label": {
"message": "v2.1.0",
"description": "The label for version v2.1.0"
},
"sidebar.docs.category.Getting Started": {
"message": "Getting Started",
"description": "The label for category Getting Started in sidebar docs"
},
"sidebar.docs.category.Reference": {
"message": "Reference",
"description": "The label for category Reference in sidebar docs"
},
"sidebar.docs.category.Runtime": {
"message": "Runtime",
"description": "The label for category Runtime in sidebar docs"
},
"sidebar.docs.category.Community": {
"message": "Community",
"description": "The label for category Community in sidebar docs"
},
"sidebar.docs.category.Showcase": {
"message": "Showcase",
"description": "The label for category Showcase in sidebar docs"
},
"sidebar.docs.category.Guides": {
"message": "Guides",
"description": "The label for category Guides in sidebar docs"
},
"sidebar.docs.category.Tutorials": {
"message": "Tutorials",
"description": "The label for category Tutorials in sidebar docs"
},
"sidebar.docs.link.Contributing": {
"message": "Contributing",
"description": "The label for link Contributing in sidebar docs, linking to /community-guide#ways-of-contributing"
}
}

View file

@ -0,0 +1,26 @@
---
sidebar_position: 2
---
# Links
This page serves as a list for community related links. Please submit a PR (click `Edit this page` at the bottom) to submit links.
## Awesome Wails
The [definitive list](https://github.com/wailsapp/awesome-wails) of links related to Wails.
## Support Channels
- [Gophers Slack Channel](https://gophers.slack.com/messages/CJ4P9F7MZ/)
- [Gophers Slack Channel Invite](https://invite.slack.golangbridge.org/)
- [Github Issues](https://github.com/wailsapp/wails/issues)
- [v2 Beta Discussion Board](https://github.com/wailsapp/wails/discussions/828)
## Social Media
- [Twitter](https://twitter.com/wailsapp)
- [Wails Chinese Community QQ Group](https://qm.qq.com/cgi-bin/qm/qr?k=PmIURne5hFGNd7QWzW5qd6FV-INEjNJv&jump_from=webapi) - Group number: 1067173054
## Other Tutorials and Articles
- [Building of Bulletin Board](https://blog.customct.com/building-bulletin-board)

View file

@ -1,8 +1,10 @@
# EmailIt
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/emailit.webp").default} />
<br />
</p>
```
[EmailIt](https://github.com/raguay/EmailIt/) is a Wails 2 program that is a markdown based email sender only with nine notepads, scripts to manipulate the text, and templates. It also has a builtin [Node-Red](https://nodered.org/) server, scripts terminal, and the [ScriptBar](https://github.com/raguay/ScriptBarApp) program for displaying results from Node-Red or a script on your system. Documentation is very scarce, but the programs works. Its built using Wails2 and Svelte, and the download is a universal macOS application.

View file

@ -1,9 +1,11 @@
# EncryptEasy
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/encrypteasy.webp").default} />
<br />
</p>
```
**[EncryptEasy](https://www.encrypteasy.app) is a simple and easy to use PGP encryption tool, managing all your and your contacts keys. Encryption should be simple. Developed with Wails.**

View file

@ -0,0 +1,16 @@
# FileHound Export Utility
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/filehound.webp").default} />
<br />
</p>
```
[FileHound Export Utility](https://www.filehound.co.uk/) FileHound is a cloud document management platform made for secure file retention, business process automation and SmartCapture capabilities.
The FileHound Export Utility allows FileHound Administrators the ability to run a secure document and data extraction tasks for alternative back-up and recovery purposes. This application will download all documents and/or meta data saved in FileHound based on the filters you choose. The metadata will be exported in both JSON and XML formats.
Backend built with: Go 1.15 Wails 1.11.0 go-sqlite3 1.14.6 go-linq 3.2
Frontend with: Vue 2.6.11 Vuex 3.4.0 Typescript Tailwind 1.9.6

View file

@ -1,10 +1,14 @@
# Minecraft Updater
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img
src={require("@site/static/img/showcase/minecraft-mod-updater.webp").default}
src={
require("@site/static/img/showcase/minecraft-mod-updater.webp").default
}
/>
<br />
</p>
```
[Minecraft Updater](https://github.com/Gurkengewuerz/MinecraftModUpdater) is a utility tool to update and synchronize Minecraft mods for your userbase. Its built using Wails2 and React with [antd](https://ant.design/) as frontend framework.

View file

@ -1,11 +1,13 @@
# Modal File Manager
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img
src={require("@site/static/img/showcase/modalfilemanager.webp").default}
/>
<br />
</p>
```
[Modal File Manager](https://github.com/raguay/ModalFileManager) is a dual pane file manager using web technologies. My original design was based on NW.js and can be found [here](https://github.com/raguay/ModalFileManager-NWjs). This version uses the same Svelte based frontend code (but it has be greatly modified since the departure from NW.js), but the backend is a [Wails 2](https://wails.io/) implementation. By using this implementation, I no longer use command line `rm`, `cp`, etc. commands. It is fully coded using Go and runs much faster than the previous versions.

View file

@ -1,8 +1,10 @@
# Molley Wallet
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/mollywallet.webp").default} />
<br />
</p>
```
[Molly Wallet](https://github.com/grvlle/constellation_wallet/) the official $DAG wallet of the Constellation Network. It'll let users interact with the Hypergraph Network in various ways, not limited to producing $DAG transactions.

View file

@ -1,9 +1,11 @@
# October
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/october.webp").default} />
<br />
</p>
```
[October](https://october.utf9k.net) is a small Wails application that makes it really easy to extract highlights from [Kobo eReaders](https://en.wikipedia.org/wiki/Kobo_eReader) and then forward them to [Readwise](https://readwise.io).

View file

@ -1,8 +1,10 @@
# Optimus
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/optimus.webp").default} />
<br />
</p>
```
[Optimus](https://github.com/splode/optimus) is a desktop image optimization application. It supports conversion and compression between WebP, JPEG, and PNG image formats.

View file

@ -1,8 +1,10 @@
# Portfall
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/portfall.webp").default} />
<br />
</p>
```
[Portfall](https://github.com/rekon-oss/portfall) - A desktop k8s port-forwarding portal for easy access to all your cluster UIs

View file

@ -1,10 +1,12 @@
# Restic Browser
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img
src={require("@site/static/img/showcase/restic-browser-2.png").default}
/>
<br />
</p>
```
[Restic-Browser](https://github.com/emuell/restic-browser) - A simple, cross-platform [restic](https://github.com/restic/restic) backup GUI for browsing and restoring restic repositories.

View file

@ -1,9 +1,11 @@
# RiftShare
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/riftshare-main.webp").default} />
<br />
</p>
```
Easy, Secure, and Free file sharing for everyone. Learn more at [Riftshare.app](https://riftshare.app)

View file

@ -1,8 +1,10 @@
# ScriptBar
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/scriptbar.webp").default} />
<br />
</p>
```
[ScriptBar](https://GitHub.com/raguay/ScriptBarApp) is a program to show the output of the embedded [Node-Red](https://nodered.org) server in the [EmailIt](https://GitHub.com/raguay/EmailIt) application. It also displays the output of scripts on your system. ScriptBar doesn't put them in the menubar, but has them all in a convient window for easy viewing. You can have multiple tabs to have many different things show. You can also keep the links to your most visited web sites.

View file

@ -1,8 +1,10 @@
# Surge
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/surge.png").default} />
<br />
</p>
```
[Surge](https://getsurge.io/) is a p2p filesharing app designed to utilize blockchain technologies to enable 100% anonymous file transfers. Surge is end-to-end encrypted, decentralized and open source.

View file

@ -1,8 +1,10 @@
# Wally
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/wally.webp").default} />
<br />
</p>
```
[Wally](https://ergodox-ez.com/pages/wally) is the official firmware flasher for [Ergodox](https://ergodox-ez.com/) keyboards. It looks great and is a fantastic example of what you can achieve with Wails: the ability to combine the power of Go and the rich graphical tools of the web development world.

View file

@ -1,8 +1,10 @@
# Wombat
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/wombat.webp").default} />
<br />
</p>
```
[Wombat](https://github.com/rogchap/wombat) is a cross platform gRPC client.

View file

@ -1,8 +1,10 @@
# Ytd
```mdx-code-block
<p style={{ "text-align": "center" }}>
<img src={require("@site/static/img/showcase/ytd.webp").default} />
<br />
</p>
```
[Ytd](https://github.com/marcio199226/ytd/tree/v2-wails) is an app for downloading tracks from youtube, creating offline playlists and share them with your friends, your friends will be able to playback your playlists or download them for offline listening, has an built-in player.

View file

@ -0,0 +1,56 @@
---
sidebar_position: 1
---
# テンプレート
このページでは、コミュニティがサポートしているテンプレートを紹介しています。 このページに新たにテンプレートを含めたい場合は、このページの下側にある`このページを編集`をクリックして、プルリクエストを出してください。 独自テンプレートの作成方法については、[テンプレート](../guides/templates.mdx)ガイドをご覧ください。
これらのテンプレートを使用するには、`wails init -n "プロジェクト名" -t [テンプレートのリンク[@バージョン]]`コマンドを実行してください。
バージョンサフィックスが無い場合は、デフォルトで、メインブランチのコードテンプレートが使用されます。 バージョンサフィックスがある場合は、当該バージョンのタグに対応するコードテンプレートが使用されます。
例: `wails init -n "プロジェクト名" -t https://github.com/misitebao/wails-template-vue`
:::warning 注意
**Wailsプロジェクトでは、サードパーティ製テンプレートのメンテナンスは行っておらず、責任も負いません!**
テンプレートについてよく分からない場合は、`package.json`および`wails.json`を確認し、どのようなスクリプトが実行されるのかや、どのようなパッケージがインストールされるのかを調べてください。
:::
## Vue
- [wails-template-vue](https://github.com/misitebao/wails-template-vue) - Wails template based on Vue ecology (Integrated TypeScript, Dark theme, Internationalization, Single page routing, TailwindCSS)
- [wails-vite-vue-ts](https://github.com/codydbentley/wails-vite-vue-ts) - Viteを使用したVue 3 TypeScript (および機能を追加する手順)
- [wails-vite-vue-the-works](https://github.com/codydbentley/wails-vite-vue-the-works) - Vite、Vuex、Vue Router、SaaS、ESLint + Prettier を使用した Vue 3 TypeScript
## Angular
- [wails-angular-template](https://github.com/TAINCER/wails-angular-template) - TypeScript、Sass、ホットリロード、コード分割、i18n を使用した Angular
## React
- [wails-react-template](https://github.com/AlienRecall/wails-react-template) - reactjsを使用したテンプレート
- [wails-react-template](https://github.com/flin7/wails-react-template) - ライブ開発をサポートしたReactの最小テンプレート
- [wails-template-nextjs](https://github.com/LGiki/wails-template-nextjs) - Next.js、TypeScript を使用したテンプレート
- [wails-vite-react-ts-tailwind-template](https://github.com/hotafrika/wails-vite-react-ts-tailwind-template) - A template for React + TypeScript + Vite + TailwindCSS
## Svelte
- [wails-svelte-template](https://github.com/raitonoberu/wails-svelte-template) - Svelteを使用したテンプレート
- [wails-vite-svelte-template](https://github.com/BillBuilt/wails-vite-svelte-template) - SvelteおよびViteを使用したテンプレート
- [wails-vite-svelte-tailwind-template](https://github.com/BillBuilt/wails-vite-svelte-tailwind-template) - TailwindCSS v3を含んだ、SvelteおよびViteを使用したテンプレート
- [wails-sveltekit-template](https://github.com/h8gi/wails-sveltekit-template) - SvelteKitを使用したテンプレート
## Elm
- [wails-elm-template](https://github.com/benjamin-thomas/wails-elm-template) - 関数型プログラミングと**高速な**ホットリロードを使ったGUIアプリ開発 :tada: :rocket:
- [wails-template-elm-tailwind](https://github.com/rnice01/wails-template-elm-tailwind) - Combine the powers :muscle: of Elm + Tailwind CSS + Wails! Hot reloading supported.
## ピュアJavaScript (バニラ)
- [wails-pure-js-template](https://github.com/KiddoV/wails-pure-js-template) - 基本的なJavaScript、HTML、CSSのみを含むテンプレート

View file

@ -0,0 +1,22 @@
---
sidebar_position: 6
---
# プロジェクトのコンパイル
プロジェクトディレクトリ上で、`wails build`コマンドを実行しましょう。 そうすることで、プロジェクトがコンパイルされ、`build/bin`ディレクトリ内に本番配布用のバイナリが出力されます。
バイナリを起動すると、デフォルト仕様のアプリを確認することができます:
```mdx-code-block
<div class="text--center">
<img
src={require("@site/static/img/defaultproject.webp").default}
width="50%"
class="screenshot"
/>
</div>
<br />
```
コンパイルオプションについて詳しくは、[CLIリファレンス](../reference/cli.mdx#build)をご覧ください。

View file

@ -0,0 +1,16 @@
---
sidebar_position: 5
---
# アプリの開発
プロジェクトディレクトリのルート上で`wails dev`コマンドを実行すると、アプリを開発モードで起動することができます。 コマンドを実行すると下記の処理が実行されます:
- アプリをビルドしたのち、起動する
- Goのコードをフロントエンドにバインドし、Javascriptから呼び出せるようにする
- Using the power of [Vite](https://vitejs.dev/), will watch for modifications in your Go files and rebuild/re-run on change
- ブラウザからアプリを操作できるようにする[Webサーバ](http://localhost:34115)を立ち上げる。 これにより、任意のブラウザ拡張機能を利用できる。 JavascriptのコンソールからGoのコードを呼び出すこともできる
アプリ開発を始めるときは、プロジェクトディレクトリ上で`wails dev`コマンドを実行しましょう。 詳しくは、[こちら](../reference/cli.mdx#dev)をご覧ください。
近日中にチュートリアルを公開予定です。

View file

@ -0,0 +1,130 @@
---
sidebar_position: 2
---
# プロジェクトの開始
## プロジェクトの生成
CLIのインストールが終わったら、`wails init`コマンドで新しいプロジェクトを生成しましょう。
好きなフレームワークを選択してください:
```mdx-code-block
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
<Tabs
defaultValue="Svelte"
values={[
{label: "Svelte", value: "Svelte"},
{label: "React", value: "React"},
{label: "Vue", value: "Vue"},
{label: "Preact", value: "Preact"},
{label: "Lit", value: "Lit"},
{label: "Vanilla", value: "Vanilla"},
]}
>
<TabItem value="Svelte">
Generate a <a href={"https://svelte.dev/"}>Svelte</a> project using Javascript with:<br/><br/>
wails init -n myproject -t svelte
If you would rather use Typescript:<br/>
wails init -n myproject -t svelte-ts
</TabItem>
<TabItem value="React">
Generate a <a href={"https://reactjs.org/"}>React</a> project using Javascript with:<br/><br/>
wails init -n myproject -t react
If you would rather use Typescript:<br/>
wails init -n myproject -t react-ts
</TabItem>
<TabItem value="Vue">
Generate a <a href={"https://vuejs.org/"}>Vue</a> project using Javascript with:<br/><br/>
wails init -n myproject -t vue
If you would rather use Typescript:<br/>
wails init -n myproject -t vue-ts
</TabItem>
<TabItem value="Preact">
Generate a <a href={"https://preactjs.com/"}>Preact</a> project using Javascript with:<br/><br/>
wails init -n myproject -t preact
If you would rather use Typescript:<br/>
wails init -n myproject -t preact-ts
</TabItem>
<TabItem value="Lit">
Generate a <a href={"https://lit.dev/"}>Lit</a> project using Javascript with:<br/><br/>
wails init -n myproject -t lit
If you would rather use Typescript:<br/>
wails init -n myproject -t lit-ts
</TabItem>
<TabItem value="Vanilla">
Generate a Vanilla project using Javascript with:<br/><br/>
wails init -n myproject -t vanilla
If you would rather use Typescript:<br/>
wails init -n myproject -t vanilla-ts
</TabItem>
</Tabs>
```
<hr />
様々な機能やフレームワークを提供する[コミュニティテンプレート](../community/templates.mdx)を利用することもできます。
プロジェクト生成時に使用可能なオプションを確認するには、`wails init -help`を実行してください。 詳しくは、[CLIリファレンス](../reference/cli.mdx#init)を参照してください。
## プロジェクトのディレクトリ構成
Wailsのプロジェクトディレクトリの構成は次のとおりです:
```
.
├── build/
│ ├── appicon.png
│ ├── darwin/
│ └── windows/
├── frontend/
├── go.mod
├── go.sum
├── main.go
└── wails.json
```
### プロジェクトの構造
- `/main.go` - アプリのメインコード
- `/frontend/` - フロントエンドのプロジェクトディレクトリ
- `/build/` - ビルドディレクトリ
- `/build/appicon.png` - アプリアイコン
- `/build/darwin/` - Mac固有のプロジェクトディレクトリ
- `/build/windows/` - Windows固有のプロジェクトディレクトリ
- `/wails.json` - プロジェクト構成ファイル
- `/go.mod` - Goモジュール定義ファイル
- `/go.sum` - Goモジュールチェックサムファイル
`frontend`ディレクトリ内は、Wailsで決まったファイル構成等は無く、お好きなフロントエンドプロジェクトを配置することができます。
`build`ディレクトリは、アプリのビルド時に使用されます。 この中のファイルは、ビルドの挙動をカスタマイズするために、適宜ファイル内容を書き換えることができます。 buildディレクトリ内のファイルを削除すると、デフォルトのファイルが再生成されます。
`go.mod`のモジュール名は、最初は"changeme"になっています。 このモジュール名は、あなたのプロジェクトに適切な名前に変更しましょう。

View file

@ -0,0 +1,79 @@
---
sidebar_position: 1
---
# インストール
## サポートされているプラットフォーム
- Windows 10/11 AMD64/ARM64
- MacOS 10.13+ AMD64
- MacOS 11.0+ ARM64
- Linux AMD64/ARM64
## 依存関係
Wailsをインストールする前に、下記のものを導入しておく必要があります。
- Go 1.18+
- NPM (Node 15+)
### Go
Download Go from the [Go Downloads Page](https://go.dev/dl/).
公式の[Goインストール手順](https://go.dev/doc/install)に従って、Goをインストールしてください。 その際、`PATH`環境変数に`~/go/bin`ディレクトリへのパスが含まれていることも確認してください。 それらが終わったら、ターミナルを再起動し、以下の確認をしてください:
- Goが正しくインストールされているかを確認する: `go version`
- "~/go/bin"のディレクトリパスがPATH環境変数に含まれているか確認する: `echo $PATH | grep go/bin`
### NPM
[Nodeダウンロードページ](https://nodejs.org/ja/download/)からNPMをダウンロードしてください。 最新版を利用することをお勧めします。なぜなら、私たちは最新版に対してテストを実施しているためです。
`npm --version`を実行して、インストールが完了しているかを確認してください。
## プラットフォーム固有の依存関係
開発作業を行うプラットフォームによって、必要な依存関係が存在します:
```mdx-code-block
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
<Tabs
defaultValue="Windows"
values={[
{ label: "Windows", value: "Windows" },
{ label: "MacOS", value: "MacOS" },
{ label: "Linux", value: "Linux" },
]}
>
<TabItem value="MacOS">
Wails requires that the xcode command line tools are installed. This can be
done by running <code>xcode-select --install</code>.
</TabItem>
<TabItem value="Windows">
Wails requires that the <a href="https://developer.microsoft.com/en-us/microsoft-edge/webview2/">WebView2</a> runtime is installed. Some Windows installations will already have this installed. You can check using the <code>wails doctor</code> command.
</TabItem>
<TabItem value={"Linux"}>
Linux required the standard <code>gcc</code> build tools plus <code>libgtk3</code> and <code>libwebkit</code>. Rather than list a ton of commands for different distros, Wails can try to determine what the installation commands are for your specific distribution. Run <code>wails doctor</code> after installation to be shown how to install the dependencies. If your distro/package manager is not supported, please consult the <a href={"/docs/guides/linux-distro-support"}>Add Linux Distro</a> guide.
</TabItem>
</Tabs>
```
## 任意の依存関係
- [UPX](https://upx.github.io/)を導入することで、構築したアプリを圧縮できます。
## Wailsのインストール
`go install github.com/wailsapp/wails/v2/cmd/wails@latest`を実行して、Wails CLIをインストールしてください。
## システムチェック
`wails doctor`を実行すると、必要な依存関係が正しくインストールされているかを確認することができます。 正しくインストールされていない場合は、その内容をあなたにお知らせして、どうすれば解決できるかを教えてくれます。
## `wails`コマンドが見つからないのですが?
`wails`コマンドが見つからないとシステムに怒られた場合は、Goが、公式のGoインストール手順に従って導入されているかを確認してください。 コマンドが見つからないほとんどの理由は、あなたのホームディレクトリ配下にある`go/bin`ディレクトリのパスが、`PATH`環境変数に含まれていないからです。 また、インストールによって行われた環境変更を反映させるために、もともと開いていたコマンドプロンプト(ターミナル)がある場合はそれらをいったん閉じて、再度開きなおしてください。

View file

@ -0,0 +1,194 @@
# Application Development
There are no hard and fast rules for developing applications with Wails, but there are some basic guidelines.
## Application Setup
The pattern used by the default templates are that `main.go` is used for configuring and running the application, whilst `app.go` is used for defining the application logic.
The `app.go` file will define a struct that has 2 methods which act as hooks into the main application:
```go title="app.go"
type App struct {
ctx context.Context
}
func NewApp() *App {
return &App{}
}
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}
func (a *App) shutdown(ctx context.Context) {
}
```
- The startup method is called as soon as Wails allocates the resources it needs and is a good place for creating resources, setting up event listeners and anything else the application needs at startup. It is given a `context.Context` which is usually saved in a struct field. This context is needed for calling the [runtime](../reference/runtime/intro.mdx). If this method returns an error, the application will terminate. In dev mode, the error will be output to the console.
- The shutdown method will be called by Wails right at the end of the shutdown process. This is a good place to deallocate memory and perform any shutdown tasks.
The `main.go` file generally consists of a single call to `wails.Run()`, which accepts the application configuration. The pattern used by the templates is that before the call to `wails.Run()`, an instance of the struct we defined in `app.go` is created and saved in a variable called `app`. This configuration is where we add our callbacks:
```go {3,9,10} title="main.go"
func main() {
app := NewApp()
err := wails.Run(&options.App{
Title: "My App",
Width: 800,
Height: 600,
OnStartup: app.startup,
OnShutdown: app.shutdown,
})
if err != nil {
log.Fatal(err)
}
}
```
More information on application lifecycle hooks can be found [here](../howdoesitwork.mdx#application-lifecycle-callbacks).
## Binding Methods
It is likely that you will want to call Go methods from the frontend. This is normally done by adding public methods to the already defined struct in `app.go`:
```go {16-18} title="app.go"
type App struct {
ctx context.Context
}
func NewApp() *App {
return &App{}
}
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}
func (a *App) shutdown(ctx context.Context) {
}
func (a *App) Greet(name string) string {
return fmt.Sprintf("Hello %s!", name)
}
```
In the main application configuration, the `Bind` key is where we can tell Wails what we want to bind:
```go {11-13} title="main.go"
func main() {
app := NewApp()
err := wails.Run(&options.App{
Title: "My App",
Width: 800,
Height: 600,
OnStartup: app.startup,
OnShutdown: app.shutdown,
Bind: []interface{}{
app,
},
})
if err != nil {
log.Fatal(err)
}
}
```
This will bind all public methods in our `App` struct (it will never bind the startup and shutdown methods).
### Dealing with context when binding multiple structs
If you want to bind methods for multiple structs but want each struct to keep a reference to the context so that you can use the runtime functions, a good pattern is to pass the context from the `OnStartup` method to your struct instances :
```go
func main() {
app := NewApp()
otherStruct := NewOtherStruct()
err := wails.Run(&options.App{
Title: "My App",
Width: 800,
Height: 600,
OnStartup: func(ctx context.Context){
app.SetContext(ctx)
otherStruct.SetContext(ctx)
},
OnShutdown: app.shutdown,
Bind: []interface{}{
app,
otherStruct
},
})
if err != nil {
log.Fatal(err)
}
}
```
More information on Binding can be found [here](../howdoesitwork.mdx#method-binding).
## Application Menu
Wails supports adding a menu to your application. This is done by passing a [Menu](../reference/menus.mdx#menu) struct to application config. It's common to use a method that returns a Menu, and even more common for that to be a method on the `App` struct used for the lifecycle hooks.
```go {11} title="main.go"
func main() {
app := NewApp()
err := wails.Run(&options.App{
Title: "My App",
Width: 800,
Height: 600,
OnStartup: app.startup,
OnShutdown: app.shutdown,
Menu: app.menu(),
Bind: []interface{}{
app,
},
})
if err != nil {
log.Fatal(err)
}
}
```
## Assets
The great thing about the way Wails v2 handles assets is that it doesn't! The only thing you need to give Wails is an `embed.FS`. How you get to that is entirely up to you. You can use vanilla html/css/js files like the vanilla template. You could have some complicated build system, it doesn't matter.
When `wails build` is run, it will check the `wails.json` project file at the project root. There are 2 keys in the project file that are read:
- "frontend:install"
- "frontend:build"
The first, if given, will be executed in the `frontend` directory to install the node modules. The second, if given, will be executed in the `frontend` directory to build the frontend project.
If these 2 keys aren't given, then Wails does absolutely nothing with the frontend. It is only expecting that `embed.FS`.
### AssetsHandler
A Wails v2 app can optionally define a `http.Handler` in the `options.App`, which allows hooking into the AssetServer to create files on the fly or process POST/PUT requests. GET requests are always first handled by the `assets` FS. If the FS doesn't find the requested file the request will be forwarded to the `http.Handler` for serving. Any requests other than GET will be directly processed by the `AssetsHandler` if specified. It's also possible to only use the `AssetsHandler` by specifiy `nil` as the `Assets` option.
## Built in Dev Server
Running `wails dev` will start the built in dev server which will start a file watcher in your project directory. By default, if any file changes, wails checks if it was an application file (default: `.go`, configurable with `-e` flag). If it was, then it will rebuild your application and relaunch it. If the changed file was in the assets, it will issue a reload after a short amount of time.
The dev server uses a technique called "debouncing" which means it doesn't reload straight away, as there may be multiple files changed in a short amount of time. When a trigger occurs, it waits for a set amount of time before issuing a reload. If another trigger happens, it resets to the wait time again. By default this value is `100ms`. If this value doesn't work for your project, it can be configured using the `-debounce` flag. If used, this value will be saved to your project config and become the default.
## External Dev Server
Some frameworks come with their own live-reloading server, however they will not be able to take advantage of the Wails Go bindings. In this scenario, it is best to run a watcher script that rebuilds the project into the build directory, which Wails will be watching. For an example, see the default svelte template that uses [rollup](https://rollupjs.org/guide/en/). For [create-react-app](https://create-react-app.dev/), it's possible to use [this script](https://gist.github.com/int128/e0cdec598c5b3db728ff35758abdbafd) to achieve a similar result.
## Go Module
The default Wails templates generate a `go.mod` file that contains the module name "changeme". You should change this to something more appropriate after project generation.

View file

@ -0,0 +1,55 @@
# Bleeding Edge
## Overview
Wails is in constant development and new releases are regularly "tagged". This usually happens when all the newer code on `master` has been tested and confirmed working. If you need a bugfix or feature that has not yet made it to a release, it's possible to use the latest "bleeding edge" version using the following steps:
- `git clone https://github.com/wailsapp/wails`
- `cd wails/v2/cmd/wails`
- `go install`
NOTE: The directory that you cloned the project into will now be called "clonedir".
The Wails CLI will now be at the very latest version.
### Updating your project
To update projects to use the latest version of the Wails library, update the project's `go.mod` and ensure the following line is at the bottom of the file:
`replace github.com/wailsapp/wails/v2 => <clonedir>`
Example:
On Windows: `replace github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2`
On 'nix: `replace github.com/wailsapp/wails/v2 => /home/me/projects/wails/v2`
To revert to a stable version, run:
`go install github.com/wailsapp/wails/v2/cmd/wails@latest`
## Testing a Branch
If you want to test a branch, follow the instructions above, but ensure you switch the branch you want to test before installing:
- `git clone https://github.com/wailsapp/wails`
- `cd wails`
- `git checkout -b branch-to-test --track origin/branch-to-test`
- `cd v2/cmd/wails`
- `go install`
Make sure you [update your project](#updating-your-project) as described above.
## Testing a PR
If you want to test a PR, follow the instructions above, but ensure you fetch the PR and switch the branch before installing. Please replace `[IDofThePR]` with the ID of the PR shown on github.com:
- `git clone https://github.com/wailsapp/wails`
- `cd wails`
- `git fetch -u origin pull/[IDofThePR]/head:test/pr-[IDofThePR]`
- `git checkout test/pr-[IDofThePR]`
- `git reset --hard HEAD`
- `cd v2/cmd/wails`
- `go install`
Make sure you [update your project](#updating-your-project) as described above.

View file

@ -0,0 +1,134 @@
# Dynamic Assets
If you want to load or generate assets for your frontend dynamically, you can achieve that using the [AssetsHandler](../reference/options#assetshandler) option. The AssetsHandler is a generic `http.Handler` which will be called for any non GET request on the assets server and for GET requests which can not be served from the bundled assets because the file is not found.
By installing a custom AssetsHandler, you can serve your own assets using a custom asset server.
## Example
In our example project, we will create a simple assets handler which will load files off disk:
```go title=main.go {16-35,49}
package main
import (
"embed"
"fmt"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"net/http"
"os"
"strings"
)
//go:embed all:frontend/dist
var assets embed.FS
type FileLoader struct {
http.Handler
}
func NewFileLoader() *FileLoader {
return &FileLoader{}
}
func (h *FileLoader) ServeHTTP(res http.ResponseWriter, req *http.Request) {
var err error
requestedFilename := strings.TrimPrefix(req.URL.Path, "/")
println("Requesting file:", requestedFilename)
fileData, err := os.ReadFile(requestedFilename)
if err != nil {
res.WriteHeader(http.StatusBadRequest)
res.Write([]byte(fmt.Sprintf("Could not load file %s", requestedFilename)))
}
res.Write(fileData)
}
func main() {
// Create an instance of the app structure
app := NewApp()
// Create application with options
err := wails.Run(&options.App{
Title: "helloworld",
Width: 1024,
Height: 768,
Assets: assets,
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 255},
OnStartup: app.startup,
AssetsHandler: NewFileLoader(),
Bind: []interface{}{
app,
},
})
if err != nil {
println("Error:", err)
}
}
```
When we run the application in dev mode using `wails dev`, we will see the following output:
```
DEB | [ExternalAssetHandler] Loading 'http://localhost:3001/favicon.ico'
DEB | [ExternalAssetHandler] Loading 'http://localhost:3001/favicon.ico' failed, using AssetHandler
Requesting file: favicon.ico
```
As you can see, the assets handler is called when the default assets server is unable to serve the `favicon.ico` file.
If you right click the main application and select "inspect" to bring up the devtools, you can test this feature out by typing the following into the console:
```
let response = await fetch('does-not-exist.txt');
```
This will generate an error in the devtools. We can see that the error is what we expect, returned by our custom assets handler:
```mdx-code-block
<p className="text--center">
<img
src={require("@site/static/img/assetshandler-does-not-exist.webp").default}
/>
</p>
```
However, if we request `go.mod`, we will see the following output:
```mdx-code-block
<p className="text--center">
<img src={require("@site/static/img/assetshandler-go-mod.webp").default} />
</p>
```
This technique can be used to load images directly into the page. If we updated our default vanilla template and replaced the logo image:
```html
<img id="logo" class="logo" />
```
with:
```html
<img src="build/appicon.png" style="width: 300px" />
```
Then we would see the following:
```mdx-code-block
<p className="text--center">
<img
src={require("@site/static/img/assetshandler-image.webp").default}
style={{ width: "75%" }}
/>
</p>
```
:::warning
Exposing your filesystem in this way is a security risk. It is recommended that you properly manage access to your filesystem.
:::

View file

@ -0,0 +1,85 @@
# Frameless Applications
Wails supports application that have no frames. This can be achieved by using the [frameless](../reference/options.mdx#frameless) field in [Application Options](../reference/options.mdx#application-options).
Wails offers a simple solution for dragging the window: Any HTML element that has the CSS style `--wails-draggable:drag` will act as a "drag handle". This property applies to all child elements. If you need to indicate that a nested element should not drag, then use the attribute '--wails-draggable:no-drag' on that element.
```html
<html>
<head>
<link rel="stylesheet" href="/main.css" />
</head>
<body style="--wails-draggable:drag">
<div id="logo"></div>
<div id="input" style="--wails-draggable:no-drag">
<input id="name" type="text" />
<button onclick="greet()">Greet</button>
</div>
<div id="result"></div>
<script src="/main.js"></script>
</body>
</html>
```
For some projects, using a CSS variable may not be possible due to dynamic styling. In this case, you can use the `CSSDragProperty` and `CSSDragValue` application options to define a property and value that will be used to indicate draggable regions:
```go title=main.go
package main
import (
"embed"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
)
//go:embed all:frontend/dist
var assets embed.FS
func main() {
// Create an instance of the app structure
app := NewApp()
// Create application with options
err := wails.Run(&options.App{
Title: "alwaysontop",
Width: 1024,
Height: 768,
Assets: assets,
Frameless: true,
CSSDragProperty: "widows",
CSSDragValue: "1",
Bind: []interface{}{
app,
},
})
if err != nil {
println("Error:", err)
}
}
```
```html title=index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>alwaysontop</title>
</head>
<body style="widows: 1">
<div id="app"></div>
<script src="./src/main.js" type="module"></script>
</body>
</html>
```
:::info Fullscreen
If you allow your application to go fullscreen, this drag functionality will be disabled.
:::

View file

@ -0,0 +1,75 @@
# Frontend
## Script Injection
When Wails serves your `index.html`, by default, it will inject 2 script entries into the `<body>` tag to load `/wails/ipc.js` and `/wails/runtime.js`. These files install the bindings and runtime respectively.
The code below shows where these are injected by default:
```html
<html>
<head>
<title>injection example</title>
<link rel="stylesheet" href="/main.css">
<!-- <script src="/wails/ipc.js"></script> -->
<!-- <script src="/wails/runtime.js"></script> -->
</head>
<body data-wails-drag>
<div class="logo"></div>
<div class="result" id="result">Please enter your name below 👇</div>
<div class="input-box" id="input" data-wails-no-drag>
<input class="input" id="name" type="text" autocomplete="off">
<button class="btn" onclick="greet()">Greet</button>
</div>
<script src="/main.js"></script>
</body>
</html>
```
### Overriding Default Script Injection
To provide more flexibility to developers, there is a meta tag that may be used to customise this behaviour:
```html
<meta name="wails-options" content="[options]">
```
The options are as follows:
| 値 | Description |
| ------------------- | ------------------------------------------------ |
| noautoinjectruntime | Disable the autoinjection of `/wails/runtime.js` |
| noautoinjectipc | Disable the autoinjection of `/wails/ipc.js` |
| noautoinject | Disable all autoinjection of scripts |
Multiple options may be used provided they are comma seperated.
This code is perfectly valid and operates the same as the autoinjection version:
```html
<html>
<head>
<title>injection example</title>
<meta name="wails-options" content="noautoinject">
<link rel="stylesheet" href="/main.css">
</head>
<body data-wails-drag>
<div class="logo"></div>
<div class="result" id="result">Please enter your name below 👇</div>
<div class="input-box" id="input" data-wails-no-drag>
<input class="input" id="name" type="text" autocomplete="off">
<button class="btn" onclick="greet()">Greet</button>
</div>
<script src="/wails/ipc.js"></script>
<script src="/wails/runtime.js"></script>
<script src="/main.js"></script>
</body>
</html>
```

View file

@ -0,0 +1,128 @@
# IDEs
Wails aims to provide a great development experience. To that aim, we now support generating IDE specific configuration to provide smoother project setup.
Currently, we support [Visual Studio Code](https://code.visualstudio.com/) but aim to support other IDEs such as Goland.
## Visual Studio Code
```mdx-code-block
<p className="text--center">
<img
src={require("@site/static/img/vscode.webp").default}
style={{ width: "75%" }}
/>
</p>
```
When generating a project using the `-ide vscode` flags, IDE files will be created alongside the other project files. These files are placed into the `.vscode` directory and provide the correct configuration for debugging your application.
The 2 files generated are `tasks.json` and `launch.json`. Below are the files generated for the default vanilla project:
```json title="tasks.json"
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"options": {
"cwd": "${workspaceFolder}"
},
"command": "go",
"args": [
"build",
"-tags",
"dev",
"-gcflags",
"all=-N -l",
"-o",
"build/bin/myproject.exe"
]
}
]
}
```
```json title="launch.json"
{
"version": "0.2.0",
"configurations": [
{
"name": "Wails: Debug myproject",
"type": "go",
"request": "launch",
"mode": "exec",
"program": "${workspaceFolder}/build/bin/myproject.exe",
"preLaunchTask": "build",
"cwd": "${workspaceFolder}",
"env": {}
}
]
}
```
### Configuring the install and build steps
The `tasks.json` file is simple for the default project as there is no `npm install` or `npm run build` step needed. For projects that have a frontend build step, such as the svelte template, we would need to edit `tasks.json` to add the install and build steps:
```json title="tasks.json"
{
"version": "2.0.0",
"tasks": [
{
"label": "npm install",
"type": "npm",
"script": "install",
"options": {
"cwd": "${workspaceFolder}/frontend"
},
"presentation": {
"clear": true,
"panel": "shared",
"showReuseMessage": false
},
"problemMatcher": []
},
{
"label": "npm run build",
"type": "npm",
"script": "build",
"options": {
"cwd": "${workspaceFolder}/frontend"
},
"presentation": {
"clear": true,
"panel": "shared",
"showReuseMessage": false
},
"problemMatcher": []
},
{
"label": "build",
"type": "shell",
"options": {
"cwd": "${workspaceFolder}"
},
"command": "go",
"args": [
"build",
"-tags",
"dev",
"-gcflags",
"all=-N -l",
"-o",
"build/bin/vscode.exe"
],
"dependsOn": ["npm install", "npm run build"]
}
]
}
```
:::info Future Enhancement
In the future, we hope to generate a `tasks.json` that includes the install and build steps automatically.
:::

View file

@ -0,0 +1,104 @@
# Linux Distro Support
## Overview
Wails offers Linux support but providing installation instructions for all available distributions is an impossible task. Instead, Wails tries to determine if the packages you need to develop applications are available via your system's package manager. Currently, we support the following package managers:
- apt
- dnf
- emerge
- eopkg
- nixpkgs
- pacman
- zypper
## Adding package names
There may be circumstances where your distro uses one of the supported package managers but the package name is different. For example, you may use an Ubuntu derivative, but the package name for gtk may be different. Wails attempts to find the correct package by iterating through a list of package names. The list of packages are stored in the packagemanager specific file in the `v2/internal/system/packagemanager` directory. In our example, this would be `v2/internal/system/packagemanager/apt.go`.
In this file, the list of packages are defined by the `Packages()` method:
```go
func (a *Apt) Packages() packagemap {
return packagemap{
"libgtk-3": []*Package{
{Name: "libgtk-3-dev", SystemPackage: true, Library: true},
},
"libwebkit": []*Package{
{Name: "libwebkit2gtk-4.0-dev", SystemPackage: true, Library: true},
},
"gcc": []*Package{
{Name: "build-essential", SystemPackage: true},
},
"pkg-config": []*Package{
{Name: "pkg-config", SystemPackage: true},
},
"npm": []*Package{
{Name: "npm", SystemPackage: true},
},
"docker": []*Package{
{Name: "docker.io", SystemPackage: true, Optional: true},
},
}
}
```
Let's assume that in our linux distro, `libgtk-3` is packaged under the name `lib-gtk3-dev`. We could add support for this by adding the following line:
```go {5}
func (a *Apt) Packages() packagemap {
return packagemap{
"libgtk-3": []*Package{
{Name: "libgtk-3-dev", SystemPackage: true, Library: true},
{Name: "lib-gtk3-dev", SystemPackage: true, Library: true},
},
"libwebkit": []*Package{
{Name: "libwebkit2gtk-4.0-dev", SystemPackage: true, Library: true},
},
"gcc": []*Package{
{Name: "build-essential", SystemPackage: true},
},
"pkg-config": []*Package{
{Name: "pkg-config", SystemPackage: true},
},
"npm": []*Package{
{Name: "npm", SystemPackage: true},
},
"docker": []*Package{
{Name: "docker.io", SystemPackage: true, Optional: true},
},
}
}
```
## Adding new package managers
To add a new package manager, perform the following steps:
- Create a new file in `v2/internal/system/packagemanager` called `<pm>.go`, where `<pm>` is the name of the package manager.
- Define a struct that conforms to the package manager interface defined in `pm.go`:
```go
type PackageManager interface {
Name() string
Packages() packagemap
PackageInstalled(*Package) (bool, error)
PackageAvailable(*Package) (bool, error)
InstallCommand(*Package) string
}
```
- `Name()` should return the name of the package manager
- `Packages()` should return a `packagemap`, that provides candidate filenames for dependencies
- `PackageInstalled()` should return `true` if the given package is installed
- `PackageAvailable()` should return `true` if the given package is not installed but available for installation
- `InstallCommand()` should return the exact command to install the given package name
Take a look at the other package managers code to get an idea how this works.
:::info Remember
If you add support for a new package manager, don't forget to also update this page!
:::

View file

@ -0,0 +1,18 @@
# Linux
This page has miscellaneous guides related to developing Wails applications for Linux.
## Video tag doesn't fire "ended" event
When using a video tag, the "ended" event is not fired when the video is finished playing. This is a bug in WebkitGTK, however you can use the following workaround to fix it:
```js
videoTag.addEventListener("timeupdate", (event) => {
if (event.target.duration - event.target.currentTime < 0.2) {
let ended = new Event("ended");
event.target.dispatchEvent(ended);
}
});
```
Source: [Lyimmi](https://github.com/Lyimmi) on the [discussions board](https://github.com/wailsapp/wails/issues/1729#issuecomment-1212291275)

View file

@ -0,0 +1,95 @@
# Manual Builds
The Wails CLI does a lot of heavy lifting for the project, but sometimes it's desirable to manually build your project. This document will discuss the different operations the CLI does and how this may be achieved in different ways.
## Build Process
When either `wails build` or `wails dev` are used, the Wails CLI performs a common build process:
- Install frontend dependencies
- Build frontend project
- Generate build assets
- Compile application
- [optional] Compress application
### Install frontend dependencies
#### CLI Steps
- If the `-s` flag is given, this step is skipped
- Checks `wails.json` to see if there is an install command in the key `frontend:install`
- If there isn't, it skips this step
- If there is, it checks if `package.json` exists in the frontend directory. If it doesn't exist, it skips this step
- An MD5 sum is generated from the `package.json` file contents
- It checks for the existence of `package.json.md5` and if it exists, will compare the contents of it (an MD5 sum) with the one generated to see if the contents have changed. If they are the same, this step is skipped
- If `package.json.md5` does not exist, it creates it using the generated MD5 sum
- If a build is now required, or `node_modules` does not exist, or the `-f` flag is given, the install command is executed in the frontend directory
#### Manual Steps
This step could be done from the command line or a script with `npm install`.
### Build frontend project
#### Wails CLI
- If the `-s` flag is given, this step is skipped
- Checks `wails.json` to see if there is a build command in the key `frontend:build`
- If there isn't, it skips this step
- If there is, it is executed in the frontend directory
#### Manual Steps
This step could be done from the command line or a script with `npm run build` or whatever the frontend build script is.
### Generate assets
#### Wails CLI
- If `-nopackage` flag is set, this stage is skipped
- If the `build/appicon.png` file does not exist, a default one is created
- For Windows, see [Bundling for Windows](#windows)
- If `build/windows/icon.ico` does not exist, it will create it from the `build/appicon.png` image.
##### Windows
- If `build/windows/icon.ico` does not exist, it will create it from `build/appicon.png` using icon sizes of 256, 128, 64, 48, 32 and 16. This is done using [winicon](https://github.com/leaanthony/winicon).
- If the `build/windows/<projectname>.manifest` file does not exist, it creates it from a default version.
- Compiles the application as a production build (above)
- Uses [winres](https://github.com/tc-hib/winres) to bundle the icon and manifest into a `.syso` file ready for linking.
#### Manual Steps
- Create `icon.ico` using the [winicon](https://github.com/leaanthony/winicon) CLI tool (or any other tool).
- Create / Update a `.manifest` file for your application
- Use the [winres CLI](https://github.com/tc-hib/go-winres) to generate a `.syso` file.
### Compile application
#### Wails CLI
- If the `-clean` flag is provided, the `build` directory is deleted and recreated
- For `wails dev`, the following default Go flags are used: `-tags dev -gcflags "all=-N -l"`
- For `wails build`, the following default Go flags are used: `-tags desktop,production -ldflags "-w -s"`
- On Windows, `-ldflags "-w -h -H windowsgui"`
- Additional tags passed to the CLI using `-tags` are added to the defaults
- Additional ldflags passed to the CLI using `-ldflags` are added to the defaults
- The `-o` flag is passed through
- The Go compiler specified by `-compiler` will be used for compilation
#### Manual steps
- For dev build, the minimum command would be: `go build -tags dev -gcflags "all=-N -l"`
- For production build, the minimum command would be: `go build -tags desktop,production -ldflags "-w -s -H windowsgui"`
- Ensure that you compile in the same directory as the `.syso` file
### Compress application
#### Wails CLI
- If the `-upx` flag has been given, the `upx` program will be run to compress the application with the default settings
- If `-upxflags` is also passed, these flags are used instead of the default ones
#### Manual steps
- Run `upx [flags]` manually to compress the application.

View file

@ -0,0 +1,187 @@
# Migrating from v1
## Overview
Wails v2 is a significant change from v1. This document aims to highlight the changes and the steps in migrating an existing project.
### Creating the Application
In v1, the main application is created using `wails.CreateApp`, bindings are added with `app.Bind`, then the application is run using `app.Run()`.
Example:
```go title="v1"
app := wails.CreateApp(&wails.AppConfig{
Title: "MyApp",
Width: 1024,
Height: 768,
JS: js,
CSS: css,
Colour: "#131313",
})
app.Bind(basic)
app.Run()
```
In v2, there is just a single method, `wails.Run()`, that accepts [application options](../reference/options.mdx#application-options).
```go title="v2"
err := wails.Run(&options.App{
Title: "MyApp",
Width: 800,
Height: 600,
Assets: assets,
Bind: []interface{}{
basic,
},
})
```
### Binding
In v1, it was possible to bind both arbitrary functions and structs. In v2, this has been simplified to only binding structs. The struct instances that were previously passed to the `Bind()` method in v1, are now specified in the `Bind` field of the [application options](../reference/options.mdx#application-options):
```go title="v1"
app := wails.CreateApp(/* options */)
app.Bind(basic)
```
```go title="v2"
err := wails.Run(&options.App{
/* other options */
Bind: []interface{}{
basic,
},
})
```
In v1, bound methods were available to the frontend at `window.backend`. This has changed to `window.go`.``
### Application Lifecycle
In v1, there were 2 special methods in a bound struct: `WailsInit()` and `WailsShutdown()`. These have been replaced with 3 lifecycle hooks as part of the [application options](../reference/options.mdx#application-options):
- [OnStartup](../reference/options.mdx#onstartup)
- [OnShutdown](../reference/options.mdx#onshutdown)
- [OnDomReady](../reference/options.mdx#ondomready)
Note: [OnDomReady](../reference/options.mdx#ondomready) replaces the `wails:ready` system event in v1.
These methods can be standard functions, but a common practice is to have them part of a struct:
```go title="v2"
basic := NewBasicApp()
err := wails.Run(&options.App{
/* Other Options */
OnStartup: basic.startup,
OnShutdown: basic.shutdown,
OnDomReady: basic.domready,
})
...
type Basic struct {
ctx context.Context
}
func (b *Basic) startup(ctx context.Context) {
b.ctx = ctx
}
...
```
### Runtime
The runtime in v2 is much richer than v1 with support for menus, window manipulation and better dialogs. The signature of the methods has changed slightly - please refer the the [Runtime Reference](../reference/runtime/intro.mdx).
In v1, the [runtime](../reference/runtime/intro.mdx) was available via a struct passed to `WailsInit()`. In v2, the runtime has been moved out to its own package. Each method in the runtime takes the `context.Context` that is passed to the [OnStartup](../reference/options.mdx#onstartup) method.
```go title="Runtime Example"
package main
import "github.com/wailsapp/wails/v2/pkg/runtime"
type Basic struct {
ctx context.Context
}
// startup is called at application startup
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
runtime.LogInfo(ctx, "Application Startup called!")
}
```
### Assets
The _biggest_ change in v2 is how assets are handled.
In v1, assets were passed via 2 application options:
- `JS` - The application's Javascript
- `CSS` - The application's CSS
This meant that the responsibility of generating a single JS and CSS file was on the developer. This essentially required the use of complicated packers such as webpack.
In v2, Wails makes no assumptions about your frontend assets, just like a webserver. All of your application assets are passed to the application options as an `embed.FS`.
**This means there is no requirement to bundle your assets, encode images as Base64 or attempt the dark art of bundler configuration to use custom fonts**.
At startup, Wails will scan the given `embed.FS` for `index.html` and use its location as the root path for all the other application assets - just like a webserver would.
Example: An application has the following project layout. All final assets are placed in the `frontend/dist` directory:
```shell
.
├── build/
├── frontend/
│ └── dist/
│ ├── index.html
│ ├── main.js
│ ├── main.css
│ └── logo.svg
├── main.go
└── wails.json
```
Those assets may be used by the application by simply creating an `embed.FS`:
```go title="Assets Example"
//go:embed all:frontend/dist
var assets embed.FS
func main() {
err := wails.Run(&options.App{
/* Other Options */
Assets: assets,
})
}
```
Of course, bundlers can be used if you wish to. The only requirement is to pass the final application assets directory to Wails using an `embed.FS` in the `Assets` key of the [application options](../reference/options.mdx#application-options).
### Project Configuration
In v1, the project configuration was stored in the `project.json` file in the project root. In v2, the project configuration is stored in the `wails.json` file in the project root.
The format of the file is slightly different. Here is a comparison:
<p align="center">
| v1 | v2 | Notes |
| ------------------ | ---------------- | --------------------------------------------------- |
| name | name | |
| description | | Removed |
| author / name | author / name | |
| author / email | author / email | |
| version | version | |
| binaryname | outputfilename | Changed |
| frontend / dir | | Removed |
| frontend / install | frontend:install | Changed |
| frontend / build | frontend:build | Changed |
| frontend / bridge | | Removed |
| frontend / serve | | Removed |
| tags | | Removed |
| | wailsjsdir | The directory to generate wailsjs modules |
| | assetdir | The directory of the compiled frontend assets for `dev` mode. This is normally inferred and could be left empty. |
| | reloaddirs | Comma separated list of additional directories to watch for changes and to trigger reloads in `dev` mode. This is only needed for some more advanced asset configurations. |
</p>

View file

@ -0,0 +1,25 @@
# Mouse Buttons
The Wails runtime intercepts mouse clicks to determine whether a frameless window needs resizing or a window needs to be moved. It has been asked how to detect when a mouse click has occurred, because `window.onclick` doesn't report the mouse buttons correctly. The following code shows how to detect mouse clicks:
```javascript
window.addEventListener('mousedown', handleMouseButtonDown);
function handleMouseButtonDown(event) {
if (event.button === 0) {
// left mouse button
} else if (event.button === 1) {
// middle mouse button
} else if (event.button === 2) {
// right mouse button
} else if (event.button === 3) {
// back mouse button
} else if (event.button === 4) {
// forward mouse button
} else {
// other mouse button
}
}
```
Reference: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button

View file

@ -0,0 +1,42 @@
# Obfuscated Builds
Wails includes support for obfuscating your application using [garble](https://github.com/burrowers/garble).
To produce an obfuscated build, you can use the `-obfuscate` flag with the `wails build` command:
```bash
wails build -obfuscated
```
To customise the obfuscation settings, you can use the `-garbleargs` flag:
```bash
wails build -obfuscated -garbleargs "-literals -tiny -seed=myrandomseed"
```
These settings may be persisted in your [project config](../reference/project-config).
## How it works
In a standard build, all bound methods are available in the frontend under the `window.go` variable. When these methods are called, the corresponding backend method is called using the fully qualified function name. When using an obfuscated build, methods are bound using an ID instead of a name. The bindings generated in the `wailsjs` directory use these IDs to call the backend functions.
:::note
To ensure that your application will work in obfuscated mode, you must use the generated bindings under the `wailsjs` directory in your application.
:::
## Example
Importing the "Greet" method from the bindings like this:
```js
import { Greet } from "../../wailsjs/go/main/App";
// snip
Greet("World");
```
will ensure that the method will work correctly in obfuscated mode, as the bindings will be regenerated with IDs and the call mechanism updated.

View file

@ -0,0 +1,10 @@
# Overscroll
[Overscroll](https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior) is the "bounce effect" you sometimes get when you scroll beyond a page's content boundaries. This is common in mobile apps. This can be disabled using CSS:
```css
html {
height: 100%;
overflow: hidden;
}
```

Some files were not shown because too many files have changed in this diff Show more