Compare commits

...

177 Commits

Author SHA1 Message Date
Lea Anthony
02bd44f772 Merge branch 'develop' into feature/gopath 2020-09-15 20:16:42 +10:00
Ilgıt Yıldırım
a84f43d959 fixed return 2020-09-15 19:13:49 +10:00
Ilgıt Yıldırım
b48da620b3 added Get method 2020-09-15 19:13:49 +10:00
Lea Anthony
ed8884a581 v1.8.1-pre1 2020-09-15 17:52:39 +10:00
Lea Anthony
05d27dda64 Remove incorrect build flag 2020-09-15 17:51:14 +10:00
Travis McLane
bd0c9dbc43 use wailsapp/xgo:1.0.1 for cross-compiling 2020-09-14 19:05:00 -05:00
Travis McLane
1c813622c0 update messages to output xgo version
TODO: allow image override
2020-09-13 08:51:54 -05:00
Travis McLane
76ba7d56ec implement gopath handling during cross compilation 2020-09-13 08:47:29 -05:00
Ilgıt Yıldırım
fe2c5e8611 handle Set error on Update method 2020-09-13 18:50:41 +10:00
Lea Anthony
422ee22d0c Updated contributors 2020-09-11 06:40:36 +10:00
Lea Anthony
22f94cfdb6 Merge branch 'artooro-angular-routing-fix' into develop 2020-09-11 06:39:15 +10:00
Arthur Wiebe
5d754f40de resolve angular routing broken when app is built 2020-09-09 22:06:14 -04:00
Lea Anthony
c9b26c6352 Merge branch 'develop' 2020-09-10 06:55:39 +10:00
Lea Anthony
6f0696631f v1.8.0 2020-09-10 06:45:42 +10:00
Lea Anthony
fefb54de12 v1.7.2-pre12 2020-09-10 06:40:13 +10:00
Lea Anthony
b4224066f7 css fix for vanilla 2020-09-10 06:39:36 +10:00
Lea Anthony
3d9ef75488 v1.7.2-pre11 2020-09-10 06:19:07 +10:00
Lea Anthony
79ecb0704e test fix 2020-09-10 06:17:17 +10:00
Lea Anthony
8154887824 v1.7.2-pre10 2020-09-09 06:58:30 +10:00
Lea Anthony
39ae91a250 Simplify Subscribe() 2020-09-09 06:27:57 +10:00
Lea Anthony
861f5f2a1c v1.7.2-pre9 2020-09-08 07:06:27 +10:00
Lea Anthony
89ed00d6ed Fix svelte for Windows 2020-09-08 07:04:01 +10:00
Lea Anthony
1e1834158b fix vanilla template for windows 2020-09-07 20:45:20 +10:00
Lea Anthony
214fcf03b9 v1.7.2-pre8 2020-09-07 20:28:29 +10:00
Tim Kipp
82ac4f1358 chore: removed Svelte TypeScript support for now 2020-09-07 17:35:12 +10:00
Tim Kipp
8b7cd03428 chore: removed Svelte template link 2020-09-07 17:35:12 +10:00
Tim Kipp
4d2b4fec45 feat: added Svelte template 2020-09-07 17:35:12 +10:00
Lea Anthony
bc0478b2b2 Update README.md 2020-09-06 15:45:37 +10:00
Lea Anthony
d59849b952 v1.7.2-pre7 2020-09-05 07:11:28 +10:00
Lea Anthony
806821ad49 v1.7.2-pre6 2020-09-05 07:01:15 +10:00
Lea Anthony
a4cc95351f Updated runtime package version 2020-09-05 06:52:39 +10:00
Lea Anthony
f851b89350 v1.7.2-pre5 2020-09-04 20:23:26 +10:00
Lea Anthony
7f72189231 updates 2020-09-04 20:20:21 +10:00
Lea Anthony
ef79dd95cf Merge branch 'sync-store' into develop 2020-09-04 20:19:20 +10:00
Lea Anthony
d49b146eaa Add mutex to store 2020-09-04 20:08:31 +10:00
Lea Anthony
8c051e004d Release v1.7.2-pre4 2020-09-03 20:13:55 +10:00
Lea Anthony
c02b9ac032 Update contributors 2020-09-03 20:12:57 +10:00
Dmitry
35f839ae65 Support Ctlos Linux 2020-09-03 20:11:39 +10:00
Lea Anthony
8a6c44963d Upgrade packages for js runtime 2020-09-02 08:51:21 +10:00
Lea Anthony
d31c5522e9 fix: revert location of wails config 2020-09-02 07:07:12 +10:00
Lea Anthony
6e5b2ce871 Update Contributors 2020-09-01 20:44:50 +10:00
Tim Kipp
c2c9c331cd refactor: attempt to refactor spacing issues detected by houndci 2020-09-01 20:43:20 +10:00
Tim Kipp
9d5280d4d6 feat(templates): updated react template to latest CRA implementation 2020-09-01 20:43:20 +10:00
Lea Anthony
0474f15c05 Simplify Store type conversions 2020-09-01 20:36:18 +10:00
Lea Anthony
ba0af0c16d Lint fix! 2020-08-31 21:17:04 +10:00
Lea Anthony
c9f1247284 Updated to use specialised update fuctions 2020-08-31 21:10:32 +10:00
Lea Anthony
baa661532d Now using reflection
Update() can now take specialised functions
2020-08-31 21:10:20 +10:00
Lea Anthony
43a5f410d9 Major updates
Automatic type conversion
Support struct data
Custom error handler
2020-08-31 20:00:39 +10:00
Lea Anthony
0ab6a93e0c Update runtime defs
Use runtime.Store instead of wails.Store
2020-08-30 16:07:53 +10:00
Lea Anthony
485df87560 Update vanilla template 2020-08-30 15:49:37 +10:00
Lea Anthony
176c447e87 Update to latest assets 2020-08-30 15:49:29 +10:00
Lea Anthony
aa9cb5e58e Add store to js runtime 2020-08-30 15:49:12 +10:00
Lea Anthony
9a7be38462 Update WailsInit checker 2020-08-30 15:47:32 +10:00
Lea Anthony
678c2194db Add Sync Store 2020-08-30 15:46:50 +10:00
Lea Anthony
9f9c9e27de Move runtime out to package 2020-08-30 15:46:31 +10:00
Lea Anthony
01a1288364 Merge branch 'develop' of github.com:wailsapp/wails into develop 2020-08-23 09:50:58 +10:00
Lea Anthony
5cd34e07b9 Release v1.7.2-pre3 2020-08-23 09:50:16 +10:00
Lea Anthony
2290f36939 Allow customising of info.plist 2020-08-23 09:49:54 +10:00
Lea Anthony
670986df5b Lintastic update 2020-08-21 23:44:22 +10:00
Lea Anthony
086cd02cd4 Lint fix 2020-08-21 23:44:22 +10:00
Lea Anthony
d8c8dd57fa Add artem to Contributors 2020-08-21 23:44:22 +10:00
Lea Anthony
2d158d449a Use double encoding for notify data 2020-08-21 23:44:22 +10:00
Lea Anthony
04577c242b Updated contributors 2020-08-04 05:06:50 +10:00
Zámbó Levente
ac0d4493d3 fix(bridge): fixed long message emit issue #469 2020-08-04 05:06:43 +10:00
Lea Anthony
b123156331 v1.7.2-pre2 2020-07-27 21:13:46 +10:00
Lea Anthony
23468ce7c7 Don't clean up windows files 2020-07-27 21:10:32 +10:00
Lea Anthony
7b6bb5e259 Update Contributors 2020-07-16 19:41:41 +10:00
konez2k
77bd34d601 refactor: use proper user config directory
replace os.UserHomeDir with os.UserConfigDir which manages properly the config folder based on platform.
2020-07-13 20:00:59 +10:00
konez2k
d750077a90 Update minimum go version to 1.13 2020-07-13 20:00:59 +10:00
konez2k
71df64087b refactor homedir to stdlib os.UserHomeDir (go1.12)
- remove mitchellh/go-homedir dependency
2020-07-13 20:00:59 +10:00
Lea Anthony
f86996705b v1.7.1 2020-07-03 20:07:02 +10:00
Lea Anthony
254aa664d7 Fix contributors 2020-07-03 20:07:02 +10:00
Lea Anthony
c8371ee824 v1.7.0-pre2 2020-07-03 20:07:02 +10:00
Lea Anthony
02e0250555 fix: vanilla template for windows 2020-07-03 20:07:02 +10:00
Lea Anthony
e1b025cab6 fix: windows icon name 2020-07-03 20:07:02 +10:00
Lea Anthony
626854f1b7 free filter memory 2020-07-03 20:07:02 +10:00
Lea Anthony
98468d1c4d v1.7.0-pre1 2020-07-03 20:07:02 +10:00
Lea Anthony
1062aeb136 Add ldflags option to build 2020-07-03 20:07:02 +10:00
Lea Anthony
5565f8ba94 v1.7.1 2020-07-03 20:05:01 +10:00
Lea Anthony
3bc91f20c5 Fix contributors 2020-07-03 13:54:02 +10:00
Lea Anthony
0a15cbaa1d v1.7.0-pre2 2020-07-03 13:52:51 +10:00
Lea Anthony
c650671265 fix: vanilla template for windows 2020-07-03 13:48:27 +10:00
Lea Anthony
7106c338df fix: windows icon name 2020-07-03 06:05:47 +10:00
Lea Anthony
be2cef4a63 free filter memory 2020-06-29 06:41:24 +10:00
Lea Anthony
ee93f3c1e6 v1.7.0-pre1 2020-06-22 20:14:10 +10:00
Lea Anthony
b9ffe53732 Add ldflags option to build 2020-06-22 20:13:15 +10:00
Lea Anthony
aa93e5d8d2 Merge branch 'develop' 2020-06-19 09:42:15 +10:00
Lea Anthony
bbc16fe03a Lint fixes 2020-06-19 09:38:01 +10:00
Lea Anthony
bdfc3ca631 Release v1.6.0 2020-06-19 09:26:19 +10:00
Lea Anthony
490d66cf77 Update Contributors 2020-06-19 09:11:34 +10:00
Lea Anthony
bce686d779 lint fixes 2020-06-19 08:52:44 +10:00
Lea Anthony
c0b0ef0200 Initial implementation of vanilla template 2020-06-19 08:52:44 +10:00
Florian
8e869baed7 fix: trim whitespaces from filters 2020-06-18 20:25:40 +10:00
Florian Didron
7522428d5c feat: add dialog filter for Cocoa 2020-06-18 20:25:40 +10:00
Florian Didron
bc570999e8 feat: file dialog filters for win32 2020-06-18 20:25:40 +10:00
Florian
393a4fceb2 feat: adds file filtering to gtk select/save dialogs 2020-06-16 18:57:25 +10:00
Lea Anthony
da20bcc8d2 v1.6.0-pre6 2020-06-06 14:42:59 +10:00
Lea Anthony
b091baa16f fix: Project options getting overwritten 2020-06-06 14:42:59 +10:00
Lea Anthony
ecaaafa9d9 v1.6.0-pre5 2020-05-27 12:49:14 +10:00
Lea Anthony
0009da9585 fix: ignore case of linux distro id 2020-05-27 12:48:28 +10:00
Lea Anthony
6235e83677 v1.6.0-pre4 2020-05-27 10:38:40 +10:00
Lea Anthony
9f93e7d979 fix default libs identifier for solus 2020-05-27 10:35:42 +10:00
Lea Anthony
949bc40317 Fix package detection for solus 2020-05-27 10:35:42 +10:00
Lea Anthony
81777f29d8 update package names 2020-05-27 10:35:42 +10:00
Lea Anthony
5d35dd3105 Initial support for solus 2020-05-27 10:35:42 +10:00
Lea Anthony
ad034d3950 fix: popos ID 2020-05-27 08:28:23 +10:00
Lea Anthony
98337df92d Support Pop!_OS
Added link to guide in request process
2020-05-27 08:28:23 +10:00
Dario Emerson
9cc417cf04 Add myself as contributor 2020-05-25 20:48:48 +10:00
Dario Emerson
030b954971 Correctly add icon to window on Windows 2020-05-25 20:48:48 +10:00
Dario Emerson
7a3ab27977 Changed icon scaling algorithm to CatmullRom 2020-05-25 17:32:49 +10:00
Dario Emerson
0e6265a9d7 Moved ico file writing later 2020-05-25 17:32:49 +10:00
Dario Emerson
b003a080b0 Improved Windows .ico generation 2020-05-25 17:32:49 +10:00
Lea Anthony
376ba743f4 Release v1.6.0-pre3 2020-05-22 07:10:58 +10:00
Lea Anthony
aa8ffff68d fix! 2020-05-21 22:05:17 +01:00
Lea Anthony
613a44af5e fix: icon size 2020-05-21 22:05:17 +01:00
Lea Anthony
2e15c4e045 intial support for .ico generation 2020-05-21 22:05:17 +01:00
Lea Anthony
421c13805d Merge branch 'dedo1911-408-default-serve-platform' into develop 2020-05-22 07:02:55 +10:00
Dario Emerson
cc204ab1f7 Fix missing import 2020-05-21 01:35:34 +02:00
Dario Emerson
a94a1a9fcb Merge branch 'develop' into 408-default-serve-platform 2020-05-21 01:33:22 +02:00
Dario Emerson
e860bd06ec Default Platform for serve command 2020-05-20 14:36:52 +02:00
Lea Anthony
6da02e6b44 v1.6.0-pre2 2020-05-17 20:17:36 +10:00
Lea Anthony
7cd78df1cd Support ArchLabs 2020-05-17 20:17:00 +10:00
Lea Anthony
bcecd854bc v1.6.0-pre1 2020-05-16 07:21:26 +10:00
Lea Anthony
6339f230f9 Fix CWE-126 2020-05-15 22:18:50 +01:00
Lea Anthony
131fd973cd lint fix 2020-05-15 22:18:50 +01:00
Lea Anthony
f1d16a03ec Disable F5 key on windows 2020-05-15 22:18:50 +01:00
Lea Anthony
cc99dcce80 Update contributors 2020-05-07 06:30:53 +10:00
Lea Anthony
a8ecc1e872 Merge branch 'develop' 2020-04-26 16:10:56 +10:00
Lea Anthony
6fc419ab48 Version v1.5.0 2020-04-26 16:00:39 +10:00
Lea Anthony
400bb37c03 Updated contributors 2020-04-26 14:39:11 +10:00
Lea Anthony
10fc7d762b fixes to OSX cross compile 2020-04-26 14:29:46 +10:00
Lea Anthony
61b9e50b8e Rapply -o suffix handling 2020-04-26 14:29:46 +10:00
Travis McLane
47916216de fixup 2020-04-26 14:29:46 +10:00
Travis McLane
a2b066decb wip: attempting to remove '.exe' from binary name 2020-04-26 14:29:46 +10:00
Travis McLane
8d1238289f correct issues with exe naming on windows 2020-04-26 14:29:46 +10:00
Travis McLane
b232b608fa remove extraneous println 2020-04-26 14:29:46 +10:00
Travis McLane
3b034ceadb correct issue with wails serve 2020-04-26 14:29:46 +10:00
Travis McLane
75be8f698a handle windows resources + cleanup when done
Moved the file cleanup to a CleanWindows function which simply deletes
all the files a Windows compilation generates for the icon resources.
Windows resource files MUST be in the same directory as the `go` source or
it will not be included in the resulting binary.
package.PackageWindows should be renamed to `GenerateWindowsResources`
since it doesn't actually package anything but rather generates the
various resources that are used during compilation.
2020-04-26 14:29:46 +10:00
Travis McLane
a78acbb247 continueOnError for RemoveFiles
Allow the purge to continue even if one of the files fails to be removed.
2020-04-26 14:29:46 +10:00
Travis McLane
6e29d1b087 docker process updates
- inform user we are starting to pull image and it may take a while
- inform user the target platform being built using docker image
2020-04-26 14:29:46 +10:00
Travis McLane
8171b644ca use 'build' dir for windows resources + cleanup 2020-04-26 14:29:46 +10:00
Travis McLane
a691ef0580 ensure OS X App is built inside 'build' dir 2020-04-26 14:29:46 +10:00
Travis McLane
151b4bff06 generate mewn files + cleanup 2020-04-26 14:29:46 +10:00
Travis McLane
fb093c58d2 cleanup: build native vs docker
- BuildDocker: uses wailsapp/xgo:latest docker container to build
- BuildNative: builds on the host for the host platform
2020-04-26 14:29:46 +10:00
Travis McLane
b68c69f4eb run tests first to fail fast 2020-04-26 14:29:46 +10:00
Travis McLane
8405ce37f9 remove "'" around ldflags
The single quote is needed for running the command in the shell directly.
When run inside the Go command 'os/exec' it is not removed and is
preventing the flags from being processed correctly inside the Docker image.
2020-04-26 14:29:46 +10:00
Travis McLane
76edc7976e use userid=1000 if user.Current() is not an integer 2020-04-26 14:29:46 +10:00
Travis McLane
2ab8fc37ab remove linux/i386 (buggy) 2020-04-26 14:29:46 +10:00
Travis McLane
c66e9cba5a cross-platform builds w/ wailsapp/xgo:latest
- use docker directly instead of `xgo` to call docker.
- use custom xgo docker image
2020-04-26 14:29:46 +10:00
Travis McLane
b33794c883 use wailsapp/xgo:latest for windres call 2020-04-26 14:29:46 +10:00
Travis McLane
5d719685d3 cross compilation command + help 2020-04-26 14:29:46 +10:00
Travis McLane
5b2e5b1d85 chore: correct build tag on app_other 2020-04-26 14:29:46 +10:00
Travis McLane
773bad45f6 build.sh improvements
- run `go test ./...` and STOP on error
- run `go build` and STOP on error
- run `go install` and STOP on error
2020-04-26 14:29:46 +10:00
Travis McLane
404cd7d14e linux packaging (noop) + list platforms 2020-04-26 14:29:46 +10:00
Travis McLane
ea703acfed generated files 2020-04-26 14:29:46 +10:00
konez2k
4750ee5d22 update image version 2020-04-26 14:29:46 +10:00
konez2k
1bd9408e9e add docker version pinning 2020-04-26 14:29:46 +10:00
konez2k
34202f1a2f add docker webkit/gtk support 2020-04-26 14:29:46 +10:00
konez2k
9d6d77f768 initial docker support 2020-04-26 14:29:46 +10:00
konez2k
fbff822ea9 check if xgo and docker are installed 2020-04-26 14:29:46 +10:00
konez2k
da28683ddc create build directory if not exists 2020-04-26 14:29:46 +10:00
konez2k
b7f0155c3b add cross-platform build and packaging 2020-04-26 14:29:46 +10:00
konez2k
f220e0e5bd add FindFile to FSHelper 2020-04-26 14:29:46 +10:00
konez2k
b5bad14124 add arch to project options 2020-04-26 14:29:46 +10:00
konez2k
0f36ee7030 add cross-compile flag to build command 2020-04-26 14:29:46 +10:00
konez2k
58761ccff4 embed assets internally using mewn 2020-04-26 14:29:46 +10:00
Lea Anthony
059cbbfd9a doc: updated contributors 2020-04-26 07:00:05 +10:00
Jarek Cora
339b0ecbaf feat: added openSUSE support 2020-04-26 06:56:53 +10:00
Travis McLane
615cc55b31 bump channel capacity to 100 2020-03-26 05:21:35 +11:00
Travis McLane
577b59aa89 writer channel to protect websocket 2020-03-26 05:21:35 +11:00
Travis McLane
45a507673e custom logger for Bridge.session 2020-03-26 05:21:35 +11:00
Lea Anthony
eb8a1f2303 Add Reuben to Contributors 2020-03-24 22:04:48 +11:00
Reuben
566914a9c2 fix RunCommandArray bug where if exec.LookPath returns an error program is an empty string in the log. 2020-03-24 22:02:56 +11:00
Lea Anthony
0866a633a3 fix optional callback data in definition 2020-03-21 14:32:17 +11:00
konez2k
0e7981cf87 Macos notarize fix (#362)
* Develop (#343)

* Support Distribution 'ArcoLinux' #310 (#312)

* Support Distribution 'ArcoLinux' #310

* Vuetify2 support (resurrected from git@github.com:MichaelHipp/wails.git) (#315)

* Initial create of vuetify2-basic folder

* Change template descr of vuetify-basic to say Vuetify 1.5

* Get vuetify2 template installing vuetify v2.0 (but with styling probs)

* Update App.vue, HelloWorld.vue for Vuetify v2

* Remove babel-polyfill, add mdi/font

* fix: codacy corrections

* fix: babel -> core-js, regenerator-runtime

Co-authored-by: Michael Hipp <michael@redmule.com>
Co-authored-by: Lea Anthony <lea.anthony@gmail.com>

* Update Contributors

* v1.0.2-pre1

* [313-remote-conn] allow remote connections to the websocket bridge (#314)

* [313-remote-conn] feat: compute wsURL based on window.location

* [313-remote-conn] feat: allow any host to connect to vue server

removing the 'host: "localhost"' specification causes the development
server to listen on all interfaces.

* [313-remote-conn] feat: allow any host to connect to angular dev server

* test: reinject tabs

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>

* fix: disable host check for vuetify 2 template

* v1.0.2-pre2

* fix: shutdown ipcmanager

* use channel to trigger shutdown

* load linuxdb from relative path

* Feat manjaro arm & deepin (#324)

* feat: new distros: manjaroARM & Deepin

* v1.0.2-pre3

* [326-platform-raspbian] feat: implement raspbian support (#327)

* fix: emit arguments (#306)

* v1.0.2-pre4 Raspbarian support

* Initial support for Typescript decl file (#330)

* v1.0.2-pre5

* revert to Go 1.12

* New CI (#331)

* prepare

* new CI/github actions

* Rename later-pre.yml to latest-pre.yml

* Update latest-pre.yml

* Update README.md

* Ensure version in go.mod is up to date (#339)

* release v1.0.2-pre6

* Fix typescript generation

* Release v1.0.2-pre7

* 316-multi-bridge-conn (#317)

* [316-multi-bridge-conn] feat: use callback func for bridge response

* [316-multi-bridge-conn] feat: implement multiple session support

* split client handling portion into 'session'
* keep track of sessions by remote address (ip & port)
* notify each of the sessions anytime an event comes across the bus

* [316-multi-bridge-conn] chore: move bridge files to package

* [316-multi-bridge-conn] chore: remove deprecated Callback function

The Callback function is no longer needed for the operation of
the frontend callback since the ipc.Dispatch function now requires
a callback function to be provided as an argument.
This function can be a private function since it is passed by reference.

* [316-multi-bridge-conn] chore: make webview.Callback private

* [316-multi-bridge-conn] chore: remove unused injectCSS function

I believe a slightly better method of doing this might need to be devised
if it is needed in the future. I presume it should collect the values
into a cache and then inject it into each sesssion as it appears.

* [316-multi-bridge-conn] ensure wails:ready event is emitted

Event is only emitted for the first session created from the Bridge.

* [316-multi-bridge-conn] emit events for session lifecycle

Emit an event for each session started and ended.

* [316-multi-bridge-conn] fix: session handling fixes

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>

* Release v1.0.2-pre8

* Release v1.0.2

Co-authored-by: Byron <ktc@protonmail.com>
Co-authored-by: Travis McLane <tmclane@gmail.com>
Co-authored-by: Michael Hipp <michael@redmule.com>

* Macos notarize fix

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
Co-authored-by: Byron <ktc@protonmail.com>
Co-authored-by: Travis McLane <tmclane@gmail.com>
Co-authored-by: Michael Hipp <michael@redmule.com>
2020-03-17 23:29:43 +11:00
Lea Anthony
ce3bd8f56f Update dependencies (#354)
* update vuetify2 dependencies

* update eslint

* update vuetify1 deps

* update vuebasic deps

* update react deps

* bump version
2020-03-04 06:39:34 +10:00
Lea Anthony
79188c503f Develop (#343)
* Support Distribution 'ArcoLinux' #310 (#312)

* Support Distribution 'ArcoLinux' #310

* Vuetify2 support (resurrected from git@github.com:MichaelHipp/wails.git) (#315)

* Initial create of vuetify2-basic folder

* Change template descr of vuetify-basic to say Vuetify 1.5

* Get vuetify2 template installing vuetify v2.0 (but with styling probs)

* Update App.vue, HelloWorld.vue for Vuetify v2

* Remove babel-polyfill, add mdi/font

* fix: codacy corrections

* fix: babel -> core-js, regenerator-runtime

Co-authored-by: Michael Hipp <michael@redmule.com>
Co-authored-by: Lea Anthony <lea.anthony@gmail.com>

* Update Contributors

* v1.0.2-pre1

* [313-remote-conn] allow remote connections to the websocket bridge (#314)

* [313-remote-conn] feat: compute wsURL based on window.location

* [313-remote-conn] feat: allow any host to connect to vue server

removing the 'host: "localhost"' specification causes the development
server to listen on all interfaces.

* [313-remote-conn] feat: allow any host to connect to angular dev server

* test: reinject tabs

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>

* fix: disable host check for vuetify 2 template

* v1.0.2-pre2

* fix: shutdown ipcmanager

* use channel to trigger shutdown

* load linuxdb from relative path

* Feat manjaro arm & deepin (#324)

* feat: new distros: manjaroARM & Deepin

* v1.0.2-pre3

* [326-platform-raspbian] feat: implement raspbian support (#327)

* fix: emit arguments (#306)

* v1.0.2-pre4 Raspbarian support

* Initial support for Typescript decl file (#330)

* v1.0.2-pre5

* revert to Go 1.12

* New CI (#331)

* prepare

* new CI/github actions

* Rename later-pre.yml to latest-pre.yml

* Update latest-pre.yml

* Update README.md

* Ensure version in go.mod is up to date (#339)

* release v1.0.2-pre6

* Fix typescript generation

* Release v1.0.2-pre7

* 316-multi-bridge-conn (#317)

* [316-multi-bridge-conn] feat: use callback func for bridge response

* [316-multi-bridge-conn] feat: implement multiple session support

* split client handling portion into 'session'
* keep track of sessions by remote address (ip & port)
* notify each of the sessions anytime an event comes across the bus

* [316-multi-bridge-conn] chore: move bridge files to package

* [316-multi-bridge-conn] chore: remove deprecated Callback function

The Callback function is no longer needed for the operation of
the frontend callback since the ipc.Dispatch function now requires
a callback function to be provided as an argument.
This function can be a private function since it is passed by reference.

* [316-multi-bridge-conn] chore: make webview.Callback private

* [316-multi-bridge-conn] chore: remove unused injectCSS function

I believe a slightly better method of doing this might need to be devised
if it is needed in the future. I presume it should collect the values
into a cache and then inject it into each sesssion as it appears.

* [316-multi-bridge-conn] ensure wails:ready event is emitted

Event is only emitted for the first session created from the Bridge.

* [316-multi-bridge-conn] emit events for session lifecycle

Emit an event for each session started and ended.

* [316-multi-bridge-conn] fix: session handling fixes

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>

* Release v1.0.2-pre8

* Release v1.0.2

Co-authored-by: Byron <ktc@protonmail.com>
Co-authored-by: Travis McLane <tmclane@gmail.com>
Co-authored-by: Michael Hipp <michael@redmule.com>
2020-02-08 09:58:16 +11:00
89 changed files with 5426 additions and 4137 deletions

View File

@@ -13,10 +13,10 @@ jobs:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Set up Go 1.12
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.12
go-version: 1.13
id: go
- name: Check out code into the Go module directory
@@ -27,6 +27,6 @@ jobs:
go get -v -d ./...
- name: Build
run: go build -v ./cmd/wails
- name: Test
run: ./wails version

View File

@@ -13,10 +13,10 @@ jobs:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Set up Go 1.12
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.12
go-version: 1.13
id: go
- name: Check out code into the Go module directory
@@ -27,6 +27,6 @@ jobs:
go get -v -d ./...
- name: Build
run: go build -v ./cmd/wails
- name: Test
run: ./wails version

View File

@@ -4,7 +4,7 @@ on:
branches:
- master
tags:
- '!**pre**'
- '!**pre**'
jobs:
build:
@@ -15,10 +15,10 @@ jobs:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Set up Go 1.12
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.12
go-version: 1.13
id: go
- name: Check out code into the Go module directory
@@ -29,6 +29,6 @@ jobs:
go get -v -d ./...
- name: Build
run: go build -v ./cmd/wails
- name: Test
run: ./wails version

View File

@@ -15,11 +15,23 @@ Wails is what it is because of the time and effort given by these great people.
* [admin_3.exe](https://github.com/bh90210)
* [iceleo-com](https://github.com/iceleo-com)
* [fallendusk](https://github.com/fallendusk)
* [Florian Didran](https://github.com/fdidron)
* [Nikolai Zimmermann](https://github.com/Chronophylos)
* [Toyam Cox](https://github.com/Vaelatern)
* [Robin Eklind](https://github.com/mewmew)
* [Kris Raney](https://github.com/kraney)
* [Jack Mordaunt](https://github.com/JackMordaunt)
* [Michael Hipp](https://github.com/MichaelHipp)
* [Travis McLane](https://github.com/tmclane)
* [Travis McLane](https://github.com/tmclane)
* [Reuben Thomas-Davis](https://github.com/Rested)
* [Jarek](https://github.com/Jarek-SRT)
* [Konez2k](https://github.com/konez2k)
* [msms](https://github.com/sayuthisobri)
* [dedo1911](https://github.com/dedo1911)
* [Florian Didron](https://github.com/fdidron)
* [Christopher Murphy](https://github.com/Splode)
* [Zámbó, Levente](https://github.com/Lyimmi)
* [artem](https://github.com/Unix4ever)
* [Tim Kipp](https://github.com/timkippdev)
* [Dmitry Gomzyakov](https://github.com/kyoto44)
* [Arthur Wiebe](https://github.com/artooro)
* [Ilgıt Yıldırım](https://github.com/ilgityildirim)

View File

@@ -12,7 +12,6 @@
<a href="https://houndci.com"><img src="https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg"/></a>
<a href="https://github.com/avelino/awesome-go" rel="nofollow"><img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome"></a>
<a href="https://github.com/wailsapp/wails/workflows/release/badge.svg?branch=master" rel="nofollow"><img src="https://github.com/wailsapp/wails/workflows/release/badge.svg?branch=master" alt="Release Pipelines"></a>
<a href="https://github.com/wailsapp/wails/workflows/latest-pre/badge.svg?branch=masterr" rel="nofollow"><img src="https://github.com/wailsapp/wails/workflows/latest-pre/badge.svg?branch=master" alt="Pre-Release Pipelines"></a>
</p>
The traditional method of providing web interfaces to Go programs is via a built-in web server. Wails offers a different approach: it provides the ability to wrap both Go code and a web frontend into a single binary. Tools are provided to make this easy for you by handling project creation, compilation and bundling. All you have to do is get creative!
@@ -36,7 +35,7 @@ The official docs can be found at [https://wails.app](https://wails.app).
Wails uses cgo to bind to the native rendering engines so a number of platform dependent libraries are needed as well as an installation of Go. The basic requirements are:
- Go 1.12
- Go 1.13
- npm
### MacOS
@@ -55,9 +54,9 @@ _Debian: 8, 9, 10_
_Ubuntu: 16.04, 18.04, 19.04_
_Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_
_Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_, Pop!_OS
#### Arch Linux
#### Arch Linux / ArchLabs / Ctlos Linux
`sudo pacman -S webkit2gtk gtk3`

13
app.go
View File

@@ -13,6 +13,7 @@ import (
"github.com/wailsapp/wails/lib/ipc"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/renderer"
wailsruntime "github.com/wailsapp/wails/runtime"
)
// -------------------------------- Compile time Flags ------------------------------
@@ -20,6 +21,16 @@ import (
// BuildMode indicates what mode we are in
var BuildMode = cmd.BuildModeProd
// Runtime is the Go Runtime struct
type Runtime = wailsruntime.Runtime
// Store is a state store used for syncing with
// the front end
type Store = wailsruntime.Store
// CustomLogger is a specialised logger
type CustomLogger = logger.CustomLogger
// ----------------------------------------------------------------------------------
// App defines the main application struct
@@ -125,7 +136,7 @@ func (a *App) start() error {
a.ipc.Start(a.eventManager, a.bindingManager)
// Create the runtime
a.runtime = NewRuntime(a.eventManager, a.renderer)
a.runtime = wailsruntime.NewRuntime(a.eventManager, a.renderer)
// Start binding manager and give it our renderer
err = a.bindingManager.Start(a.renderer, a.runtime)

View File

@@ -1,4 +1,4 @@
// +build +linux +darwin !windows
// +build linux darwin !windows
package wails

File diff suppressed because one or more lines are too long

View File

@@ -12,6 +12,7 @@ import (
"path"
"path/filepath"
"runtime"
"strings"
"github.com/leaanthony/slicer"
)
@@ -47,6 +48,22 @@ func (fs *FSHelper) FileExists(path string) bool {
return fi.Mode().IsRegular()
}
// FindFile returns the first occurrence of match inside path.
func (fs *FSHelper) FindFile(path, match string) (string, error) {
files, err := ioutil.ReadDir(path)
if err != nil {
return "", err
}
for _, f := range files {
if !f.IsDir() && strings.Contains(f.Name(), match) {
return f.Name(), nil
}
}
return "", fmt.Errorf("file not found")
}
// CreateFile creates a file at the given filename location with the contents
// set to the given data. It will create intermediary directories if needed.
func (fs *FSHelper) CreateFile(filename string, data []byte) error {
@@ -100,10 +117,10 @@ func (fs *FSHelper) RemoveFile(filename string) error {
}
// RemoveFiles removes the given filenames
func (fs *FSHelper) RemoveFiles(files []string) error {
func (fs *FSHelper) RemoveFiles(files []string, continueOnError bool) error {
for _, filename := range files {
err := os.Remove(filename)
if err != nil {
if err != nil && !continueOnError {
return err
}
}

View File

@@ -5,16 +5,21 @@ import (
"io/ioutil"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/leaanthony/mewn"
"github.com/leaanthony/mewn/lib"
"github.com/leaanthony/slicer"
"github.com/leaanthony/spinner"
)
const xgoVersion = "1.0.1"
var fs = NewFSHelper()
// ValidateFrontendConfig checks if the frontend config is valid
@@ -56,21 +61,148 @@ func InstallGoDependencies(verbose bool) error {
return nil
}
// BuildApplication will attempt to build the project based on the given inputs
func BuildApplication(binaryName string, forceRebuild bool, buildMode string, packageApp bool, projectOptions *ProjectOptions) error {
// EmbedAssets will embed the built frontend assets via mewn.
func EmbedAssets() ([]string, error) {
mewnFiles := lib.GetMewnFiles([]string{}, false)
// Generate Windows assets if needed
if runtime.GOOS == "windows" {
cleanUp := !packageApp
err := NewPackageHelper().PackageWindows(projectOptions, cleanUp)
if err != nil {
return err
}
referencedAssets, err := lib.GetReferencedAssets(mewnFiles)
if err != nil {
return []string{}, err
}
// Check Mewn is installed
err := CheckMewn(projectOptions.Verbose)
targetFiles := []string{}
for _, referencedAsset := range referencedAssets {
packfileData, err := lib.GeneratePackFileString(referencedAsset, false)
if err != nil {
return []string{}, err
}
targetFile := filepath.Join(referencedAsset.BaseDir, referencedAsset.PackageName+"-mewn.go")
targetFiles = append(targetFiles, targetFile)
ioutil.WriteFile(targetFile, []byte(packfileData), 0644)
}
return targetFiles, nil
}
func InitializeCrossCompilation(verbose bool) error {
// Check Docker
if err := CheckIfInstalled("docker"); err != nil {
return err
}
var packSpinner *spinner.Spinner
msg := fmt.Sprintf("Pulling wailsapp/xgo:%s docker image... (may take a while)", xgoVersion)
if !verbose {
packSpinner = spinner.New(msg)
packSpinner.SetSpinSpeed(50)
packSpinner.Start()
} else {
println(msg)
}
err := NewProgramHelper(verbose).RunCommandArray([]string{"docker",
"pull", fmt.Sprintf("wailsapp/xgo:%s", xgoVersion)})
if err != nil {
if packSpinner != nil {
packSpinner.Error()
}
return err
}
if packSpinner != nil {
packSpinner.Success()
}
return nil
}
// BuildDocker builds the project using the cross compiling wailsapp/xgo:<xgoVersion> container
func BuildDocker(binaryName string, buildMode string, projectOptions *ProjectOptions) error {
var packSpinner *spinner.Spinner
if buildMode == BuildModeBridge {
return fmt.Errorf("you cant serve the application in cross-compilation")
}
// Check build directory
buildDirectory := filepath.Join(fs.Cwd(), "build")
if !fs.DirExists(buildDirectory) {
fs.MkDir(buildDirectory)
}
buildCommand := slicer.String()
userid := 1000
user, _ := user.Current()
if i, err := strconv.Atoi(user.Uid); err == nil {
userid = i
}
for _, arg := range []string{
"docker",
"run",
"--rm",
"-v", fmt.Sprintf("%s:/build", filepath.Join(fs.Cwd(), "build")),
"-v", fmt.Sprintf("%s:/source", fs.Cwd()),
"-e", fmt.Sprintf("LOCAL_USER_ID=%v", userid),
"-e", fmt.Sprintf("FLAG_LDFLAGS=%s", ldFlags(projectOptions, buildMode)),
"-e", "FLAG_V=false",
"-e", "FLAG_X=false",
"-e", "FLAG_RACE=false",
"-e", "FLAG_BUILDMODE=default",
"-e", "FLAG_TRIMPATH=false",
"-e", fmt.Sprintf("TARGETS=%s/%s", projectOptions.Platform, projectOptions.Architecture),
"-e", "GOPROXY=",
"-e", "GO111MODULE=on",
} {
buildCommand.Add(arg)
}
if projectOptions.GoPath != "" {
buildCommand.Add("-v")
buildCommand.Add(fmt.Sprintf("%s:/go", projectOptions.GoPath))
}
buildCommand.Add(fmt.Sprintf("wailsapp/xgo:%s", xgoVersion))
buildCommand.Add(".")
compileMessage := fmt.Sprintf(
"Packing + Compiling project for %s/%s using docker image wailsapp/xgo:%s",
projectOptions.Platform, projectOptions.Architecture, xgoVersion)
if buildMode == BuildModeDebug {
compileMessage += " (Debug Mode)"
}
if !projectOptions.Verbose {
packSpinner = spinner.New(compileMessage + "...")
packSpinner.SetSpinSpeed(50)
packSpinner.Start()
} else {
println(compileMessage)
}
err := NewProgramHelper(projectOptions.Verbose).RunCommandArray(buildCommand.AsSlice())
if err != nil {
if packSpinner != nil {
packSpinner.Error()
}
return err
}
if packSpinner != nil {
packSpinner.Success()
}
return nil
}
// BuildNative builds on the target platform itself.
func BuildNative(binaryName string, forceRebuild bool, buildMode string, projectOptions *ProjectOptions) error {
// Check Mewn is installed
if err := CheckMewn(projectOptions.Verbose); err != nil {
return err
}
if err := CheckWindres(); err != nil {
return err
}
@@ -90,18 +222,13 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
}
buildCommand := slicer.String()
buildCommand.Add("mewn")
if buildMode == BuildModeBridge {
// Ignore errors
buildCommand.Add("-i")
}
buildCommand.Add("go")
buildCommand.Add("build")
if binaryName != "" {
// Alter binary name based on OS
switch runtime.GOOS {
switch projectOptions.Platform {
case "windows":
if !strings.HasSuffix(binaryName, ".exe") {
binaryName += ".exe"
@@ -111,7 +238,7 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
binaryName = strings.TrimSuffix(binaryName, ".exe")
}
}
buildCommand.Add("-o", binaryName)
buildCommand.Add("-o", filepath.Join("build", binaryName))
}
// If we are forcing a rebuild
@@ -119,31 +246,13 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
buildCommand.Add("-a")
}
// Setup ld flags
ldflags := "-w -s "
if buildMode == BuildModeDebug {
ldflags = ""
buildCommand.AddSlice([]string{"-ldflags", ldFlags(projectOptions, buildMode)})
if projectOptions.Verbose {
fmt.Printf("Command: %v\n", buildCommand.AsSlice())
}
// Add windows flags
if runtime.GOOS == "windows" && buildMode == BuildModeProd {
ldflags += "-H windowsgui "
}
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode
// If we wish to generate typescript
if projectOptions.typescriptDefsFilename != "" {
cwd, err := os.Getwd()
if err != nil {
return err
}
filename := filepath.Join(cwd, projectOptions.FrontEnd.Dir, projectOptions.typescriptDefsFilename)
ldflags += " -X github.com/wailsapp/wails/lib/binding.typescriptDefinitionFilename=" + filename
}
buildCommand.AddSlice([]string{"-ldflags", ldflags})
err = NewProgramHelper(projectOptions.Verbose).RunCommandArray(buildCommand.AsSlice())
err := NewProgramHelper(projectOptions.Verbose).RunCommandArray(buildCommand.AsSlice())
if err != nil {
if packSpinner != nil {
packSpinner.Error()
@@ -154,7 +263,57 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
packSpinner.Success()
}
// packageApp
return nil
}
// BuildApplication will attempt to build the project based on the given inputs
func BuildApplication(binaryName string, forceRebuild bool, buildMode string, packageApp bool, projectOptions *ProjectOptions) error {
var err error
// embed resources
targetFiles, err := EmbedAssets()
if err != nil {
return err
}
if projectOptions.CrossCompile {
if err := InitializeCrossCompilation(projectOptions.Verbose); err != nil {
return err
}
}
helper := NewPackageHelper(projectOptions.Platform)
// Generate windows resources
if projectOptions.Platform == "windows" {
if err := helper.PackageWindows(projectOptions, false); err != nil {
return err
}
}
// cleanup temporary embedded assets
defer func() {
for _, filename := range targetFiles {
if err := os.Remove(filename); err != nil {
fmt.Println(err)
}
}
// Removed by popular demand
// TODO: Potentially add a flag to cleanup
// if projectOptions.Platform == "windows" {
// helper.CleanWindows(projectOptions)
// }
}()
if projectOptions.CrossCompile {
err = BuildDocker(binaryName, buildMode, projectOptions)
} else {
err = BuildNative(binaryName, forceRebuild, buildMode, projectOptions)
}
if err != nil {
return err
}
if packageApp {
err = PackageApplication(projectOptions)
if err != nil {
@@ -167,22 +326,14 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
// PackageApplication will attempt to package the application in a platform dependent way
func PackageApplication(projectOptions *ProjectOptions) error {
// Package app
message := "Generating .app"
if runtime.GOOS == "windows" {
err := CheckWindres()
if err != nil {
return err
}
message = "Generating resource bundle"
}
var packageSpinner *spinner.Spinner
if projectOptions.Verbose {
packageSpinner = spinner.New(message)
packageSpinner = spinner.New("Packaging application...")
packageSpinner.SetSpinSpeed(50)
packageSpinner.Start()
}
err := NewPackageHelper().Package(projectOptions)
err := NewPackageHelper(projectOptions.Platform).Package(projectOptions)
if err != nil {
if packageSpinner != nil {
packageSpinner.Error()
@@ -244,7 +395,7 @@ func CheckMewn(verbose bool) (err error) {
// CheckWindres checks if Windres is installed and if not, aborts
func CheckWindres() (err error) {
if runtime.GOOS != "windows" {
if runtime.GOOS != "windows" { // FIXME: Handle windows cross-compile for windows!
return nil
}
programHelper := NewProgramHelper()
@@ -254,6 +405,15 @@ func CheckWindres() (err error) {
return nil
}
// CheckIfInstalled returns if application is installed
func CheckIfInstalled(application string) (err error) {
programHelper := NewProgramHelper()
if !programHelper.IsInstalled(application) {
return fmt.Errorf("%s not installed. Ensure you have installed %s correctly", application, application)
}
return nil
}
// InstallFrontendDeps attempts to install the frontend dependencies based on the given options
func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forceRebuild bool, caller string) error {
@@ -377,7 +537,7 @@ func ServeProject(projectOptions *ProjectOptions, logger *Logger) error {
time.Sleep(2 * time.Second)
logger.Green(">>>>> To connect, you will need to run '" + projectOptions.FrontEnd.Serve + "' in the '" + projectOptions.FrontEnd.Dir + "' directory <<<<<")
}()
location, err := filepath.Abs(projectOptions.BinaryName)
location, err := filepath.Abs(filepath.Join("build", projectOptions.BinaryName))
if err != nil {
return err
}
@@ -393,3 +553,33 @@ func ServeProject(projectOptions *ProjectOptions, logger *Logger) error {
return nil
}
func ldFlags(po *ProjectOptions, buildMode string) string {
// Setup ld flags
ldflags := "-w -s "
if buildMode == BuildModeDebug {
ldflags = ""
}
// Add windows flags
if po.Platform == "windows" && buildMode == BuildModeProd {
ldflags += "-H windowsgui "
}
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode
// Add additional ldflags passed in via the `ldflags` cli flag
if len(po.LdFlags) > 0 {
ldflags += " " + po.LdFlags
}
// If we wish to generate typescript
if po.typescriptDefsFilename != "" {
cwd, err := os.Getwd()
if err == nil {
filename := filepath.Join(cwd, po.FrontEnd.Dir, po.typescriptDefsFilename)
ldflags += " -X github.com/wailsapp/wails/lib/binding.typescriptDefinitionFilename=" + filename
}
}
return ldflags
}

View File

@@ -53,6 +53,18 @@ const (
Deepin
// Raspbian distribution
Raspbian
// Tumbleweed (OpenSUSE) distribution
Tumbleweed
// Leap (OpenSUSE) distribution
Leap
// ArchLabs distribution
ArchLabs
// PopOS distribution
PopOS
// Solus distribution
Solus
// Ctlos Linux distribution
Ctlos
)
// DistroInfo contains all the information relating to a linux distribution
@@ -101,7 +113,7 @@ func parseOsRelease(osRelease string) *DistroInfo {
}
switch splitLine[0] {
case "ID":
osID = strings.Trim(splitLine[1], "\"")
osID = strings.ToLower(strings.Trim(splitLine[1], "\""))
case "NAME":
osNAME = strings.Trim(splitLine[1], "\"")
case "VERSION_ID":
@@ -117,6 +129,10 @@ func parseOsRelease(osRelease string) *DistroInfo {
result.Distribution = CentOS
case "arch":
result.Distribution = Arch
case "archlabs":
result.Distribution = ArchLabs
case "ctlos":
result.Distribution = Ctlos
case "debian":
result.Distribution = Debian
case "ubuntu":
@@ -147,6 +163,14 @@ func parseOsRelease(osRelease string) *DistroInfo {
result.Distribution = Deepin
case "raspbian":
result.Distribution = Raspbian
case "opensuse-tumbleweed":
result.Distribution = Tumbleweed
case "opensuse-leap":
result.Distribution = Leap
case "pop":
result.Distribution = PopOS
case "solus":
result.Distribution = Solus
default:
result.Distribution = Unknown
}
@@ -183,6 +207,17 @@ func DpkgInstalled(packageName string) (bool, error) {
return exitCode == 0, nil
}
// EOpkgInstalled uses dpkg to see if a package is installed
func EOpkgInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
eopkg := program.FindProgram("eopkg")
if eopkg == nil {
return false, fmt.Errorf("cannot check dependencies: eopkg not found")
}
stdout, _, _, _ := eopkg.Run("info", packageName)
return strings.HasPrefix(stdout, "Installed"), nil
}
// PacmanInstalled uses pacman to see if a package is installed.
func PacmanInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
@@ -254,5 +289,9 @@ func RequestSupportForDistribution(distroInfo *DistroInfo) error {
fmt.Println("Opening browser to file request.")
browser.OpenURL(fullURL + url.PathEscape(params))
result = Prompt("We have a guide for adding support for your distribution. Would you like to view it?", "yes")
if strings.ToLower(result) == "yes" {
browser.OpenURL("https://wails.app/guides/distro/")
}
return nil
}

View File

@@ -22,5 +22,25 @@ UBUNTU_CODENAME=bionic
if result.Distribution != Ubuntu {
t.Errorf("expected 'Ubuntu' ID but got '%d'", result.Distribution)
}
}
func TestTumbleweedDetection(t *testing.T) {
osrelease := `
NAME="openSUSE Tumbleweed"
# VERSION="20200414"
ID="opensuse-tumbleweed"
ID_LIKE="opensuse suse"
VERSION_ID="20200414"
PRETTY_NAME="openSUSE Tumbleweed"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:opensuse:tumbleweed:20200414"
BUG_REPORT_URL="https://bugs.opensuse.org"
HOME_URL="https://www.opensuse.org/"
LOGO="distributor-logo"
`
result := parseOsRelease(osrelease)
if result.Distribution != Tumbleweed {
t.Errorf("expected 'Tumbleweed' ID but got '%d'", result.Distribution)
}
}

View File

@@ -28,6 +28,15 @@ distributions:
gccversioncommand: &gccdumpfullversion -dumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
pop:
id: pop
releases:
default:
version: default
name: Pop!_OS
gccversioncommand: &gccdumpfullversion -dumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
kali:
id: kali
releases:
@@ -176,6 +185,24 @@ distributions:
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
archlabs:
id: archlabs
releases:
default:
version: default
name: ArchLabs
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
ctlos:
id: ctlos
releases:
default:
version: default
name: Ctlos Linux
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
manjaro:
id: manjaro
releases:
@@ -223,3 +250,51 @@ distributions:
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
solus:
id: solus
releases:
default:
version: default
name: Solus
gccversioncommand: *gccdumpfullversion
programs: &solusdefaultprograms
- name: gcc
help: Please install with `sudo eopkg it -c system.devel` and try again
- name: pkg-config
help: Please install with `sudo eopkg it -c system.devel` and try again
- name: npm
help: Please install with `sudo eopkg it nodejs` and try again
libraries: &solusdefaultlibraries
- name: libgtk-3-devel
help: Please install with `sudo eopkg it libgtk-3-devel` and try again
- name: libwebkit-gtk-devel
help: Please install with `sudo eopkg it libwebkit-gtk-devel` and try again
opensuse-tumbleweed:
id: opensuse-tumbleweed
releases:
default:
version: default
name: openSUSE Tumbleweed
gccversioncommand: *gccdumpfullversion
programs: &opensusedefaultprograms
- name: gcc
help: Please install with `sudo zypper in gcc-c++` and try again
- name: pkg-config
help: Please install with `sudo zypper in pkgconf-pkg-config` and try again
- name: npm
help: Please install `sudo zypper in nodejs` and try again
libraries: &opensusedefaultlibraries
- name: gtk3-devel
help: Please install with `sudo zypper in gtk3-devel` and try again
- name: webkit2gtk3-devel
help: Please install with `sudo zypper in webkit2gtk3-devel` and try again
opensuse-leap:
id: opensuse-leap
releases:
default:
version: default
name: openSUSE Leap
gccversioncommand: *gccdumpfullversion
programs: *opensusedefaultprograms
libraries: *opensusedefaultlibraries

View File

@@ -1,9 +1,12 @@
package cmd
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"image"
"image/png"
"io/ioutil"
"os"
"path"
@@ -14,21 +17,24 @@ import (
"time"
"github.com/jackmordaunt/icns"
"golang.org/x/image/draw"
)
// PackageHelper helps with the 'wails package' command
type PackageHelper struct {
fs *FSHelper
log *Logger
system *SystemHelper
platform string
fs *FSHelper
log *Logger
system *SystemHelper
}
// NewPackageHelper creates a new PackageHelper!
func NewPackageHelper() *PackageHelper {
func NewPackageHelper(platform string) *PackageHelper {
return &PackageHelper{
fs: NewFSHelper(),
log: NewLogger(),
system: NewSystemHelper(),
platform: platform,
fs: NewFSHelper(),
log: NewLogger(),
system: NewSystemHelper(),
}
}
@@ -53,6 +59,111 @@ func newPlistData(title, exe, packageID, version, author string) *plistData {
}
}
type windowsIcoHeader struct {
_ uint16
imageType uint16
imageCount uint16
}
type windowsIcoDescriptor struct {
width uint8
height uint8
colours uint8
_ uint8
planes uint16
bpp uint16
size uint32
offset uint32
}
type windowsIcoContainer struct {
Header windowsIcoDescriptor
Data []byte
}
func generateWindowsIcon(pngFilename string, iconfile string) error {
sizes := []int{256, 128, 64, 48, 32, 16}
pngfile, err := os.Open(pngFilename)
if err != nil {
return err
}
defer pngfile.Close()
pngdata, err := png.Decode(pngfile)
if err != nil {
return err
}
icons := []windowsIcoContainer{}
for _, size := range sizes {
rect := image.Rect(0, 0, int(size), int(size))
rawdata := image.NewRGBA(rect)
scale := draw.CatmullRom
scale.Scale(rawdata, rect, pngdata, pngdata.Bounds(), draw.Over, nil)
icondata := new(bytes.Buffer)
writer := bufio.NewWriter(icondata)
err = png.Encode(writer, rawdata)
if err != nil {
return err
}
writer.Flush()
imgSize := size
if imgSize >= 256 {
imgSize = 0
}
data := icondata.Bytes()
icn := windowsIcoContainer{
Header: windowsIcoDescriptor{
width: uint8(imgSize),
height: uint8(imgSize),
planes: 1,
bpp: 32,
size: uint32(len(data)),
},
Data: data,
}
icons = append(icons, icn)
}
outfile, err := os.Create(iconfile)
if err != nil {
return err
}
defer outfile.Close()
ico := windowsIcoHeader{
imageType: 1,
imageCount: uint16(len(sizes)),
}
err = binary.Write(outfile, binary.LittleEndian, ico)
if err != nil {
return err
}
offset := uint32(6 + 16*len(sizes))
for _, icon := range icons {
icon.Header.offset = offset
err = binary.Write(outfile, binary.LittleEndian, icon.Header)
if err != nil {
return err
}
offset += icon.Header.size
}
for _, icon := range icons {
_, err = outfile.Write(icon.Data)
if err != nil {
return err
}
}
return nil
}
func defaultString(val string, defaultVal string) string {
if val != "" {
return val
@@ -63,29 +174,30 @@ func defaultString(val string, defaultVal string) string {
func (b *PackageHelper) getPackageFileBaseDir() string {
// Calculate template base dir
_, filename, _, _ := runtime.Caller(1)
return filepath.Join(path.Dir(filename), "packages", runtime.GOOS)
return filepath.Join(path.Dir(filename), "packages", b.platform)
}
// Package the application into a platform specific package
func (b *PackageHelper) Package(po *ProjectOptions) error {
switch runtime.GOOS {
switch b.platform {
case "darwin":
// Check we have the exe
if !b.fs.FileExists(po.BinaryName) {
return fmt.Errorf("cannot bundle non-existent binary file '%s'. Please build with 'wails build' first", po.BinaryName)
}
return b.packageOSX(po)
case "windows":
return b.PackageWindows(po, false)
return b.PackageWindows(po, true)
case "linux":
return fmt.Errorf("linux is not supported at this time. Please see https://github.com/wailsapp/wails/issues/2")
return b.packageLinux(po)
default:
return fmt.Errorf("platform '%s' not supported for bundling yet", runtime.GOOS)
return fmt.Errorf("platform '%s' not supported for bundling yet", b.platform)
}
}
func (b *PackageHelper) packageLinux(po *ProjectOptions) error {
return nil
}
// Package the application for OSX
func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
build := path.Join(b.fs.Cwd(), "build")
system := NewSystemHelper()
config, err := system.LoadConfig()
@@ -100,39 +212,68 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
packageID := strings.Join([]string{"wails", name, version}, ".")
plistData := newPlistData(name, exe, packageID, version, author)
appname := po.Name + ".app"
plistFilename := path.Join(build, appname, "Contents", "Info.plist")
customPlist := path.Join(b.fs.Cwd(), "info.plist")
// Check binary exists
source := path.Join(b.fs.Cwd(), exe)
if !b.fs.FileExists(source) {
// We need to build!
return fmt.Errorf("Target '%s' not available. Has it been compiled yet?", exe)
source := path.Join(build, exe)
if po.CrossCompile == true {
file, err := b.fs.FindFile(build, "darwin")
if err != nil {
return err
}
source = path.Join(build, file)
}
if !b.fs.FileExists(source) {
// We need to build!
return fmt.Errorf("Target '%s' not available. Has it been compiled yet?", source)
}
// Remove the existing package
os.RemoveAll(appname)
exeDir := path.Join(b.fs.Cwd(), appname, "/Contents/MacOS")
// Create directories
exeDir := path.Join(build, appname, "/Contents/MacOS")
b.fs.MkDirs(exeDir, 0755)
resourceDir := path.Join(b.fs.Cwd(), appname, "/Contents/Resources")
resourceDir := path.Join(build, appname, "/Contents/Resources")
b.fs.MkDirs(resourceDir, 0755)
tmpl := template.New("infoPlist")
plistFile := filepath.Join(b.getPackageFileBaseDir(), "info.plist")
infoPlist, err := ioutil.ReadFile(plistFile)
if err != nil {
return err
}
tmpl.Parse(string(infoPlist))
// Write the template to a buffer
var tpl bytes.Buffer
err = tmpl.Execute(&tpl, plistData)
if err != nil {
return err
}
filename := path.Join(b.fs.Cwd(), appname, "Contents", "Info.plist")
err = ioutil.WriteFile(filename, tpl.Bytes(), 0644)
if err != nil {
return err
// Do we have a custom plist in the project directory?
if !fs.FileExists(customPlist) {
// No - create a new plist from our defaults
tmpl := template.New("infoPlist")
plistFile := filepath.Join(b.getPackageFileBaseDir(), "info.plist")
infoPlist, err := ioutil.ReadFile(plistFile)
if err != nil {
return err
}
tmpl.Parse(string(infoPlist))
// Write the template to a buffer
var tpl bytes.Buffer
err = tmpl.Execute(&tpl, plistData)
if err != nil {
return err
}
// Save to the package
err = ioutil.WriteFile(plistFilename, tpl.Bytes(), 0644)
if err != nil {
return err
}
// Also write to project directory for customisation
err = ioutil.WriteFile(customPlist, tpl.Bytes(), 0644)
if err != nil {
return err
}
} else {
// Yes - we have a plist. Copy it to the package verbatim
err = fs.CopyFile(customPlist, plistFilename)
if err != nil {
return err
}
}
// Copy executable
@@ -150,22 +291,37 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
return err
}
// CleanWindows removes any windows related files found in the directory
func (b *PackageHelper) CleanWindows(po *ProjectOptions) {
pdir := b.fs.Cwd()
basename := strings.TrimSuffix(po.BinaryName, ".exe")
exts := []string{".ico", ".exe.manifest", ".rc", "-res.syso"}
rsrcs := []string{}
for _, ext := range exts {
rsrcs = append(rsrcs, filepath.Join(pdir, basename+ext))
}
b.fs.RemoveFiles(rsrcs, true)
}
// PackageWindows packages the application for windows platforms
func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
outputDir := b.fs.Cwd()
basename := strings.TrimSuffix(po.BinaryName, ".exe")
// Copy icon
tgtIconFile := filepath.Join(b.fs.Cwd(), basename+".ico")
if !b.fs.FileExists(tgtIconFile) {
srcIconfile := filepath.Join(b.getPackageFileBaseDir(), "wails.ico")
err := b.fs.CopyFile(srcIconfile, tgtIconFile)
if err != nil {
return err
}
// Copy default icon if needed
icon, err := b.copyIcon()
if err != nil {
return err
}
// Generate icon from PNG
err = generateWindowsIcon(icon, basename+".ico")
if err != nil {
return err
}
// Copy manifest
tgtManifestFile := filepath.Join(b.fs.Cwd(), basename+".exe.manifest")
tgtManifestFile := filepath.Join(outputDir, basename+".exe.manifest")
if !b.fs.FileExists(tgtManifestFile) {
srcManifestfile := filepath.Join(b.getPackageFileBaseDir(), "wails.exe.manifest")
err := b.fs.CopyFile(srcManifestfile, tgtManifestFile)
@@ -175,7 +331,7 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
}
// Copy rc file
tgtRCFile := filepath.Join(b.fs.Cwd(), basename+".rc")
tgtRCFile := filepath.Join(outputDir, basename+".rc")
if !b.fs.FileExists(tgtRCFile) {
srcRCfile := filepath.Join(b.getPackageFileBaseDir(), "wails.rc")
rcfilebytes, err := ioutil.ReadFile(srcRCfile)
@@ -190,33 +346,37 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
}
// Build syso
sysofile := filepath.Join(b.fs.Cwd(), basename+"-res.syso")
sysofile := filepath.Join(outputDir, basename+"-res.syso")
batfile, err := fs.LocalDir(".")
if err != nil {
return err
}
// cross-compile
if b.platform != runtime.GOOS {
args := []string{
"docker", "run", "--rm",
"-v", outputDir + ":/build",
"--entrypoint", "/bin/sh",
"wailsapp/xgo:latest",
"-c", "/usr/bin/x86_64-w64-mingw32-windres -o /build/" + basename + "-res.syso /build/" + basename + ".rc",
}
if err := NewProgramHelper().RunCommandArray(args); err != nil {
return err
}
} else {
batfile, err := fs.LocalDir(".")
if err != nil {
return err
}
windresBatFile := filepath.Join(batfile.fullPath, "windres.bat")
windresCommand := []string{windresBatFile, sysofile, tgtRCFile}
err = NewProgramHelper().RunCommandArray(windresCommand)
if err != nil {
return err
}
// clean up
if cleanUp {
filesToDelete := []string{tgtIconFile, tgtManifestFile, tgtRCFile, sysofile}
err := b.fs.RemoveFiles(filesToDelete)
windresBatFile := filepath.Join(batfile.fullPath, "windres.bat")
windresCommand := []string{windresBatFile, sysofile, tgtRCFile}
err = NewProgramHelper().RunCommandArray(windresCommand)
if err != nil {
return err
}
}
return nil
}
func (b *PackageHelper) copyIcon(resourceDir string) (string, error) {
func (b *PackageHelper) copyIcon() (string, error) {
// TODO: Read this from project.json
const appIconFilename = "appicon.png"
@@ -241,7 +401,7 @@ func (b *PackageHelper) copyIcon(resourceDir string) (string, error) {
func (b *PackageHelper) packageIconOSX(resourceDir string) error {
srcIcon, err := b.copyIcon(resourceDir)
srcIcon, err := b.copyIcon()
if err != nil {
return err
}

View File

@@ -1,5 +1,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>CFBundlePackageType</key><string>APPL</string>
<key>CFBundleName</key><string>{{.Title}}</string>
<key>CFBundleExecutable</key><string>{{.Exe}}</string>
<key>CFBundleIdentifier</key><string>{{.PackageID}}</string>

View File

@@ -1,2 +1,2 @@
100 ICON "$NAME$.ico"
100 24 "$NAME$.exe.manifest"
110 24 "$NAME$.exe.manifest"

View File

@@ -138,11 +138,11 @@ func (p *ProgramHelper) RunCommand(command string) error {
// RunCommandArray runs the command specified in the array
func (p *ProgramHelper) RunCommandArray(args []string, dir ...string) error {
program := args[0]
programCommand := args[0]
// TODO: Run FindProgram here and get the full path to the exe
program, err := exec.LookPath(program)
program, err := exec.LookPath(programCommand)
if err != nil {
fmt.Printf("ERROR: Looks like '%s' isn't installed. Please install and try again.", program)
fmt.Printf("ERROR: Looks like '%s' isn't installed. Please install and try again.", programCommand)
return err
}

View File

@@ -6,7 +6,6 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
@@ -159,6 +158,11 @@ type ProjectOptions struct {
WailsVersion string
typescriptDefsFilename string
Verbose bool `json:"-"`
CrossCompile bool
Platform string
Architecture string
LdFlags string
GoPath string
}
// Defaults sets the default project template
@@ -333,11 +337,6 @@ func processBinaryName(po *ProjectOptions) {
if po.BinaryName == "" {
var binaryNameComputed = computeBinaryName(po.Name)
po.BinaryName = Prompt("The output binary name", binaryNameComputed)
if runtime.GOOS == "windows" {
if !strings.HasSuffix(po.BinaryName, ".exe") {
po.BinaryName += ".exe"
}
}
}
fmt.Println("Output binary Name: " + po.BinaryName)
}

View File

@@ -5,12 +5,11 @@ import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"runtime"
"strconv"
"time"
homedir "github.com/mitchellh/go-homedir"
)
// SystemHelper - Defines everything related to the system
@@ -38,10 +37,11 @@ func NewSystemHelper() *SystemHelper {
// setSystemDirs calculates the system directories it is interested in
func (s *SystemHelper) setSystemDirs() {
var err error
s.homeDir, err = homedir.Dir()
s.homeDir, err = os.UserHomeDir()
if err != nil {
log.Fatal("Cannot find home directory! Please file a bug report!")
}
// TODO: A better config system
s.wailsSystemDir = filepath.Join(s.homeDir, ".wails")
s.wailsSystemConfig = filepath.Join(s.wailsSystemDir, s.configFilename)
@@ -132,7 +132,7 @@ func (s *SystemHelper) setup() error {
}
const introText = `
Wails is a lightweight framework for creating web-like desktop apps in Go.
Wails is a lightweight framework for creating web-like desktop apps in Go.
I'll need to ask you a few questions so I can fill in your project templates and then I will try and see if you have the correct dependencies installed. If you don't have the right tools installed, I'll try and suggest how to install them.
`
@@ -274,16 +274,18 @@ func CheckDependencies(logger *Logger) (bool, error) {
distroInfo := GetLinuxDistroInfo()
switch distroInfo.Distribution {
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian:
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian, PopOS:
libraryChecker = DpkgInstalled
case Arch, ArcoLinux, Manjaro, ManjaroARM:
case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM:
libraryChecker = PacmanInstalled
case CentOS, Fedora:
case CentOS, Fedora, Tumbleweed, Leap:
libraryChecker = RpmInstalled
case Gentoo:
libraryChecker = EqueryInstalled
case VoidLinux:
libraryChecker = XbpsInstalled
case Solus:
libraryChecker = EOpkgInstalled
default:
return false, RequestSupportForDistribution(distroInfo)
}

View File

@@ -5,7 +5,7 @@ const routes: Routes = [];
@NgModule({
imports: [
RouterModule.forRoot(routes)
RouterModule.forRoot(routes,{useHash:true})
],
exports: [RouterModule]
})

View File

@@ -4,12 +4,12 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"core-js": "^3.1.4",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"core-js": "^3.6.4",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"wails-react-scripts": "3.0.1-2",
"react-modal": "3.8.1",
"@wailsapp/runtime": "^1.0.0"
"react-modal": "3.11.2",
"@wailsapp/runtime": "^1.0.10"
},
"scripts": {
"start": "react-scripts start",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,10 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
@@ -20,8 +23,9 @@
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="app"></div>
<!--
@@ -34,5 +38,6 @@
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

View File

@@ -6,6 +6,16 @@
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",

View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@@ -3,11 +3,16 @@
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;

View File

@@ -1,48 +1,35 @@
import React from 'react';
import React, { useState } from 'react';
import Modal from 'react-modal';
class HelloWorld extends React.Component {
constructor(props, context) {
super();
this.state = {
showModal: false
};
function HelloWorld() {
const [showModal, setShowModal] = useState(false);
const [result, setResult] = useState(null);
this.handleOpenModal = this.handleOpenModal.bind(this);
this.handleCloseModal = this.handleCloseModal.bind(this);
}
const handleOpenModal = () => {
setShowModal(true);
handleOpenModal () {
this.setState({ showModal: true });
window.backend.basic().then((result) => setResult(result));
};
window.backend.basic().then(result =>
this.setState({
result
})
);
}
const handleCloseModal = () => {
setShowModal(false);
};
handleCloseModal () {
this.setState({ showModal: false });
}
render() {
const { result } = this.state;
return (
<div className="App">
<button onClick={this.handleOpenModal} type="button">
Hello
</button>
<Modal
isOpen={this.state.showModal}
contentLabel="Minimal Modal Example"
>
<p>{result}</p>
<button onClick={this.handleCloseModal}>Close Modal</button>
</Modal>
</div>
);
}
return (
<div className="App">
<button onClick={() => handleOpenModal()} type="button">
Hello
</button>
<Modal
appElement={document.getElementById("app")}
isOpen={showModal}
contentLabel="Minimal Modal Example"
>
<p>{result}</p>
<button onClick={() => handleCloseModal()}>Close Modal</button>
</Modal>
</div>
);
}
export default HelloWorld;

View File

@@ -3,9 +3,20 @@ import ReactDOM from 'react-dom';
import 'core-js/stable';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import * as Wails from '@wailsapp/runtime';
Wails.Init(() => {
ReactDOM.render(<App />, document.getElementById('app'));
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("app")
);
});
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

View File

@@ -1,8 +1,8 @@
{
"name": "React JS",
"version": "1.0.0",
"shortdescription": "Create React App v3 template",
"description": "Create React App v3 standar tooling",
"shortdescription": "Create React App v4 template",
"description": "Create React App v4 standard tooling",
"install": "npm install",
"build": "npm run build",
"author": "bh90210 <ktc@pm.me>",

View File

@@ -0,0 +1,4 @@
/node_modules/
/public/build/
.DS_Store

View File

@@ -0,0 +1,90 @@
*Looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)*
---
# svelte app
This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template.
To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
```bash
npx degit sveltejs/template svelte-app
cd svelte-app
```
*Note that you will need to have [Node.js](https://nodejs.org) installed.*
## Get started
Install the dependencies...
```bash
cd svelte-app
npm install
```
...then start [Rollup](https://rollupjs.org):
```bash
npm run dev
```
Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`.
## Building and running in production mode
To create an optimised version of the app:
```bash
npm run build
```
You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com).
## Single-page app mode
By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json:
```js
"start": "sirv public --single"
```
## Deploying to the web
### With [Vercel](https://vercel.com)
Install `vercel` if you haven't already:
```bash
npm install -g vercel
```
Then, from within your project folder:
```bash
cd public
vercel deploy --name my-project
```
### With [surge](https://surge.sh/)
Install `surge` if you haven't already:
```bash
npm install -g surge
```
Then, from within your project folder:
```bash
npm run build
surge public my-project.surge.sh
```

View File

@@ -0,0 +1,31 @@
{
"name": "{{.NPMProjectName}}",
"author": "{{.Author.Name}}<{{.Author.Email}}>",
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"start": "sirv public"
},
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.11.5",
"@babel/preset-env": "^7.11.5",
"@rollup/plugin-commonjs": "^14.0.0",
"@rollup/plugin-image": "^2.0.5",
"@rollup/plugin-node-resolve": "^8.0.0",
"core-js": "^3.6.5",
"rollup": "^2.3.4",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-polyfill": "^3.0.0",
"rollup-plugin-svelte": "^6.0.0",
"rollup-plugin-terser": "^7.0.0",
"svelte": "^3.0.0"
},
"dependencies": {
"sirv-cli": "^1.0.0",
"@wailsapp/runtime": "^1.0.10",
"svelte-simple-modal": "^0.6.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Svelte app</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,110 @@
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import image from '@rollup/plugin-image';
import babel from 'rollup-plugin-babel';
import polyfill from 'rollup-plugin-polyfill';
const production = !process.env.ROLLUP_WATCH;
function serve() {
let server;
function toExit() {
if (server) server.kill(0);
}
return {
writeBundle() {
if (server) return;
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
process.on('SIGTERM', toExit);
process.on('exit', toExit);
}
};
}
export default {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/bundle.js'
},
plugins: [
image(),
svelte({
// enable run-time checks when not in production
dev: !production,
// we'll extract any component CSS out into
// a separate file - better for performance
css: css => {
css.write('bundle.css');
}
}),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
resolve({
browser: true,
dedupe: ['svelte', 'svelte/transition', 'svelte/internal']
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// Credit: https://blog.az.sg/posts/svelte-and-ie11/
babel({
extensions: [ '.js', '.jsx', '.es6', '.es', '.mjs', '.svelte', '.html' ],
runtimeHelpers: true,
exclude: [ 'node_modules/@babel/**', 'node_modules/core-js/**' ],
presets: [
[
'@babel/preset-env',
{
targets: '> 0.25%, not dead, IE 11',
modules: false,
spec: true,
useBuiltIns: 'usage',
forceAllTransforms: true,
corejs: 3,
},
]
],
plugins: [
'@babel/plugin-syntax-dynamic-import',
[
'@babel/plugin-transform-runtime',
{
useESModules: true
}
]
]
}),
polyfill(['@webcomponents/webcomponentsjs']),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
};

View File

@@ -0,0 +1,69 @@
<script>
import Modal from 'svelte-simple-modal';
import HelloWorld from './components/HelloWorld.svelte'
import logo from './logo.png';
</script>
<main>
<div class="App">
<header class="App-header">
<Modal>
<img src={logo} class="App-logo" alt="logo" />
<p>Welcome to your new <code>wails/svelte</code> project.</p>
<HelloWorld/>
</Modal>
</header>
</div>
</main>
<style>
:global(body) {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>

View File

@@ -0,0 +1,18 @@
<script>
import { getContext } from 'svelte';
import ModalContent from './ModalContent.svelte'
const { open } = getContext('simple-modal');
const handleOpenModal = () => {
window.backend.basic().then((result) => {
open(ModalContent, { message: result });
});
};
</script>
<main>
<p><button on:click={handleOpenModal}>Hello</button></p>
</main>
<style></style>

View File

@@ -0,0 +1,7 @@
<script>
export let message;
</script>
<p>
{message}
</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

View File

@@ -0,0 +1,13 @@
import App from './App.svelte';
import * as Wails from '@wailsapp/runtime';
let app;
Wails.Init(() => {
app = new App({
target: document.body,
});
});
export default app;

View File

@@ -0,0 +1,5 @@
module {{.BinaryName}}
require (
github.com/wailsapp/wails {{.WailsVersion}}
)

View File

@@ -0,0 +1,27 @@
package main
import (
"github.com/leaanthony/mewn"
"github.com/wailsapp/wails"
)
func basic() string {
return "World!"
}
func main() {
js := mewn.String("./frontend/public/build/bundle.js")
css := mewn.String("./frontend/public/build/bundle.css")
app := wails.CreateApp(&wails.AppConfig{
Width: 1024,
Height: 768,
Title: "{{.Name}}",
JS: js,
CSS: css,
Colour: "#131313",
})
app.Bind(basic)
app.Run()
}

View File

@@ -0,0 +1,14 @@
{
"name": "Svelte",
"version": "1.0.0",
"shortdescription": "A basic Svelte template",
"description": "A basic Svelte template",
"install": "npm install",
"build": "npm run build",
"author": "Tim Kipp <timkipp.22.developer@gmail.com>",
"created": "2020-09-06 13:06:10.469848 -0700 PDT m=+213.578828559",
"frontenddir": "frontend",
"serve": "npm run dev",
"bridge": "src",
"wailsdir": ""
}

View File

@@ -0,0 +1,5 @@
# README
This is an experimental template for vanilla HTML/JS/CSS.
The webpack rules may need to be adjusted to correctly embed all assets. Babel may also need to be setup correctly.

View File

@@ -0,0 +1,46 @@
package main
import (
"math/rand"
"github.com/wailsapp/wails"
)
// Counter is what we use for counting
type Counter struct {
r *wails.Runtime
store *wails.Store
}
// WailsInit is called when the component is being initialised
func (c *Counter) WailsInit(runtime *wails.Runtime) error {
c.r = runtime
c.store = runtime.Store.New("Counter", 0)
return nil
}
// RandomValue sets the counter to a random value
func (c *Counter) RandomValue() {
c.store.Set(rand.Intn(1000))
}
// Increment will increment the counter
func (c *Counter) Increment() {
increment := func(data int) int {
return data + 1
}
// Update the store using the increment function
c.store.Update(increment)
}
// Decrement will decrement the counter
func (c *Counter) Decrement() {
decrement := func(data int) int {
return data - 1
}
// Update the store using the decrement function
c.store.Update(decrement)
}

View File

@@ -0,0 +1,42 @@
{
"name": "vanilla",
"author": "Lea<l>",
"private": true,
"scripts": {
"serve": "webpack-dev-server",
"build": "npx webpack"
},
"dependencies": {
"core-js": "^3.6.4",
"regenerator-runtime": "^0.13.3",
"@wailsapp/runtime": "^1.0.10"
},
"devDependencies": {
"babel-eslint": "^10.1.0",
"copy-webpack-plugin": "^6.0.2",
"eslint": "^6.8.0",
"eventsource-polyfill": "^0.9.6",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0",
"webpack-hot-middleware": "^2.25.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

View File

@@ -0,0 +1,9 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<div id="app"></div>
<script src="main.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,47 @@
import 'core-js/stable';
const runtime = require('@wailsapp/runtime');
// Main entry point
function start() {
var mystore = runtime.Store.New('Counter');
// Ensure the default app div is 100% wide/high
var app = document.getElementById('app');
app.style.width = '100%';
app.style.height = '100%';
// Inject html
app.innerHTML = `
<div class='logo'></div>
<div class='container'>
<button onClick='window.backend.Counter.Increment()'>
Increment Counter
</button>
<button onClick='window.backend.Counter.Decrement()'>
Decrement Counter
</button>
</div>
<div class='result'>Counter: <span id='counter'></span></div>
<div class='container'>
<input id='newCounter' type="number" value="0"/>
<button id='setvalue'>Set Counter Value</button>
<button onclick='window.backend.Counter.RandomValue()'>Set to Random Value</button>
</div>
`;
// Connect counter value button to Go method
document.getElementById('setvalue').onclick = function() {
let newValue = parseInt(document.getElementById('newCounter').value,10);
mystore.set(newValue);
};
mystore.subscribe( function(state) {
document.getElementById('counter').innerText = state;
});
mystore.set(0);
};
// We provide our entrypoint as a callback for runtime.Init
runtime.Init(start);

View File

@@ -0,0 +1,56 @@
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
let imageSizeLimit = 9007199254740991; // Number.MAX_SAFE_INTEGER
let sourceDir = path.resolve(__dirname, 'src');
let buildDir = path.resolve(__dirname, 'build');
module.exports = {
entry: {
index: path.resolve(sourceDir, 'main.js')
},
output: {
path: buildDir,
filename: 'main.js'
},
optimization: {
splitChunks: false
},
devServer: {
disableHostCheck: true,
contentBase: path.join(__dirname, 'src'),
compress: true,
open: true,
port: 8090
},
mode: 'production',
module: {
rules: [
{
test: /\.(png|gif|jpg|woff2?|eot|ttf|otf|svg)(\?.*)?$/i,
use: [
{
loader: 'url-loader',
options: {
limit: imageSizeLimit
}
}
],
}
]
},
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: path.resolve(sourceDir, 'main.css'),
to: path.resolve(buildDir, 'main.css')
},
{
from: path.resolve(sourceDir, 'index.html'),
to: path.resolve(buildDir, 'index.html')
},
]
})
]
};

View File

@@ -0,0 +1,5 @@
module {{.BinaryName}}
require (
github.com/wailsapp/wails {{.WailsVersion}}
)

View File

@@ -0,0 +1,23 @@
package main
import (
"github.com/leaanthony/mewn"
"github.com/wailsapp/wails"
)
func main() {
js := mewn.String("./frontend/build/main.js")
css := mewn.String("./frontend/build/main.css")
app := wails.CreateApp(&wails.AppConfig{
Width: 1024,
Height: 768,
Title: "{{.Name}}",
JS: js,
CSS: css,
Colour: "#131313",
})
app.Bind(&Counter{})
app.Run()
}

View File

@@ -0,0 +1,12 @@
{
"name": "Vanilla",
"shortdescription": "A Vanilla HTML/JS template",
"description": "A basic template using plain html/js and bundled using Webpack 4",
"author": "Lea Anthony<lea.anthony@gmail.com>",
"created": "2020-06-14",
"frontenddir": "frontend",
"install": "npm install",
"build": "npm run build",
"serve": "npm run serve",
"bridge": "src"
}

View File

@@ -8,21 +8,21 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.1",
"core-js": "^3.6.4",
"regenerator-runtime": "^0.13.3",
"vue": "^2.5.22",
"@wailsapp/runtime": "^1.0.0"
"vue": "^2.6.11",
"@wailsapp/runtime": "^1.0.10"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.4.0",
"@vue/cli-plugin-eslint": "^3.4.0",
"@vue/cli-service": "^3.4.0",
"babel-eslint": "^10.0.1",
"eslint": "^5.8.0",
"eslint-plugin-vue": "^5.0.0",
"@vue/cli-plugin-babel": "^4.2.3",
"@vue/cli-plugin-eslint": "^4.2.3",
"@vue/cli-service": "^4.2.3",
"babel-eslint": "^10.1.0",
"eslint": "^6.8.0",
"eslint-plugin-vue": "^6.2.1",
"eventsource-polyfill": "^0.9.6",
"vue-template-compiler": "^2.5.21",
"webpack-hot-middleware": "^2.24.3"
"vue-template-compiler": "^2.6.11",
"webpack-hot-middleware": "^2.25.0"
},
"eslintConfig": {
"root": true,

View File

@@ -7,24 +7,24 @@
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.1",
"dependencies": {
"core-js": "^3.6.4",
"regenerator-runtime": "^0.13.3",
"material-design-icons-iconfont": "^5.0.1",
"vue": "^2.5.22",
"vuetify": "^1.5.14",
"@wailsapp/runtime": "^1.0.0"
"@wailsapp/runtime": "^1.0.10"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.4.0",
"@vue/cli-plugin-eslint": "^3.4.0",
"@vue/cli-service": "^3.4.0",
"babel-eslint": "^10.0.1",
"eslint": "^5.8.0",
"eslint-plugin-vue": "^5.0.0",
"@vue/cli-plugin-babel": "^4.2.3",
"@vue/cli-plugin-eslint": "^4.2.3",
"@vue/cli-service": "^4.2.3",
"babel-eslint": "^10.1.0",
"eslint": "^6.8.0",
"eslint-plugin-vue": "^6.2.1",
"eventsource-polyfill": "^0.9.6",
"vue-template-compiler": "^2.5.21",
"webpack-hot-middleware": "^2.24.3"
"vue-template-compiler": "^2.6.11",
"webpack-hot-middleware": "^2.25.0"
},
"eslintConfig": {
"root": true,

View File

@@ -8,23 +8,23 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.1",
"core-js": "^3.6.4",
"regenerator-runtime": "^0.13.3",
"vue": "^2.5.22",
"vuetify": "^2.0.15",
"@wailsapp/runtime": "^1.0.0"
"vue": "^2.6.11",
"vuetify": "^2.2.15",
"@wailsapp/runtime": "^1.0.10"
},
"devDependencies": {
"@mdi/font": "^4.3.95",
"@vue/cli-plugin-babel": "^3.4.0",
"@vue/cli-plugin-eslint": "^3.4.0",
"@vue/cli-service": "^3.4.0",
"babel-eslint": "^10.0.1",
"eslint": "^5.8.0",
"eslint-plugin-vue": "^5.0.0",
"@mdi/font": "^4.9.95",
"@vue/cli-plugin-babel": "^4.2.3",
"@vue/cli-plugin-eslint": "^4.2.3",
"@vue/cli-service": "^4.2.3",
"babel-eslint": "^10.1.0",
"eslint": "^6.8.0",
"eslint-plugin-vue": "^6.2.1",
"eventsource-polyfill": "^0.9.6",
"vue-template-compiler": "^2.5.21",
"webpack-hot-middleware": "^2.24.3"
"vue-template-compiler": "^2.6.11",
"webpack-hot-middleware": "^2.25.0"
},
"eslintConfig": {
"root": true,

View File

@@ -1,4 +1,4 @@
package cmd
// Version - Wails version
const Version = "v1.0.3-pre1"
const Version = "v1.8.1-pre1"

View File

@@ -3,18 +3,34 @@ package main
import (
"fmt"
"os"
"runtime"
"strings"
"github.com/leaanthony/spinner"
"github.com/wailsapp/wails/cmd"
)
// getSupportedPlatforms returns a slice of platform/architecture
// targets that are buildable using the cross-platform 'x' option.
func getSupportedPlatforms() []string {
return []string{
"darwin/amd64",
"linux/amd64",
"linux/arm-7",
"windows/amd64",
}
}
func init() {
var packageApp = false
var forceRebuild = false
var debugMode = false
var gopath = ""
var typescriptFilename = ""
var verbose = false
var platform = ""
var ldflags = ""
buildSpinner := spinner.NewSpinner()
buildSpinner.SetSpinSpeed(50)
@@ -26,7 +42,17 @@ func init() {
BoolFlag("f", "Force rebuild of application components", &forceRebuild).
BoolFlag("d", "Build in Debug mode", &debugMode).
BoolFlag("verbose", "Verbose output", &verbose).
StringFlag("t", "Generate Typescript definitions to given file (at runtime)", &typescriptFilename)
StringFlag("t", "Generate Typescript definitions to given file (at runtime)", &typescriptFilename).
StringFlag("ldflags", "Extra options for -ldflags", &ldflags).
StringFlag("gopath", "Specify your GOPATH location. Mounted to /go during cross-compilation.", &gopath)
var b strings.Builder
for _, plat := range getSupportedPlatforms() {
fmt.Fprintf(&b, " - %s\n", plat)
}
initCmd.StringFlag("x",
fmt.Sprintf("Cross-compile application to specified platform via xgo\n%s", b.String()),
&platform)
initCmd.Action(func() error {
@@ -52,6 +78,29 @@ func init() {
return fmt.Errorf("Unable to find 'project.json'. Please check you are in a Wails project directory")
}
// Set cross-compile
projectOptions.Platform = runtime.GOOS
if len(platform) > 0 {
supported := false
for _, plat := range getSupportedPlatforms() {
if plat == platform {
supported = true
}
}
if !supported {
return fmt.Errorf("unsupported platform '%s' specified.\nPlease run `wails build -h` to see the supported platform/architecture options", platform)
}
projectOptions.CrossCompile = true
plat := strings.Split(platform, "/")
projectOptions.Platform = plat[0]
projectOptions.Architecture = plat[1]
}
// Add ldflags
projectOptions.LdFlags = ldflags
projectOptions.GoPath = gopath
// Validate config
// Check if we have a frontend
err = cmd.ValidateFrontendConfig(projectOptions)

View File

@@ -2,6 +2,7 @@ package main
import (
"fmt"
"runtime"
"github.com/leaanthony/spinner"
"github.com/wailsapp/wails/cmd"
@@ -34,7 +35,6 @@ func init() {
// Project options
projectOptions := &cmd.ProjectOptions{}
projectOptions.Verbose = verbose
// Check we are in project directory
// Check project.json loads correctly
@@ -44,6 +44,10 @@ func init() {
return err
}
// Set project options
projectOptions.Verbose = verbose
projectOptions.Platform = runtime.GOOS
// Save project directory
projectDir := fs.Cwd()

View File

@@ -3,9 +3,9 @@ package main
import (
"fmt"
"log"
"os"
"github.com/leaanthony/spinner"
"github.com/mitchellh/go-homedir"
"github.com/wailsapp/wails/cmd"
)
@@ -146,7 +146,7 @@ func updateToVersion(targetVersion *cmd.SemanticVersion, force bool) error {
updateSpinner.Start("Installing Wails " + desiredVersion)
// Run command in non module directory
homeDir, err := homedir.Dir()
homeDir, err := os.UserHomeDir()
if err != nil {
log.Fatal("Cannot find home directory! Please file a bug report!")
}

9
go.mod
View File

@@ -14,19 +14,18 @@ require (
github.com/leaanthony/spinner v0.5.3
github.com/mattn/go-colorable v0.1.1 // indirect
github.com/mattn/go-isatty v0.0.7 // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
github.com/pkg/errors v0.8.1 // indirect
github.com/sirupsen/logrus v1.4.1
github.com/stretchr/testify v1.3.0 // indirect
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 // indirect
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 // indirect
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862
golang.org/x/image v0.0.0-20200430140353-33d19683fad8
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
golang.org/x/text v0.3.0
gopkg.in/AlecAivazis/survey.v1 v1.8.4
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22
)
go 1.12
go 1.13

16
go.sum
View File

@@ -46,8 +46,6 @@ github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98=
@@ -68,19 +66,21 @@ github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba h1:2DHfQOxcpWdGf5
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba/go.mod h1:iLnlXG2Pakcii2CU0cbY07DRCSvpWNa7nFxtevhOChk=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR170vGqDhJDOmpVd4Hjak=
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 h1:rM0ROo5vb9AdYJi1110yjWGMej9ITfKddS89P3Fkhug=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc=

View File

@@ -196,7 +196,7 @@ func (b *boundMethod) processWailsInit() error {
// It must be *wails.Runtime
inputName := b.inputs[0].String()
b.log.Debugf("WailsInit input type: %s", inputName)
if inputName != "*wails.Runtime" {
if inputName != "*runtime.Runtime" {
return fmt.Errorf("Invalid WailsInit() definition. Expected input to be wails.Runtime, but got %s", inputName)
}
@@ -219,7 +219,7 @@ func (b *boundMethod) processWailsInit() error {
}
func (b *boundMethod) processWailsShutdown() error {
// We must have only 1 input, it must be *wails.Runtime
// We must not have any inputs
if len(b.inputs) != 0 {
return fmt.Errorf("Invalid WailsShutdown() definition. Expected 0 inputs, but got %d", len(b.inputs))
}

View File

@@ -17,9 +17,9 @@ type Renderer interface {
NotifyEvent(eventData *messages.EventData) error
// Dialog Runtime
SelectFile() string
SelectFile(title string, filter string) string
SelectDirectory() string
SelectSaveFile() string
SelectSaveFile(title string, filter string) string
// Window Runtime
SetColour(string) error

View File

@@ -42,12 +42,12 @@ type Bridge struct {
server *http.Server
lock sync.Mutex
sessions map[string]session
sessions map[string]*session
}
// Initialise the Bridge Renderer
func (h *Bridge) Initialise(appConfig interfaces.AppConfig, ipcManager interfaces.IPCManager, eventManager interfaces.EventManager) error {
h.sessions = map[string]session{}
h.sessions = map[string]*session{}
h.ipcManager = ipcManager
h.appConfig = appConfig
h.eventManager = eventManager
@@ -70,13 +70,11 @@ func (h *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
}
func (h *Bridge) startSession(conn *websocket.Conn) {
s := session{
conn: conn,
bindingCache: h.bindingCache,
ipc: h.ipcManager,
log: h.log,
eventManager: h.eventManager,
}
s := newSession(conn,
h.bindingCache,
h.ipcManager,
logger.NewCustomLogger("BridgeSession"),
h.eventManager)
conn.SetCloseHandler(func(int, string) error {
h.log.Infof("Connection dropped [%s].", s.Identifier())
@@ -116,7 +114,7 @@ func (h *Bridge) NewBinding(methodName string) error {
// SelectFile is unsupported for Bridge but required
// for the Renderer interface
func (h *Bridge) SelectFile() string {
func (h *Bridge) SelectFile(title string, filter string) string {
h.log.Warn("SelectFile() unsupported in bridge mode")
return ""
}
@@ -130,7 +128,7 @@ func (h *Bridge) SelectDirectory() string {
// SelectSaveFile is unsupported for Bridge but required
// for the Renderer interface
func (h *Bridge) SelectSaveFile() string {
func (h *Bridge) SelectSaveFile(title string, filter string) string {
h.log.Warn("SelectSaveFile() unsupported in bridge mode")
return ""
}
@@ -154,13 +152,20 @@ func (h *Bridge) NotifyEvent(event *messages.EventData) error {
// Marshall the data
data, err = json.Marshal(event.Data)
if err != nil {
h.log.Errorf("Cannot unmarshall JSON data in event: %s ", err.Error())
h.log.Errorf("Cannot marshal JSON data in event: %s ", err.Error())
return err
}
}
message := fmt.Sprintf("window.wails._.Notify('%s','%s')", event.Name, data)
dead := []session{}
// Double encode data to ensure everything is escaped correctly.
data, err = json.Marshal(string(data))
if err != nil {
h.log.Errorf("Cannot marshal JSON data in event: %s ", err.Error())
return err
}
message := "window.wails._.Notify('" + event.Name + "'," + string(data) + ")"
dead := []*session{}
for _, session := range h.sessions {
err := session.evalJS(message, notifyMessage)
if err != nil {
@@ -207,6 +212,9 @@ func (h *Bridge) SetTitle(title string) {
// for the Renderer interface
func (h *Bridge) Close() {
h.log.Debug("Shutting down")
for _, session := range h.sessions {
session.Shutdown()
}
err := h.server.Close()
if err != nil {
h.log.Errorf(err.Error())

View File

@@ -0,0 +1,9 @@
package renderer
// Autogenerated by Mewn - Do not alter
import "github.com/leaanthony/mewn"
func init() {
mewn.AddAsset(".", "../../runtime/assets/wails.js", "1f8b08000000000000ff94587f6f1b37d2fe2a2be27d5532a6b752ae875e56dd06a9eba23e38761127d73f744241ed8e24262b52478eac0812bffb61f6b76cb9770704b1440ec9e13ccfcc3cd460b135196a6bb8118747e5224c0f61d20c46c09d38e805c7a99b0907b87526a2cf317cdd58877e424b6c4a43e941274e16c9602cebc9e410c2a45e646851a68a82db66adb4b2fb0c42dab84807a36e2c40bc4e8d84384b51429ca79dab12a51307882d7d14c7e3fdfc336418e7b0d0067e7376030ef7a5d901cc760d4ecd0b480623b9044c5c104142ecd2fed5d9d654ab73364871bf01bb881ef6ebb92d86c3ea6f8cf6019d36cb8f6a391cbe74e2735b797854c51612f6dee6db025810f2a5c5ec8f3fc0d766cdb2c1a872174fae5f82321ee270c84d0adc0821ff36c4062133d10bfe1dcd325b1ec5d2e64e6638a47f717752b788b07469ed5ce6402170b32d0a41db41ecb87bc97527590e0bb52d903d8d78750b13847c5d3ae4cbb874413662611d2f69146913190171ce9db4b2bd2e8a434b229c8578ae4d5efa25ad100dbf1cc5c8a4cfd9fce4b66f5b8b6ed7b8f63d2467265b06935f2899621285443ace3e81a436ac43b471162d5d325e297fbf334db0aa2ca005b4c726654c0287d8a72311f8f484e340bcf41051cc326413c20085ac3db9311a99e4cf7dfe1a84a8c13c84490d5c1556f633ccb7cbb3ab8a205aab1bb3b0678d7ccfe877e50c4179ce6ed1b3bb76cebab35679cfea1785aa386bb56bae639bebd86a9595ec7e03e6d387dbb3ebf6cdeeb5dd2f9ad2ea8ce1bc39403707e86aa196ec0e7667d75c356b543a9d75f5326b70abc34ff8274612e473957db9f93901b951fbc2aa3cc13039a9be7ac177dae47617ef942efcdce97c096f9f0fc53b987b9b7d018c3d502a88a43682af08cea822d6e6d17ea1b220555c8059e2eac7519b67988e26f8433331c18b0ba1a638e34604fef787fbbbb84a51bdd873274468efb6ad589e7156d8259387021ea1488c5c83f76a0909869e714137da7296577c33bd295f4fe99264fd99453db36b98d59fcceb49a8e8d49fdad5538b8a434604bae7463e9ef4b265834d93dadba218a4301cd61f8e470ee9484803bbe83767d7da03ef9077d256a8ea496e0f3a3517ec925d6cb808bb952e803f4ef5ac2a943f8e44450c0ff851afc16e91f719647999119c5da9a288d046ecc25cb008f51af2c86e318e3ec0bfb6e031baf93989d88516220809624247a407acb6fc5599bc80444907546e122b1d785b3c42e2c204dd9e7022da3179a082a9cdf24ead898bb942953cc1996a9116215398ad28969935de161097b12662843e115627e56ec959c5ce985d9455adb5dbd356b5d58ab39f9cdd7970719bb37d08e72fdb56796b44d8a435cf33b7dfa0ed97720ab84909b94fdae05f5ebf734eedf9b86d0f270be325e0076572bbfe073527e2e374343b57fddf8c46df8fdfbc79fdd7efbeff6ef4e6cdf8d57b85abd8956bb908650158b75d2032bce4d7e059a71e701369e351998c7a1e0a812b67771139fc71bf819610c6588c08b6484559a1bc8f948f54d46cc844e0b8d25e1a2181187b3996f43dbeaa0bcc49036cf45aac369b625fb54c23e4e5789012ed47699a72b84cc72204797d922af74daa5c4fcd2ca5ff8ec7e9acee29e4f4babce98466e2cdd6afb8eb61f9d075df273c9bce625fe80c6a8f945b6ed760d08b6a988fa962a707d3632a8649c6193c824126a1caecbb1367bfd071c4f826af6117fdd28481913dbb30420e461dbf2bcbc138744e5f5560913948975254299b07fdfa5ba356a3f53b0d45da478499361ab52ab4879c35dd6a3a6be85762e4b7739f393d8713906c153f234285a4879ed24371801465df87f89a42e1e3ebb546aa94baf089df9b2cf1681d24db4dae10f2f97ee1ac4130794259f9040610a4b617d65dab6cd5ab4d461c0c0741c95e3953ed764e5571101347ce96b5e39c83f7e6cfdc23b236de9d3a507166a39c2f1b18a4e63ff92a84c4e1b0891e2924fadc41fbb94d68feb991b7ca7bbd34c7637fbfae418ea94136e4ec37ca8a1f693b37c5d9a459e648bf82f8afe41f482748bbd3bb09e86d155abd29eae428f3bccb9050c7b80e5c7a388d7adaff723c76b39d7dc9ca9bf47007bb9faa8e903c07763a8b336b3285dcc47e5310c76226ca8f6d7e9eee4c59828dca18b72ac3a5a389fba199b81c4fdc453aae4ea9de8b65767de156885eded6a9c51a27ac644d86a9e851153a8f3eab474579b4c148e760502f34b898093181a99d51139fda597a0865859cda59a8941dc61bbbe1a23e55ffe9a9fa7f3b55cfd227ad882456ef0dddd0a62d7f25073a6c276d2b3512a885b64f8e4e429c2421a6861e0fcb33b35dd90f1202174136bde139da1393e690d91c3e7db8b9b2eb8d35600878079b4265c0bffda7bff876291913ddd07474f9465d2e6687d781a6feffff8614042abf789ab975b56d6a6aca6e4c154ab28a36ca7bc849fd34da3889daf8635c6b4ac9e2e87d2d2f23265a6a8a4955890b0e955eab10acdb834b316e76d5b9b4e96343b746c2a5ac094af44d7baa93ec9b1273074bed11f2c160c09a934e1591ee9f4ac2a900e51aad67e3138926640e052044e485c46a87b736ae741baf07444223a580e31853e71341de59d48b7df24c4e50dbedca15940dbaeea0425689570ed585ab9f79344e7e68ea4e94b982a0d37de8b0075dce4f6123c7220f06093953ba174765c18fa8694769d96b836d0509d7623884a67e383916a1d41410827c97e70f654a3db96175a9dc66657ad4bf445c1740df38abb2908909c4085f3135b2b59cdb7c4fd5134c7eb5d2454eecc0e1f0a1ec513786e27df5f070a6eabd7816ee0b200e5022be43747abe45e08cea3a938cceff36f39e09897169fbb002c0b7fd2f71e6fdc7d2cf044f7c7b7226d9dcd9bcfa2da79b5c81ca8fc7f6eb12b076ceffb4ffa84a4dcf191931d2b0e2e484ead61a93af14ea9bdfae6e89d606dc4904542b4082bc4d0fb776993859ebefc4caaa9b27877bf304a5fbfa378c7bf37e5ba0de1490dc4b5225c983fc1594c339a85364bb773149d1c91df1a057b7ca24ba3108ee5115e54f1654bd1c3da3dad1fe3b8a646610a50fefb22fc6ee0ac897903c79520fe8989edcce5b99adba45d1d6d06713ad1ac77b75c148f60dd538da8853941e4a29a3e51fc94d4f867ead2489089f4f9ef1f25634add89a32d54f7fc70459be2c4f14d4ad5dc6754f7af5ead5abe84a6d972b8c3e99555951f2aaf04434c7847c6969573849febe64f5e9c32d59e0cb16b7da407467c90a5eb6bab2c5766d6a3bf7b25d1902b2b122c88746221656e5a49cc34c4cfe1d0000ffff6a61b37f93160000")
}

View File

@@ -1,7 +1,8 @@
package renderer
import (
"sync"
"time"
"unsafe"
"github.com/gorilla/websocket"
"github.com/leaanthony/mewn"
@@ -20,7 +21,22 @@ type session struct {
ipc interfaces.IPCManager
// Mutex for writing to the socket
lock sync.Mutex
shutdown chan bool
writeChan chan []byte
done bool
}
func newSession(conn *websocket.Conn, bindingCache []string, ipc interfaces.IPCManager, logger *logger.CustomLogger, eventMgr interfaces.EventManager) *session {
return &session{
conn: conn,
bindingCache: bindingCache,
ipc: ipc,
log: logger,
eventManager: eventMgr,
shutdown: make(chan bool),
writeChan: make(chan []byte, 100),
}
}
// Identifier returns a string identifier for the remote connection.
@@ -33,19 +49,15 @@ func (s *session) Identifier() string {
}
func (s *session) sendMessage(msg string) error {
s.lock.Lock()
defer s.lock.Unlock()
if err := s.conn.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil {
s.log.Debug(err.Error())
return err
if !s.done {
s.writeChan <- *(*[]byte)(unsafe.Pointer(&msg))
}
return nil
}
func (s *session) start(firstSession bool) {
s.log.Infof("Connected to frontend.")
go s.writePump()
wailsRuntime := mewn.String("../../runtime/assets/wails.js")
s.evalJS(wailsRuntime, wailsRuntimeMessage)
@@ -74,6 +86,10 @@ func (s *session) start(firstSession bool) {
s.log.Debugf("Got message: %#v\n", string(buffer))
s.ipc.Dispatch(string(buffer), s.Callback)
if s.done {
break
}
}
}
@@ -83,8 +99,38 @@ func (s *session) Callback(data string) error {
}
func (s *session) evalJS(js string, mtype messageType) error {
// Prepend message type to message
return s.sendMessage(mtype.toString() + js)
}
// Shutdown
func (s *session) Shutdown() {
s.done = true
s.shutdown <- true
s.log.Debugf("session %v exit", s.Identifier())
}
// writePump pulls messages from the writeChan and sends them to the client
// since it uses a channel to read the messages the socket is protected without locks
func (s *session) writePump() {
s.log.Debugf("Session %v - writePump start", s.Identifier())
for {
select {
case msg, ok := <-s.writeChan:
s.conn.SetWriteDeadline(time.Now().Add(1 * time.Second))
if !ok {
s.log.Debug("writeChan was closed!")
s.conn.WriteMessage(websocket.CloseMessage, []byte{})
return
}
if err := s.conn.WriteMessage(websocket.TextMessage, msg); err != nil {
s.log.Debug(err.Error())
return
}
case <-s.shutdown:
break
}
}
s.log.Debug("writePump exiting...")
}

File diff suppressed because one or more lines are too long

View File

@@ -245,7 +245,7 @@ func (w *WebView) NewBinding(methodName string) error {
}
// SelectFile opens a dialog that allows the user to select a file
func (w *WebView) SelectFile() string {
func (w *WebView) SelectFile(title string, filter string) string {
var result string
// We need to run this on the main thread, however Dispatch is
@@ -255,7 +255,7 @@ func (w *WebView) SelectFile() string {
wg.Add(1)
go func() {
w.window.Dispatch(func() {
result = w.window.Dialog(wv.DialogTypeOpen, 0, "Select File", "")
result = w.window.Dialog(wv.DialogTypeOpen, 0, title, "", filter)
wg.Done()
})
}()
@@ -273,7 +273,7 @@ func (w *WebView) SelectDirectory() string {
wg.Add(1)
go func() {
w.window.Dispatch(func() {
result = w.window.Dialog(wv.DialogTypeOpen, wv.DialogFlagDirectory, "Select Directory", "")
result = w.window.Dialog(wv.DialogTypeOpen, wv.DialogFlagDirectory, "Select Directory", "", "")
wg.Done()
})
}()
@@ -282,7 +282,7 @@ func (w *WebView) SelectDirectory() string {
}
// SelectSaveFile opens a dialog that allows the user to select a file to save
func (w *WebView) SelectSaveFile() string {
func (w *WebView) SelectSaveFile(title string, filter string) string {
var result string
// We need to run this on the main thread, however Dispatch is
// non-blocking so we launch this in a goroutine and wait for
@@ -291,7 +291,7 @@ func (w *WebView) SelectSaveFile() string {
wg.Add(1)
go func() {
w.window.Dispatch(func() {
result = w.window.Dialog(wv.DialogTypeSave, 0, "Save file", "")
result = w.window.Dialog(wv.DialogTypeSave, 0, title, "", filter)
wg.Done()
})
}()
@@ -329,7 +329,14 @@ func (w *WebView) NotifyEvent(event *messages.EventData) error {
}
}
message := fmt.Sprintf("wails._.Notify('%s','%s')", event.Name, data)
// Double encode data to ensure everything is escaped correctly.
data, err = json.Marshal(string(data))
if err != nil {
w.log.Errorf("Cannot marshal JSON data in event: %s ", err.Error())
return err
}
message := "window.wails._.Notify('" + event.Name + "'," + string(data) + ")"
return w.evalJS(message)
}

View File

@@ -74,9 +74,9 @@ static inline void CgoWebViewSetColor(void *w, uint8_t r, uint8_t g, uint8_t b,
}
static inline void CgoDialog(void *w, int dlgtype, int flags,
char *title, char *arg, char *res, size_t ressz) {
char *title, char *arg, char *res, size_t ressz, char *filter) {
webview_dialog(w, dlgtype, flags,
(const char*)title, (const char*) arg, res, ressz);
(const char*)title, (const char*) arg, res, ressz, filter);
}
static inline int CgoWebViewEval(void *w, char *js) {
@@ -186,7 +186,7 @@ type WebView interface {
// Dialog() opens a system dialog of the given type and title. String
// argument can be provided for certain dialogs, such as alert boxes. For
// alert boxes argument is a message inside the dialog box.
Dialog(dlgType DialogType, flags int, title string, arg string) string
Dialog(dlgType DialogType, flags int, title string, arg string, filter string) string
// Terminate() breaks the main UI loop. This method must be called from the main thread
// only. See Dispatch() for more details.
Terminate()
@@ -311,7 +311,7 @@ func (w *webview) SetFullscreen(fullscreen bool) {
C.CgoWebViewSetFullscreen(w.w, C.int(boolToInt(fullscreen)))
}
func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string) string {
func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string, filter string) string {
const maxPath = 4096
titlePtr := C.CString(title)
defer C.free(unsafe.Pointer(titlePtr))
@@ -319,8 +319,10 @@ func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string
defer C.free(unsafe.Pointer(argPtr))
resultPtr := (*C.char)(C.calloc((C.size_t)(unsafe.Sizeof((*C.char)(nil))), (C.size_t)(maxPath)))
defer C.free(unsafe.Pointer(resultPtr))
filterPtr := C.CString(filter)
defer C.free(unsafe.Pointer(filterPtr))
C.CgoDialog(w.w, C.int(dlgType), C.int(flags), titlePtr,
argPtr, resultPtr, C.size_t(maxPath))
argPtr, resultPtr, C.size_t(maxPath), filterPtr)
return C.GoString(resultPtr)
}

View File

@@ -139,7 +139,7 @@ struct webview_priv
#define DEFAULT_URL \
"data:text/" \
"html,%3C%21DOCTYPE%20html%3E%0A%3Chtml%20lang=%22en%22%3E%0A%3Chead%3E%" \
"3Cmeta%20charset=%22utf-8%22%3E%3Cmeta%20http-equiv=%22IE=edge%22%" \
"3Cmeta%20charset=%22utf-8%22%3E%3Cmeta%20http-equiv=%22IE=edge%22%" \
"20content=%22IE=edge%22%3E%3C%2Fhead%3E%0A%3Cbody%3E%3Cdiv%20id=%22app%22%" \
"3E%3C%2Fdiv%3E%3Cscript%20type=%22text%2Fjavascript%22%3E%3C%2Fscript%3E%" \
"3C%2Fbody%3E%0A%3C%2Fhtml%3E"
@@ -174,7 +174,7 @@ struct webview_priv
WEBVIEW_API void webview_dialog(struct webview *w,
enum webview_dialog_type dlgtype, int flags,
const char *title, const char *arg,
char *result, size_t resultsz);
char *result, size_t resultsz, char *filter);
WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn,
void *arg);
WEBVIEW_API void webview_terminate(struct webview *w);
@@ -418,7 +418,7 @@ struct webview_priv
WEBVIEW_API void webview_dialog(struct webview *w,
enum webview_dialog_type dlgtype, int flags,
const char *title, const char *arg,
char *result, size_t resultsz)
char *result, size_t resultsz, char *filter)
{
GtkWidget *dlg;
if (result != NULL)
@@ -438,6 +438,17 @@ struct webview_priv
"_Cancel", GTK_RESPONSE_CANCEL,
(dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ? "_Open" : "_Save"),
GTK_RESPONSE_ACCEPT, NULL);
if (filter[0] != '\0') {
GtkFileFilter *file_filter = gtk_file_filter_new();
gchar **filters = g_strsplit(filter, ",", -1);
gint i;
for(i = 0; filters && filters[i]; i++) {
gtk_file_filter_add_pattern(file_filter, filters[i]);
}
gtk_file_filter_set_name(file_filter, filter);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dlg), file_filter);
g_strfreev(filters);
}
gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE);
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE);
gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE);
@@ -1227,7 +1238,7 @@ struct webview_priv
}
VariantInit(&myURL);
myURL.vt = VT_BSTR;
// #ifndef UNICODE
// #ifndef UNICODE
{
wchar_t *buffer = webview_to_utf16(webPageName);
if (buffer == NULL)
@@ -1237,9 +1248,9 @@ struct webview_priv
myURL.bstrVal = SysAllocString(buffer);
GlobalFree(buffer);
}
// #else
// myURL.bstrVal = SysAllocString(webPageName);
// #endif
// #else
// myURL.bstrVal = SysAllocString(webPageName);
// #endif
if (!myURL.bstrVal)
{
badalloc:
@@ -1277,7 +1288,7 @@ struct webview_priv
if (!SafeArrayAccessData(sfArray, (void **)&pVar))
{
pVar->vt = VT_BSTR;
// #ifndef UNICODE
// #ifndef UNICODE
{
wchar_t *buffer = webview_to_utf16(url);
if (buffer == NULL)
@@ -1287,9 +1298,9 @@ struct webview_priv
bstr = SysAllocString(buffer);
GlobalFree(buffer);
}
// #else
// bstr = SysAllocString(url);
// #endif
// #else
// bstr = SysAllocString(url);
// #endif
if ((pVar->bstrVal = bstr))
{
htmlDoc2->lpVtbl->write(htmlDoc2, sfArray);
@@ -1410,6 +1421,8 @@ struct webview_priv
wc.hInstance = hInstance;
wc.lpfnWndProc = wndproc;
wc.lpszClassName = classname;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(100));
wc.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(100));
RegisterClassEx(&wc);
style = WS_OVERLAPPEDWINDOW;
@@ -1418,6 +1431,13 @@ struct webview_priv
style = WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;
}
// Scale
// Credit: https://github.com/webview/webview/issues/54#issuecomment-379528243
HDC hDC = GetDC(NULL);
w->width = GetDeviceCaps(hDC, 88)*w->width/96.0;
w->height = GetDeviceCaps(hDC, 90)*w->height/96.0;
ReleaseDC(NULL, hDC);
rect.left = 0;
rect.top = 0;
rect.right = w->width;
@@ -1444,12 +1464,12 @@ struct webview_priv
rect.right - rect.left, rect.bottom - rect.top,
HWND_DESKTOP, NULL, hInstance, (void *)w);
#else
w->priv.hwnd =
w->priv.hwnd =
CreateWindowEx(0, classname, w->title, style, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
HWND_DESKTOP, NULL, hInstance, (void *)w);
#endif
if (w->priv.hwnd == 0)
{
OleUninitialize();
@@ -1466,8 +1486,7 @@ struct webview_priv
#else
SetWindowText(w->priv.hwnd, w->title);
#endif
ShowWindow(w->priv.hwnd, SW_SHOWDEFAULT);
UpdateWindow(w->priv.hwnd);
SetFocus(w->priv.hwnd);
@@ -1494,6 +1513,11 @@ struct webview_priv
case WM_KEYDOWN:
case WM_KEYUP:
{
// Disable refresh when pressing F5 on windows
if (msg.wParam == VK_F5)
{
break;
}
HRESULT r = S_OK;
IWebBrowser2 *webBrowser2;
IOleObject *browser = *w->priv.browser;
@@ -1603,7 +1627,7 @@ struct webview_priv
WEBVIEW_API void webview_set_title(struct webview *w, const char *title)
{
#ifdef UNICODE
#ifdef UNICODE
wchar_t *u16title = webview_to_utf16(title);
if (u16title == NULL)
{
@@ -1611,12 +1635,11 @@ struct webview_priv
}
SetWindowText(w->priv.hwnd, u16title);
GlobalFree(u16title);
#else
#else
SetWindowText(w->priv.hwnd, title);
#endif
#endif
}
WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen)
{
if (w->priv.is_fullscreen == !!fullscreen)
@@ -1777,7 +1800,7 @@ struct webview_priv
WEBVIEW_API void webview_dialog(struct webview *w,
enum webview_dialog_type dlgtype, int flags,
const char *title, const char *arg,
char *result, size_t resultsz)
char *result, size_t resultsz, char *filter)
{
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
dlgtype == WEBVIEW_DIALOG_TYPE_SAVE)
@@ -1817,6 +1840,32 @@ struct webview_priv
FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS |
FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE;
}
if (filter[0] != '\0')
{
int count;
int i=0;
char* token;
char* filter_dup = strdup(filter);
for (count=1; filter[count]; filter[count]==',' ? count++ : *filter++);
COMDLG_FILTERSPEC rgSpec[count];
char* filters[count];
token = strtok(filter_dup, ",");
while(token != NULL)
{
filters[i] = token;
token = strtok(NULL, ",");
i++;
}
for (int i=0; i < count; i++) {
wchar_t *wFilter = (wchar_t *)malloc(4096);
MultiByteToWideChar(CP_ACP, 0, filters[i], -1, wFilter, 4096);
rgSpec[i].pszName = wFilter;
rgSpec[i].pszSpec = wFilter;
}
if (dlg->lpVtbl->SetFileTypes(dlg, count, rgSpec) != S_OK) {
goto error_dlg;
}
}
if (dlg->lpVtbl->GetOptions(dlg, &opts) != S_OK)
{
goto error_dlg;
@@ -1927,22 +1976,26 @@ struct webview_priv
[script setValue:self forKey:@"external"];
}
static void webview_run_input_open_panel(id self, SEL cmd, id webview,
id listener, BOOL allowMultiple) {
static void webview_run_input_open_panel(id self, SEL cmd, id webview,
id listener, BOOL allowMultiple)
{
char filename[256] = "";
struct webview *w =
(struct webview *)objc_getAssociatedObject(self, "webview");
webview_dialog(w, WEBVIEW_DIALOG_TYPE_OPEN, WEBVIEW_DIALOG_FLAG_FILE, "", "",
filename, 255);
if (strlen(filename)) {
filename, 255, "");
filename[255] = '\0';
if (strlen(filename) > 0)
{
[listener chooseFilename:[NSString stringWithUTF8String:filename]];
} else {
}
else
{
[listener cancel];
}
}
static void webview_external_invoke(id self, SEL cmd, id arg)
{
struct webview *w =
@@ -1955,7 +2008,7 @@ static void webview_run_input_open_panel(id self, SEL cmd, id webview,
{
return;
}
w->external_invoke_cb(w, [(NSString *)(arg)UTF8String]);
w->external_invoke_cb(w, [(NSString *)(arg) UTF8String]);
}
WEBVIEW_API int webview_init(struct webview *w)
@@ -2194,12 +2247,16 @@ static void webview_run_input_open_panel(id self, SEL cmd, id webview,
WEBVIEW_API void webview_dialog(struct webview *w,
enum webview_dialog_type dlgtype, int flags,
const char *title, const char *arg,
char *result, size_t resultsz)
char *result, size_t resultsz, char *filter)
{
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
dlgtype == WEBVIEW_DIALOG_TYPE_SAVE)
{
NSSavePanel *panel;
NSString *filter_str = [NSString stringWithUTF8String:filter];
filter_str = [filter_str stringByReplacingOccurrencesOfString:@"*."
withString:@""];
NSArray *fileTypes = [filter_str componentsSeparatedByString:@","];
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN)
{
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
@@ -2212,6 +2269,10 @@ static void webview_run_input_open_panel(id self, SEL cmd, id webview,
{
[openPanel setCanChooseFiles:YES];
[openPanel setCanChooseDirectories:NO];
if(filter[0] != NULL)
{
[openPanel setAllowedFileTypes:fileTypes];
}
}
[openPanel setResolvesAliases:NO];
[openPanel setAllowsMultipleSelection:NO];
@@ -2225,6 +2286,10 @@ static void webview_run_input_open_panel(id self, SEL cmd, id webview,
[panel setShowsHiddenFiles:YES];
[panel setExtensionHidden:NO];
[panel setCanSelectHiddenExtension:NO];
if(filter[0] != NULL)
{
[panel setAllowedFileTypes:fileTypes];
}
[panel setTreatsFilePackagesAsDirectories:YES];
[panel beginSheetModalForWindow:w->priv.window
completionHandler:^(NSInteger result) {

View File

@@ -1,32 +0,0 @@
package wails
import (
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/runtime"
)
// CustomLogger type alias
type CustomLogger = logger.CustomLogger
// Runtime is the Wails Runtime Interface, given to a user who has defined the WailsInit method
type Runtime struct {
Events *runtime.Events
Log *runtime.Log
Dialog *runtime.Dialog
Window *runtime.Window
Browser *runtime.Browser
FileSystem *runtime.FileSystem
}
// NewRuntime creates a new Runtime struct
func NewRuntime(eventManager interfaces.EventManager, renderer interfaces.Renderer) *Runtime {
return &Runtime{
Events: runtime.NewEvents(eventManager),
Log: runtime.NewLog(),
Dialog: runtime.NewDialog(renderer),
Window: runtime.NewWindow(renderer),
Browser: runtime.NewBrowser(),
FileSystem: runtime.NewFileSystem(),
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,7 @@
package runtime
import "github.com/wailsapp/wails/lib/interfaces"
import "strings"
// Dialog exposes an interface to native dialogs
type Dialog struct {
@@ -15,8 +16,16 @@ func NewDialog(renderer interfaces.Renderer) *Dialog {
}
// SelectFile prompts the user to select a file
func (r *Dialog) SelectFile() string {
return r.renderer.SelectFile()
func (r *Dialog) SelectFile(params ...string) string {
title := "Select File"
filter := ""
if len(params) > 0 {
title = params[0]
}
if len(params) > 1 {
filter = strings.Replace(params[1], " ", "", -1)
}
return r.renderer.SelectFile(title, filter)
}
// SelectDirectory prompts the user to select a directory
@@ -25,6 +34,14 @@ func (r *Dialog) SelectDirectory() string {
}
// SelectSaveFile prompts the user to select a file for saving
func (r *Dialog) SelectSaveFile() string {
return r.renderer.SelectSaveFile()
func (r *Dialog) SelectSaveFile(params ...string) string {
title := "Select Save"
filter := ""
if len(params) > 0 {
title = params[0]
}
if len(params) > 1 {
filter = strings.Replace(params[1], " ", "", -1)
}
return r.renderer.SelectSaveFile(title, filter)
}

View File

@@ -1,9 +1,9 @@
package runtime
import homedir "github.com/mitchellh/go-homedir"
import "os"
// FileSystem exposes file system utilities to the runtime
type FileSystem struct {}
type FileSystem struct{}
// NewFileSystem creates a new FileSystem struct
func NewFileSystem() *FileSystem {
@@ -12,5 +12,5 @@ func NewFileSystem() *FileSystem {
// HomeDir returns the user's home directory
func (r *FileSystem) HomeDir() (string, error) {
return homedir.Dir()
return os.UserHomeDir()
}

View File

@@ -15,6 +15,7 @@ import { NewBinding } from './bindings';
import { Callback } from './calls';
import { AddScript, InjectCSS } from './utils';
import { AddIPCListener } from './ipc';
import * as Store from './store';
// Initialise global if not already
window.wails = window.wails || {};
@@ -42,6 +43,7 @@ var runtime = {
Heartbeat,
Acknowledge,
},
Store,
_: internal,
};

82
runtime/js/core/store.js Normal file
View File

@@ -0,0 +1,82 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The lightweight framework for web-like apps
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 6 */
/**
* Creates a new sync store with the given name and optional default value
*
* @export
* @param {string} name
* @param {*} optionalDefault
*/
export function New(name, optionalDefault) {
var data;
// Check we are initialised
if( !window.wails) {
throw Error('Wails is not initialised');
}
// Store for the callbacks
let callbacks = [];
// Subscribe to updates by providing a callback
this.subscribe = (callback) => {
callbacks.push(callback);
};
// sets the store data to the provided `newdata` value
this.set = (newdata) => {
data = newdata;
// Emit a notification to back end
window.wails.Events.Emit('wails:sync:store:updatedbyfrontend:'+name, JSON.stringify(data));
// Notify callbacks
callbacks.forEach( function(callback) {
callback(data);
});
};
// update mutates the value in the store by calling the
// provided method with the current value. The value returned
// by the updater function will be set as the new store value
this.update = (updater) => {
var newValue = updater(data);
this.set(newValue);
};
// Setup event callback
window.wails.Events.On('wails:sync:store:updatedbybackend:'+name, function(result) {
// Parse data
result = JSON.parse(result);
// Todo: Potential preprocessing?
// Save data
data = result;
// Notify callbacks
callbacks.forEach( function(callback) {
callback(data);
});
});
// Set to the optional default if set
if( optionalDefault ) {
this.set(optionalDefault);
}
return this;
}

File diff suppressed because it is too large Load Diff

View File

@@ -29,15 +29,15 @@
},
"homepage": "https://github.com/wailsapp/runtime#readme",
"devDependencies": {
"@babel/cli": "^7.5.0",
"@babel/core": "^7.5.4",
"@babel/plugin-transform-object-assign": "^7.2.0",
"@babel/preset-env": "^7.5.4",
"babel-loader": "^8.0.6",
"babel-preset-minify": "^0.5.0",
"core-js": "^3.1.4",
"eslint": "^6.5.1",
"webpack": "^4.35.3",
"webpack-cli": "^3.3.5"
"@babel/cli": "^7.11.5",
"@babel/core": "^7.11.5",
"@babel/plugin-transform-object-assign": "^7.10.4",
"@babel/preset-env": "^7.11.5",
"babel-loader": "^8.1.0",
"babel-preset-minify": "^0.5.1",
"core-js": "^3.6.5",
"eslint": "^7.8.1",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
}
}

View File

@@ -13,10 +13,12 @@ const Log = require('./log');
const Browser = require('./browser');
const Events = require('./events');
const Init = require('./init');
const Store = require('./store');
module.exports = {
Log: Log,
Browser: Browser,
Events: Events,
Init: Init
Init: Init,
Store: Store,
};

492
runtime/js/runtime/package-lock.json generated Normal file
View File

@@ -0,0 +1,492 @@
{
"name": "@wailsapp/runtime",
"version": "1.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
"camelcase": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
"dev": true
},
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
"dev": true,
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wrap-ansi": "^2.0.0"
}
},
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true
},
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"dev": true
},
"dts-dom": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/dts-dom/-/dts-dom-3.6.0.tgz",
"integrity": "sha512-on5jxTgt+A6r0Zyyz6ZRHXaAO7J1VPnOd6+AmvI1vH440AlAZZNc5rUHzgPuTjGlrVr1rOWQYNl7ZJK6rDohbw==",
"dev": true
},
"dts-gen": {
"version": "0.5.8",
"resolved": "https://registry.npmjs.org/dts-gen/-/dts-gen-0.5.8.tgz",
"integrity": "sha512-kIAV6dlHaF7r5J+tIuOC1BJls2P72YM0cyWQUR88zcJEpX2ccRZe+HmXLfkkvfPwjvSO3FEqUiyC8On/grx5qw==",
"dev": true,
"requires": {
"dts-dom": "^3.6.0",
"parse-git-config": "^1.1.1",
"typescript": "^3.5.1",
"yargs": "^4.8.1"
}
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"requires": {
"is-arrayish": "^0.2.1"
}
},
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"requires": {
"is-extendable": "^0.1.0"
}
},
"find-up": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
"integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
"dev": true,
"requires": {
"path-exists": "^2.0.0",
"pinkie-promise": "^2.0.0"
}
},
"fs-exists-sync": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz",
"integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=",
"dev": true
},
"get-caller-file": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
"dev": true
},
"git-config-path": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/git-config-path/-/git-config-path-1.0.1.tgz",
"integrity": "sha1-bTP37WPbDQ4RgTFQO6s6ykfVRmQ=",
"dev": true,
"requires": {
"extend-shallow": "^2.0.1",
"fs-exists-sync": "^0.1.0",
"homedir-polyfill": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"homedir-polyfill": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
"integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
"dev": true,
"requires": {
"parse-passwd": "^1.0.0"
}
},
"hosted-git-info": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
"dev": true
},
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true
},
"invert-kv": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
"dev": true
},
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true
},
"is-extendable": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
"dev": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
"requires": {
"number-is-nan": "^1.0.0"
}
},
"is-utf8": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
"integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
"dev": true
},
"lcid": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
"integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
"dev": true,
"requires": {
"invert-kv": "^1.0.0"
}
},
"load-json-file": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"parse-json": "^2.2.0",
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0",
"strip-bom": "^2.0.0"
}
},
"lodash.assign": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
"dev": true
},
"normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
"dev": true,
"requires": {
"hosted-git-info": "^2.1.4",
"resolve": "^1.10.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
}
},
"number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true
},
"os-locale": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
"dev": true,
"requires": {
"lcid": "^1.0.0"
}
},
"parse-git-config": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/parse-git-config/-/parse-git-config-1.1.1.tgz",
"integrity": "sha1-06mYQxcTL1c5hxK7pDjhKVkN34w=",
"dev": true,
"requires": {
"extend-shallow": "^2.0.1",
"fs-exists-sync": "^0.1.0",
"git-config-path": "^1.0.1",
"ini": "^1.3.4"
}
},
"parse-json": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
"dev": true,
"requires": {
"error-ex": "^1.2.0"
}
},
"parse-passwd": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
"integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
"dev": true
},
"path-exists": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
"integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
"dev": true,
"requires": {
"pinkie-promise": "^2.0.0"
}
},
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"path-type": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
"integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0"
}
},
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
},
"pinkie": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
"integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
"dev": true
},
"pinkie-promise": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
"dev": true,
"requires": {
"pinkie": "^2.0.0"
}
},
"read-pkg": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
"integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
"dev": true,
"requires": {
"load-json-file": "^1.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^1.0.0"
}
},
"read-pkg-up": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
"integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
"dev": true,
"requires": {
"find-up": "^1.0.0",
"read-pkg": "^1.0.0"
}
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true
},
"require-main-filename": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
"dev": true
},
"resolve": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
"integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true
},
"spdx-correct": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
"integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
"dev": true,
"requires": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-exceptions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
"integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
"dev": true
},
"spdx-expression-parse": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
"dev": true,
"requires": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-license-ids": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
"integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
"dev": true
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
"ansi-regex": "^2.0.0"
}
},
"strip-bom": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
"integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
"dev": true,
"requires": {
"is-utf8": "^0.2.0"
}
},
"typescript": {
"version": "3.9.7",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
"integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
"dev": true
},
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"dev": true,
"requires": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
}
},
"which-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
"dev": true
},
"window-size": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz",
"integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=",
"dev": true
},
"wrap-ansi": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"dev": true,
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1"
}
},
"y18n": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
"dev": true
},
"yargs": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz",
"integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=",
"dev": true,
"requires": {
"cliui": "^3.2.0",
"decamelize": "^1.1.1",
"get-caller-file": "^1.0.1",
"lodash.assign": "^4.0.3",
"os-locale": "^1.4.0",
"read-pkg-up": "^1.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^1.0.1",
"which-module": "^1.0.0",
"window-size": "^0.2.0",
"y18n": "^3.2.1",
"yargs-parser": "^2.4.1"
}
},
"yargs-parser": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz",
"integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=",
"dev": true,
"requires": {
"camelcase": "^3.0.0",
"lodash.assign": "^4.0.6"
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@wailsapp/runtime",
"version": "1.0.10",
"version": "1.1.1",
"description": "Wails Javascript runtime library",
"main": "main.js",
"types": "runtime.d.ts",

View File

@@ -6,12 +6,12 @@ declare const wailsapp__runtime: {
OpenURL(url: string): Promise<any>;
};
Events: {
Acknowledge(eventName: string): void;
Emit(eventName: string): void;
Heartbeat(eventName: string, timeInMilliseconds: number, callback: () => void): void;
On(eventName: string, callback: () => void): void;
OnMultiple(eventName: string, callback: () => void, maxCallbacks: number): void;
Once(eventName: string, callback: () => void): void;
Acknowledge(eventName: string): void;
Emit(eventName: string, data?: any): void;
Heartbeat(eventName: string, timeInMilliseconds: number, callback: (data?: any) => void): void;
On(eventName: string, callback: (data?: any) => void): void;
OnMultiple(eventName: string, callback: (data?: any) => void, maxCallbacks: number): void;
Once(eventName: string, callback: (data?: any) => void): void;
};
Init(callback: () => void): void;
Log: {
@@ -21,6 +21,9 @@ declare const wailsapp__runtime: {
Info(message: string): void;
Warning(message: string): void;
};
Store: {
New(name: string, optionalDefault?: any): any;
};
};

View File

@@ -0,0 +1,27 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The lightweight framework for web-like apps
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 6 */
/**
* Create a new Store with the given name and optional default value
*
* @export
* @param {string} name
* @param {*} optionalDefault
*/
function New(name, optionalDefault) {
return window.wails.Store.New(name, optionalDefault);
}
module.exports = {
New: New,
};

29
runtime/runtime.go Normal file
View File

@@ -0,0 +1,29 @@
package runtime
import "github.com/wailsapp/wails/lib/interfaces"
// Runtime is the Wails Runtime Interface, given to a user who has defined the WailsInit method
type Runtime struct {
Events *Events
Log *Log
Dialog *Dialog
Window *Window
Browser *Browser
FileSystem *FileSystem
Store *StoreProvider
}
// NewRuntime creates a new Runtime struct
func NewRuntime(eventManager interfaces.EventManager, renderer interfaces.Renderer) *Runtime {
result := &Runtime{
Events: NewEvents(eventManager),
Log: NewLog(),
Dialog: NewDialog(renderer),
Window: NewWindow(renderer),
Browser: NewBrowser(),
FileSystem: NewFileSystem(),
}
// We need a reference to itself
result.Store = NewStoreProvider(result)
return result
}

298
runtime/store.go Normal file
View File

@@ -0,0 +1,298 @@
// Package runtime contains all the methods and data structures related to the
// runtime library of Wails. This includes both Go and JS runtimes.
package runtime
import (
"bytes"
"encoding/json"
"fmt"
"log"
"reflect"
"sync"
)
// Options defines the optional data that may be used
// when creating a Store
type Options struct {
// The name of the store
Name string
// The runtime to attach the store to
Runtime *Runtime
// Indicates if notifying Go listeners should be notified of updates
// synchronously (on the current thread) or asynchronously using
// goroutines
NotifySynchronously bool
}
// StoreProvider is a struct that creates Stores
type StoreProvider struct {
runtime *Runtime
}
// NewStoreProvider creates new stores using the provided Runtime reference.
func NewStoreProvider(runtime *Runtime) *StoreProvider {
return &StoreProvider{
runtime: runtime,
}
}
// Store is where we keep named data
type Store struct {
name string
data reflect.Value
dataType reflect.Type
eventPrefix string
callbacks []reflect.Value
runtime *Runtime
notifySynchronously bool
// Lock
mux sync.Mutex
// Error handler
errorHandler func(error)
}
// New creates a new store
func (p *StoreProvider) New(name string, defaultValue interface{}) *Store {
dataType := reflect.TypeOf(defaultValue)
result := Store{
name: name,
runtime: p.runtime,
data: reflect.ValueOf(defaultValue),
dataType: dataType,
}
// Setup the sync listener
result.setupListener()
return &result
}
// OnError takes a function that will be called
// whenever an error occurs
func (s *Store) OnError(callback func(error)) {
s.errorHandler = callback
}
// Processes the updates sent by the front end
func (s *Store) processUpdatedData(data string) error {
// Decode incoming data
var rawdata json.RawMessage
d := json.NewDecoder(bytes.NewBufferString(data))
err := d.Decode(&rawdata)
if err != nil {
return err
}
// Create a new instance of our data and unmarshal
// the received value into it
newData := reflect.New(s.dataType).Interface()
err = json.Unmarshal(rawdata, &newData)
if err != nil {
return err
}
// Lock mutex for writing
s.mux.Lock()
// Handle nulls
if newData == nil {
s.data = reflect.Zero(s.dataType)
} else {
// Store the resultant value in the data store
s.data = reflect.ValueOf(newData).Elem()
}
// Unlock mutex
s.mux.Unlock()
return nil
}
// Setup listener for front end changes
func (s *Store) setupListener() {
// Listen for updates from the front end
s.runtime.Events.On("wails:sync:store:updatedbyfrontend:"+s.name, func(data ...interface{}) {
// Process the incoming data
err := s.processUpdatedData(data[0].(string))
if err != nil {
if s.errorHandler != nil {
s.errorHandler(err)
return
}
}
// Notify listeners
s.notify()
})
}
// notify the listeners of the current data state
func (s *Store) notify() {
// Execute callbacks
for _, callback := range s.callbacks {
// Build args
args := []reflect.Value{s.data}
if s.notifySynchronously {
callback.Call(args)
} else {
go callback.Call(args)
}
}
}
// Set will update the data held by the store
// and notify listeners of the change
func (s *Store) Set(data interface{}) error {
inType := reflect.TypeOf(data)
if inType != s.dataType {
return fmt.Errorf("invalid data given in Store.Set(). Expected %s, got %s", s.dataType.String(), inType.String())
}
// Save data
s.mux.Lock()
s.data = reflect.ValueOf(data)
s.mux.Unlock()
// Stringify data
newdata, err := json.Marshal(data)
if err != nil {
if s.errorHandler != nil {
return err
}
}
// Emit event to front end
s.runtime.Events.Emit("wails:sync:store:updatedbybackend:"+s.name, string(newdata))
// Notify subscribers
s.notify()
return nil
}
// callbackCheck ensures the given function to Subscribe() is
// of the correct signature. Absolutely cannot wait for
// generics to land rather than writing this nonsense.
func (s *Store) callbackCheck(callback interface{}) error {
// Get type
callbackType := reflect.TypeOf(callback)
// Check callback is a function
if callbackType.Kind() != reflect.Func {
return fmt.Errorf("invalid value given to store.Subscribe(). Expected 'func(%s)'", s.dataType.String())
}
// Check input param
if callbackType.NumIn() != 1 {
return fmt.Errorf("invalid number of parameters given in callback function. Expected 1")
}
// Check input data type
if callbackType.In(0) != s.dataType {
return fmt.Errorf("invalid type for input parameter given in callback function. Expected %s, got %s", s.dataType.String(), callbackType.In(0))
}
// Check output param
if callbackType.NumOut() != 0 {
return fmt.Errorf("invalid number of return parameters given in callback function. Expected 0")
}
return nil
}
// Subscribe will subscribe to updates to the store by
// providing a callback. Any updates to the store are sent
// to the callback
func (s *Store) Subscribe(callback interface{}) {
err := s.callbackCheck(callback)
if err != nil {
log.Fatal(err)
}
callbackFunc := reflect.ValueOf(callback)
s.callbacks = append(s.callbacks, callbackFunc)
}
// updaterCheck ensures the given function to Update() is
// of the correct signature. Absolutely cannot wait for
// generics to land rather than writing this nonsense.
func (s *Store) updaterCheck(updater interface{}) error {
// Get type
updaterType := reflect.TypeOf(updater)
// Check updater is a function
if updaterType.Kind() != reflect.Func {
return fmt.Errorf("invalid value given to store.Update(). Expected 'func(%s) %s'", s.dataType.String(), s.dataType.String())
}
// Check input param
if updaterType.NumIn() != 1 {
return fmt.Errorf("invalid number of parameters given in updater function. Expected 1")
}
// Check input data type
if updaterType.In(0) != s.dataType {
return fmt.Errorf("invalid type for input parameter given in updater function. Expected %s, got %s", s.dataType.String(), updaterType.In(0))
}
// Check output param
if updaterType.NumOut() != 1 {
return fmt.Errorf("invalid number of return parameters given in updater function. Expected 1")
}
// Check output data type
if updaterType.Out(0) != s.dataType {
return fmt.Errorf("invalid type for return parameter given in updater function. Expected %s, got %s", s.dataType.String(), updaterType.Out(0))
}
return nil
}
// Update takes a function that is passed the current state.
// The result of that function is then set as the new state
// of the store. This will notify listeners of the change
func (s *Store) Update(updater interface{}) {
err := s.updaterCheck(updater)
if err != nil {
log.Fatal(err)
}
// Build args
args := []reflect.Value{s.data}
// Make call
results := reflect.ValueOf(updater).Call(args)
// We will only have 1 result. Set the store to it
err = s.Set(results[0].Interface())
if err != nil && s.errorHandler != nil {
s.errorHandler(err)
}
}
// Get returns the value of the data that's kept in the current state / Store
func (s *Store) Get() interface{} {
return s.data.Interface()
}

View File

@@ -1,5 +1,13 @@
#!/usr/bin/env bash
echo "**** Checking if Wails passes unit tests ****"
if ! go test ./...
then
echo ""
echo "ERROR: Unit tests failed!"
exit 1;
fi
# Build runtime
echo "**** Building Runtime ****"
cd runtime/js
@@ -15,9 +23,23 @@ cd lib/renderer
mewn
cd ../..
echo "**** Installing Wails locally ****"
cd cmd/wails
go install
echo "**** Checking if Wails compiles ****"
if ! go build .
then
echo ""
echo "ERROR: Build failed!"
exit 1;
fi
echo "**** Installing Wails locally ****"
if ! go install
then
echo ""
echo "ERROR: Install failed!"
exit 1;
fi
cd ../..
echo "**** Tidying the mods! ****"