Compare commits

...

167 Commits

Author SHA1 Message Date
Lea Anthony
a6fa5feb0c chore: v1 tag 2019-12-10 18:16:13 +11:00
Lea Anthony
67a52b2a57 Readme update 2019-12-10 18:11:49 +11:00
Lea Anthony
99d22fdcb7 Updates to README 2019-12-10 18:00:24 +11:00
Lea Anthony
7051ffd425 v0.20.0 2019-12-05 08:28:17 +11:00
Lea Anthony
bac823566c v0.20.0-pre2 2019-11-28 22:48:14 +11:00
Lea Anthony
401ebee719 fix: create platform specific init 2019-11-28 22:47:41 +11:00
Lea Anthony
b5b92c4afa v0.20.0-pre1 2019-11-27 22:56:18 +11:00
Lea Anthony
2c2aee2d30 Windows console (#299)
* initial release of wails console

* lint fix
2019-11-27 22:55:19 +11:00
Lea Anthony
806b1aa8e0 angular windows fixes (#298) 2019-11-27 22:35:14 +11:00
Lea Anthony
416bd7bfc4 Merge branch 'develop' of https://github.com/wailsapp/wails into develop 2019-11-27 22:22:38 +11:00
Lea Anthony
aca72d3f7a Hidpi fix (#297)
* Add global error handler for runtime

* Set DPI Aware on windows
2019-11-27 22:22:10 +11:00
Lea Anthony
ad20e2622b Add global error handler for runtime 2019-11-27 21:28:33 +11:00
Lea Anthony
daeda501f4 Merge branch 'master' into develop 2019-11-05 14:45:30 +11:00
lea
fb1e8647bc release v0.19.0 2019-11-05 14:43:37 +11:00
Lea Anthony
723236f348 chore: bump version 2019-11-04 04:06:47 +11:00
Lea Anthony
394a823d82 fix: remove unicode flag 2019-11-04 04:02:42 +11:00
Lea Anthony
afe57802ad v0.18.15-pre 2019-11-02 21:07:02 +11:00
Lea Anthony
914642bd1d Backport (#283)
* Develop (#265)


* Patch for file dialog on OSX

* Update CONTRIBUTORS.md

* 262 add wails shutdown method (#264)

* feat: support close in bridge mode

* feat: WailsShutdown callback

Now handles proper shutdown through:
  * runtime.Window.Close()
  * Killing the main window
  * Ctrl-C

* chore: version bump

* chore: version bump

* feat: adjust binary name for OS

* fix: allow spaces in gcc path

* feat: migrate command

* fix: npm/node versions

* fix: allow IE for serve

* feat: go build script

* fix: make runtime ES2015 compliant

* fix: remove invoke patch

* fix: allow any line endings

* chore: remove legacy bridge files

* chore: latest assets
2019-11-02 21:06:02 +11:00
Lea Anthony
dafd9bcb24 fix: make binary name more os specific 2019-10-29 10:24:05 +11:00
Lea Anthony
7f75f27f6b fix: debug build should use terminal 2019-10-29 07:57:29 +11:00
Lea Anthony
d77fa1ee74 fix: npm/node version reporting 2019-10-26 05:36:00 +11:00
Lea Anthony
82e00ff83a hotfix: build issue for windows 2019-10-25 18:21:18 +11:00
Lea Anthony
14f91ab109 release v0.18.12-pre 2019-10-25 18:13:57 +11:00
Lea Anthony
19706a12a4 261 korean fonts (#276)
* fix: linting

* chore: bump version

* fix: unicode text for Windows

* fix: re-add webview_set_title
2019-10-25 18:12:56 +11:00
Lea Anthony
52e6091f0f release v0.18.11-pre 2019-10-25 17:39:12 +11:00
Lea Anthony
2db1624faf 274 support spaces in windows paths (#275)
* fix: escape windows commands

* fix: allow spaces in path to windres

* Update go.mod
2019-10-25 17:37:36 +11:00
Lea Anthony
f5d3fb0848 release v0.18.10-pre 2019-10-25 12:34:43 +11:00
Lea Anthony
85a64914aa 261 korean fonts (#273)
* fix: linting

* chore: bump version

* fix: unicode text for Windows
2019-10-25 12:33:29 +11:00
Lea Anthony
0819207e33 Bump version to v0.18.9-pre 2019-10-25 12:05:51 +11:00
Lea Anthony
50a0bc7701 fix: add flag to fix windows builds (#272) 2019-10-25 12:04:08 +11:00
Lea Anthony
c51f0cad6f chore: version bump 2019-10-24 16:13:42 +11:00
Lea Anthony
6795f6c678 Migrate tool (#270)
Experimental migration tool for projects < v1.0.0
2019-10-24 16:11:50 +11:00
Lea Anthony
315ef5f7ea fix: detect encoding on windows (#266)
* fix: linting

* chore: bump version
2019-10-24 16:04:32 +11:00
Lea Anthony
24530d9da4 fix: force install when node_modules missing (#268) 2019-10-24 09:36:42 +11:00
Lea Anthony
d572418ec3 chore: version bump 2019-10-23 14:12:38 +11:00
Lea Anthony
8c7480d277 Develop (#265)
* Patch for file dialog on OSX

* Update CONTRIBUTORS.md

* 262 add wails shutdown method (#264)

* feat: support close in bridge mode

* feat: WailsShutdown callback

Now handles proper shutdown through:
  * runtime.Window.Close()
  * Killing the main window
  * Ctrl-C

* chore: version bump
2019-10-23 14:08:56 +11:00
Lea Anthony
96fc70df26 Merge branch 'master' into develop 2019-10-23 14:07:22 +11:00
Lea Anthony
1c8d4c902a Merge branch 'develop' of github.com:wailsapp/wails into develop 2019-10-23 14:06:19 +11:00
Lea Anthony
08fc1d53d0 chore: version bump 2019-10-23 14:06:14 +11:00
Lea Anthony
6c5d5e40f4 262 add wails shutdown method (#264)
* feat: support close in bridge mode

* feat: WailsShutdown callback

Now handles proper shutdown through:
  * runtime.Window.Close()
  * Killing the main window
  * Ctrl-C
2019-10-23 14:04:41 +11:00
Lea Anthony
3f1dfe931c Update CONTRIBUTORS.md 2019-10-14 09:37:01 +11:00
Kris Raney
cb850c9653 Patch for file dialog on OSX (#258)
* Patch for file dialog on OSX
2019-10-14 09:34:50 +11:00
Lea Anthony
8d8f47363a chore: supress warnings 2019-10-12 20:19:05 +11:00
Lea Anthony
d399b7580d Develop (#255)
Hotfix
2019-10-09 19:34:24 +11:00
Lea Anthony
1b04b71254 fix: importing runtime (#254) 2019-10-09 19:33:13 +11:00
Lea Anthony
0b19ad1427 chore: version bump 2019-10-09 19:30:53 +11:00
Lea Anthony
9aca99911e Update CONTRIBUTORS.md 2019-10-09 04:47:00 +11:00
Robin Eklind
a7f61e335e cmd/wails: report error status code to OS (#252)
Fixes #251.
2019-10-09 04:44:32 +11:00
Lea Anthony
9fff0a513e chore: merge master 2019-10-08 06:30:22 +11:00
Lea Anthony
f453be12c8 hotfix: version number 2019-10-08 06:20:30 +11:00
Lea Anthony
20428b0407 Develop (#250) 2019-10-08 06:12:08 +11:00
Lea Anthony
7fd5b77cbe chore: version bump 2019-10-08 06:06:02 +11:00
Lea Anthony
d2cac50f93 fix: update runtime 2019-10-07 22:16:29 +11:00
Lea Anthony
00f1f82520 Merge branch 'master' into develop 2019-10-07 21:35:52 +11:00
Lea Anthony
02fbb14e34 chore: version bump 2019-10-07 21:32:27 +11:00
Byron
f961659ada manjaro (#248)
* manjaro

* Update go.mod
2019-10-06 19:14:05 +11:00
Lea Anthony
93942111bc fix: multiple runtime fixes 2019-10-06 14:20:59 +11:00
Lea Anthony
dc5a68acce feat: add runtime typings 2019-10-06 13:36:20 +11:00
Lea Anthony
12ff0f8c97 fix: binding more than one struct method (#245) 2019-10-01 06:13:55 +10:00
Lea Anthony
c375c281ee chore: remove some debug output 2019-09-19 08:57:40 +10:00
Lea Anthony
7d86b0f7c4 chore: version bump 2019-09-14 05:56:52 +10:00
Byron
990f7dd06c feat: KDE neon support (#234) 2019-09-14 05:51:29 +10:00
Lea Anthony
0b6f256d55 docs: add Mattn 2019-09-10 06:34:40 +10:00
Lea Anthony
694f80434a fix: revert fix
Masterminds have fixed it upstream
2019-09-05 08:50:40 +10:00
Byron
0a57fa4035 (FIX) 215 support distribution kali (#224)
* fix: kali support
2019-09-04 08:44:08 +10:00
Lea Anthony
099967ae94 hotfix: semver changed api 2019-09-03 21:52:45 +10:00
Lea Anthony
718bb1b852 patch 2019-09-03 21:46:47 +10:00
Lea Anthony
af1a1a2498 chore: remove debug output 2019-09-03 19:36:21 +10:00
Lea Anthony
cc0617d247 chore: release v0.17.14-pre 2019-09-03 18:22:27 +10:00
Byron
99a3f87cef duplicate code spotted (#221)
* fix: duplicate code
2019-09-03 18:21:13 +10:00
Lea Anthony
1ccdb1bc4e fix: allow 0 return types 2019-09-01 15:52:35 +10:00
Byron
62b1967e45 215 support distribution kali (#219)
* test: azure pipeline

* fix: azure pipeline yaml

* feat: kali linux support

* feat: kali linux support
2019-08-30 23:21:54 +10:00
Lea Anthony
c10303e7c0 fix: upgrade eslint 2019-08-28 07:01:52 +10:00
Byron
4a61584827 Elementary support + readme update (#210)
* test: azure pipeline

* fix: azure pipeline yaml

* feat: elementary support+readme udpate
2019-08-28 06:19:48 +10:00
Lea Anthony
cf249ba836 chore: bump version 2019-08-26 17:58:20 +10:00
Byron
08fe7b64d6 Yaml bug (#207)
* test: azure pipeline

* fix: azure pipeline yaml

* feat: elementary support

* feat: opensuse support

* fix: 0_setup.go line 27

* fix: 0_setup.go line 27

* fix: opensuse yaml

* fix: opensuse yaml

* fix: opensuse yaml

* fix: opensuse yaml

* fix: opensuse yaml

* fix: opensuse yaml

* fix: yaml

* fix: yaml

* fix: yaml

* fix: opensuse yaml

* fix: opensuse

* fix: opensuse

* fix: opensuse

* fix: opensuse

* fix: string trim os osRelease field NAME

* fix: 0_setup.go if err typo

* test

* test

* fix: typo in linux.go

* test: remove quotes from opensuse case

* test: revert

* test: ""

* test: ""

* test: ""

* test: ""

* fix: replace trim with replace

* fix: drop 0_setup.go and run checks by system.go

* fix: elementary os yaml name

* fix: open suse yaml entry

* fix: commented out result.Release = version

* fix: commented out result.Release = version

* fix: revert Replace to Trim

* fix: Linux Mint yaml entry

* fix: capitalize distros entries

* fix: capitalize distros entries

* fix: capitalize distros entries

* test

* test

* fix: open suse yaml entry

* fix: yaml entris

* test

* test

* test

* test

* test

* test

* branch changing

* fix: bug in setup process

* debugging

* debugging

* debugging

* debugging

* fix: yaml entries & err == nil bug

* Update prerequisites.go

* fix: bug

* fix: 0_setup.go

* fix: yaml bug

* fix: yaml bug
2019-08-26 05:02:49 +10:00
Lea Anthony
77939ea414 Merge pull request #200 from Vaelatern/voidlinux-support
feat: VoidLinux support
2019-08-26 04:23:16 +10:00
Toyam Cox
468394d03c feat: VoidLinux support 2019-08-25 08:41:51 -04:00
Lea Anthony
dc87699a1e Merge pull request #205 from Vaelatern/undefined-webview
fix: undefined: webview during build
2019-08-25 19:44:53 +10:00
Toyam Cox
36e906507d fix: undefined: webview during build 2019-08-25 04:08:21 -04:00
Lea Anthony
a5f9688708 Merge branch 'develop' of github.com:wailsapp/wails into develop 2019-08-25 15:59:02 +10:00
Lea Anthony
880f900e51 Update contributors 2019-08-25 15:58:44 +10:00
Lea Anthony
349306cf73 chore: deduplicate library verificaation code (#203) 2019-08-25 15:56:42 +10:00
Toyam Cox
f140697857 chore: deduplicate library verificaation code 2019-08-25 00:14:39 -04:00
Lea Anthony
3d9e9a1342 build: fix filename 2019-08-19 22:54:06 +10:00
Lea Anthony
1a82406d2b build: add azure-pipelines script 2019-08-19 22:53:05 +10:00
Lea Anthony
26ff8df7e5 chore: remove unused mewn file 2019-08-19 22:43:54 +10:00
Lea Anthony
54b4b157b3 Update README.md 2019-08-19 22:30:25 +10:00
Lea Anthony
add7e89097 Update README.md 2019-08-19 22:29:04 +10:00
Lea Anthony
b7dae216df Merge branch 'develop' of github.com:wailsapp/wails into develop 2019-08-19 22:23:32 +10:00
Byron
0b9d093d6c Azure Pipelines (#192)
* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* test: azure pipeline

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines

* feat: azure pipelines
2019-08-19 19:11:43 +10:00
Lea Anthony
7f282ad071 chore: add inprogress label 2019-08-19 09:01:44 +10:00
Lea Anthony
d6ed583e07 chore: add stale issue bot config 2019-08-19 08:56:58 +10:00
Lea Anthony
930cab2d9d chore: rename webview for clarity 2019-08-16 19:12:55 +10:00
Lea Anthony
ae41f33dcc chore: merge webview back in 2019-08-16 08:28:43 +10:00
Lea Anthony
5af6b7cafe chore: bump version 2019-08-10 10:50:25 +10:00
Lea Anthony
68ac3763d0 chore: update linux db 2019-08-10 10:43:09 +10:00
admin_3.exe
cdc66b556e 188 support distribution linux mint (#189) 2019-08-10 10:42:02 +10:00
admin_3.exe
3ff16322c2 182 linux bd (#183)
* fix: linuxdb.yaml entries

* fix: typo

* fix: yaml syntax error
2019-08-09 20:21:01 +10:00
Lea Anthony
d60066a0b1 chore: linter fixes (#185) 2019-08-09 08:12:50 +10:00
Lea Anthony
9d6ebf0fd4 Linux db (#182)
* feat: refactor linux distro detection

* linux db updates

* feat: add gccversioncommand + windows support

* fix: build-essential
2019-08-08 21:04:59 +10:00
Lea Anthony
839815e2fb chore: bump version 2019-08-05 18:40:34 +10:00
admin_3.exe
846fe479bf 179 parrot distributionsupport (#181)
* feat: parrot support && git push

* feat: parrot support

* feat: parrot support

* fix: arch support

* fix: gentoos support

* Update README.md

* Update README.md
2019-08-05 18:35:04 +10:00
Lea Anthony
62f7070e0c chore: hotfix version bump 2019-07-31 18:26:12 +10:00
Lea Anthony
dd418b36c2 Merge pull request #178 from bh90210/169-improve-wails-issue-linux-quick-fixes
169 improve wails issue linux quick fixes
2019-07-31 18:04:51 +10:00
bh90210
f4f04f2199 fix: typo 2019-07-31 03:20:08 +03:00
bh90210
3a7514bbdc fix: issue report 2019-07-31 02:54:59 +03:00
admin_3.exe
213f07fed4 Merge pull request #3 from wailsapp/develop
Develop
2019-07-31 01:48:54 +03:00
Lea Anthony
ed3ed8aa18 chore: hotfix 2019-07-31 06:57:56 +10:00
Lea Anthony
d038dca37c Merge pull request #177 from wailsapp/gitbash-fixes
fix: gitbash fixes
2019-07-31 06:55:40 +10:00
Lea Anthony
bb86d770a1 fix: gitbash fixes 2019-07-31 06:54:07 +10:00
Lea Anthony
33daa8621e chore: version bump 2019-07-31 06:21:00 +10:00
Lea Anthony
6c124fcff4 Merge pull request #175 from bh90210/174-wails-init-does-not-work-on-centos-7
fixed centos & fedora support
2019-07-31 06:17:42 +10:00
Lea Anthony
45833574b3 Merge branch 'bh90210-169-improve-wails-issue' into develop 2019-07-31 06:09:12 +10:00
Lea Anthony
5cb00eb481 trim gcc string 2019-07-31 06:08:43 +10:00
Lea Anthony
e2105331c1 Merge branch '169-improve-wails-issue' of git://github.com/bh90210/wails into bh90210-169-improve-wails-issue 2019-07-31 06:02:41 +10:00
bh90210
21d2383e63 Merge branch '169-improve-wails-issue' of github.com:bh90210/wails into 169-improve-wails-issue 2019-07-30 16:11:51 +03:00
bh90210
6c945a4eed fix: crashing typo 2019-07-30 16:11:18 +03:00
admin_3.exe
6c8d34dfd3 Delete npm-debug.log 2019-07-30 16:00:16 +03:00
bh90210
b9b42c059e feat: show gcc, node, npm version on issue report 2019-07-30 15:57:51 +03:00
bh90210
3f657b34cf darwin working 2019-07-30 02:12:51 +03:00
bh90210
6e81a36ada linux working 2019-07-30 02:02:01 +03:00
bh90210
ddec01a429 feat: gcc,npm,node versions on issues 2019-07-29 23:53:43 +03:00
bh90210
21fdb3be7d fix(linux): fedora & centos support 2019-07-29 19:12:05 +03:00
admin_3.exe
c1a13ab6d0 Merge pull request #2 from wailsapp/develop
Develop
2019-07-29 16:09:33 +03:00
Lea Anthony
750a02efc6 chore: version bump 2019-07-29 17:54:41 +10:00
Lea Anthony
5e047debfc fix: add support back for ubuntu, redhat 2019-07-29 17:52:22 +10:00
Lea Anthony
9cac336708 docs: updated contributors 2019-07-29 09:03:17 +10:00
Lea Anthony
827c2b9a95 Merge pull request #172 from Chronophylos/dont-use-lsb_release
Drop lsb_release
2019-07-29 09:01:41 +10:00
Chronophylos
b36a3c4abb fix assumption
Signed-off-by: Chronophylos <nikolai@chronophylos.com>
2019-07-28 13:39:03 +02:00
Chronophylos
0bac205565 show distro id when creating a issue
Signed-off-by: Chronophylos <nikolai@chronophylos.com>
2019-07-28 13:16:56 +02:00
Chronophylos
67a8ad8e12 change DistributionID to NAME=
Signed-off-by: Chronophylos <nikolai@chronophylos.com>
2019-07-28 00:44:46 +02:00
Chronophylos
9de2f66f50 drop lsb_release
Signed-off-by: Chronophylos <nikolai@chronophylos.com>
2019-07-27 21:26:11 +02:00
admin_3.exe
9370030ff3 Merge pull request #1 from wailsapp/develop
Develop
2019-07-27 22:12:15 +03:00
Lea Anthony
e38d6e7ef0 Merge pull request #165 from bt/master
Fix Masterminds/semver reference import path (case-sensitivity)
2019-07-22 18:33:54 +10:00
Bertram Truong
2f21fc3575 Fix Masterminds/semver reference import path (case-sensitivity) 2019-07-22 10:25:23 +10:00
Lea Anthony
63e73f5f64 chore: Version Bump 2019-07-20 19:40:43 +10:00
Lea Anthony
e1adc1ba49 Merge pull request #163 from wailsapp/more-runtime-refactor
More runtime refactor
2019-07-20 19:35:59 +10:00
Lea Anthony
030e911ea4 feat: significant overhaul of runtime 2019-07-20 19:32:30 +10:00
Lea Anthony
d2f114e44e Update contributors 2019-07-19 08:08:26 +10:00
Lea Anthony
700d3f84d3 Merge pull request #161 from fdidron/145-Fix-React-Error-Windows
145 fix react error windows
2019-07-19 00:32:20 +10:00
Florian Didron
1d49042013 style: replace double quotes with single quotes 2019-07-18 21:46:25 +09:00
Florian Didron
8671b1e6cf fix: add promises polyfill for the windows target 2019-07-18 21:10:42 +09:00
Lea Anthony
517d6c44ec fix: Print stdout on error :rolleyes: 2019-07-18 09:12:57 +10:00
Lea Anthony
a082a659ea fix: renamed global logger 2019-07-16 18:29:15 +10:00
Lea Anthony
083153efc9 fix: eslintrc config 2019-07-13 22:32:19 +10:00
Lea Anthony
65a2560153 chore: bump version 2019-07-13 22:30:32 +10:00
Lea Anthony
29535c10a3 Merge pull request #157 from wailsapp/runtime-refactor
Runtime refactor
2019-07-13 22:27:36 +10:00
Lea Anthony
24c7362163 Even more fixes 2019-07-13 22:18:42 +10:00
Lea Anthony
f6ff7d7b16 fix: linting for hound 2019-07-13 22:12:49 +10:00
Lea Anthony
b0a075cdf2 fix: more lint fixes 2019-07-13 22:10:43 +10:00
Lea Anthony
98d4d6b33c config: eslint 2019-07-13 15:42:57 +10:00
Lea Anthony
9ba3e0512b fix: linting 2019-07-13 15:35:50 +10:00
Lea Anthony
eff63175e5 fix: lint 2019-07-13 15:34:00 +10:00
Lea Anthony
75a0b632bc fix: capitalisation and binding fix 2019-07-13 15:30:52 +10:00
Lea Anthony
a2af626477 fix: config errors 2019-07-12 10:22:19 +10:00
Lea Anthony
8aa97f64ef feat: major refactor 2019-07-12 10:12:15 +10:00
Lea Anthony
caa1e04b5a Update README.md 2019-07-08 21:29:59 +10:00
Lea Anthony
cddf6a0204 chore: version bump 2019-07-07 10:28:58 +10:00
Lea Anthony
9fa1f42dc7 Merge pull request #155 from wailsapp/154-Support-Distribution-'Fedora'
feat: test support for Fedora
2019-07-07 10:28:19 +10:00
Lea Anthony
c7e709d487 feat: test support for Fedora 2019-07-07 10:25:59 +10:00
Lea Anthony
6801398f3d docs: update contributors 2019-07-07 10:17:44 +10:00
Lea Anthony
982d14c049 Merge pull request #152 from fallendusk/master
Add support for Gentoo
2019-07-07 10:16:21 +10:00
fallendusk
ddbaf55ae7 Merge branch 'develop' into master 2019-07-06 17:31:21 -04:00
Greg Helton
5552a8501b Add support for Gentoo 2019-07-06 01:43:02 -04:00
Lea Anthony
b997becb2f chore: bump version 2019-07-03 19:47:03 +10:00
Lea Anthony
753516bab7 Merge pull request #149 from wailsapp/148-Support-Distribution-'Zorin'
148 support distribution 'zorin'
2019-07-03 19:46:20 +10:00
139 changed files with 14495 additions and 1592 deletions

View File

@@ -1,42 +0,0 @@
{{ if .Versions -}}
<a name="unreleased"></a>
## [Unreleased]
{{ if .Unreleased.CommitGroups -}}
{{ range .Unreleased.CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{ range .Versions }}
<a name="{{ .Tag.Name }}"></a>
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
{{ range .CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
{{ end }}
{{ end -}}
{{- if .NoteGroups -}}
{{ range .NoteGroups -}}
### {{ .Title }}
{{ range .Notes }}
{{ .Body }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{- if .Versions }}
[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
{{ range .Versions -}}
{{ if .Tag.Previous -}}
[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
{{ end -}}
{{ end -}}
{{ end -}}

View File

@@ -1,27 +0,0 @@
style: github
template: CHANGELOG.tpl.md
info:
title: CHANGELOG
repository_url: https://github.com/wailsapp/wails
options:
commits:
# filters:
# Type:
# - feat
# - fix
# - perf
# - refactor
commit_groups:
# title_maps:
# feat: Features
# fix: Bug Fixes
# perf: Performance Improvements
# refactor: Code Refactoring
header:
pattern: "^(\\w*)\\:\\s(.*)$"
pattern_maps:
- Type
- Subject
notes:
keywords:
- BREAKING CHANGE

1
.eslintignore Normal file
View File

@@ -0,0 +1 @@
runtime/assets/default.html

View File

@@ -1,7 +1,8 @@
module.exports = {
{
"env": {
"browser": true,
"es6": true
"es6": true,
"node": true,
},
"extends": "eslint:recommended",
"parserOptions": {
@@ -26,4 +27,4 @@ module.exports = {
"always"
]
}
};
}

19
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 30
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- onhold
- inprogress
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

2
.gitignore vendored
View File

@@ -16,4 +16,4 @@ examples/**/example*
cmd/wails/wails
.DS_Store
tmp
dist
node_modules/

6
.hound.yml Normal file
View File

@@ -0,0 +1,6 @@
jshint:
config_file: .jshintrc
eslint:
enabled: true
config_file: .eslintrc
ignore_file: .eslintignore

7
.vscode/launch.json vendored
View File

@@ -4,6 +4,13 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Test cmd package",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${workspaceFolder}/cmd/"
},
{
"name": "Wails Init",
"type": "go",

View File

@@ -1,3 +1,9 @@
2019-07-20 **v0.17.6-pre**
* Significant refactor of runtime
* Removed wailsbridge file - now a unified approach taken
* Fixed React on Windows - Thanks [Florian Didran](https://github.com/fdidron)!
2019-06-18 **v0.16.0**
* React template FTW! - Thanks [admin_3.exe](https://github.com/bh90210)!
* Updated contributors

View File

@@ -2,14 +2,22 @@
Wails is what it is because of the time and effort given by these great people. A huge thank you to each and every one!
* [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot)
* [Qais Patankar](https://github.com/qaisjp)
* [Anthony Lee](https://github.com/alee792)
* [Adrian Lanzafame](https://github.com/lanzafame)
* [0xflotus](https://github.com/0xflotus)
* [Michael D Henderson](https://github.com/mdhender)
* [fred2104](https://github.com/fishfishfish2104)
* [intelwalk](https://github.com/intelwalk)
* [Mark Stenglein](https://github.com/ocelotsloth)
* [admin_3.exe](https://github.com/bh90210)
* [iceleo-com](https://github.com/iceleo-com)
* [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot)
* [Qais Patankar](https://github.com/qaisjp)
* [Anthony Lee](https://github.com/alee792)
* [Adrian Lanzafame](https://github.com/lanzafame)
* [Mattn](https://github.com/mattn)
* [0xflotus](https://github.com/0xflotus)
* [Michael D Henderson](https://github.com/mdhender)
* [fred2104](https://github.com/fishfishfish2104)
* [intelwalk](https://github.com/intelwalk)
* [Mark Stenglein](https://github.com/ocelotsloth)
* [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)

View File

@@ -1,5 +1,5 @@
<p align="center" style="text-align: center">
<img src="https://github.com/wailsapp/docs/raw/master/.vuepress/public/media/logo_cropped.png" width="40%"><br/>
<img src="logo_cropped.png" width="40%"><br/>
</p>
<p align="center">
A framework for building desktop applications using Go & Web Technologies.<br/><br/>
@@ -8,13 +8,16 @@
<a href="http://godoc.org/github.com/wailsapp/wails"><img src="https://img.shields.io/badge/godoc-reference-blue.svg"/></a>
<a href="https://www.codefactor.io/repository/github/wailsapp/wails"><img src="https://www.codefactor.io/repository/github/wailsapp/wails/badge" alt="CodeFactor" /></a>
<a href="https://github.com/wailsapp/wails/issues"><img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="CodeFactor" /></a>
<a href="https://app.fossa.io/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_shield" alt="FOSSA Status"><img src="https://app.fossa.io/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=shield"/></a>
<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://dashboard.guardrails.io/default/gh/wailsapp/wails"><img src="https://badges.guardrails.io/wailsapp/wails.svg?token=53657bc22ec360d7673c894fdd70568e918ec581d10d84427ed4de5fe1eeff1a"></a>
<a href="https://dev.azure.com/leaanthony/Wails/_build/latest?definitionId=1&branchName=master" rel="nofollow"><img src="https://dev.azure.com/leaanthony/Wails/_apis/build/status/wailsapp.wails?branchName=master" alt="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!
The official docs can be found at [https://wails.app](https://wails.app).
## Features
- Use standard Go libraries/frameworks for the backend
@@ -27,9 +30,6 @@ The traditional method of providing web interfaces to Go programs is via a built
- Powerful cli tool
- Multiplatform
## Project Status
Wails is currently in Beta. Please make sure you read the [Project Status](https://wails.app/project_status.html) if you are interested in using this project.
## Installation
@@ -46,19 +46,41 @@ Make sure you have the xcode command line tools installed. This can be done by r
### Linux
#### Ubuntu 18.04, Debian 9
#### Debian/Ubuntu
`sudo apt install pkg-config build-essential libgtk-3-dev libwebkit2gtk-4.0-dev`
`sudo apt install libgtk-3-dev libwebkit2gtk-4.0-dev`
_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_
#### Arch Linux
`sudo pacman -S webkit2gtk gtk3`
#### Red Hat Based Distros
_Also succesfully test on: Manjaro & ArcoLinux_
`sudo yum install webkit2gtk-devel gtk3-devel`
#### Centos
Note: If you have successfully installed these dependencies on a different flavour of Linux, please consider submitting a PR.
`sudo yum install webkitgtk3-devel gtk3-devel`
_CentOS 6, 7_
#### Fedora
`sudo yum install webkit2gtk3-devel gtk3-devel`
_Fedora 29, 30_
#### VoidLinux & VoidLinux-musl
`xbps-install gtk+3-devel webkit2gtk-devel`
#### Gentoo
`sudo emerge gtk+:3 webkit-gtk`
### Windows
@@ -103,8 +125,7 @@ And without [these people](CONTRIBUTORS.md), it wouldn't be what it is today. A
Special Mentions:
* [Bill Kennedy](https://twitter.com/goinggodotnet) - Go guru, encourager and all-round nice guy, whose infectious energy and inspiration powered me on when I had none left.
* [Mark Bates](https://github.com/markbates) - Creator of [Packr](https://github.com/gobuffalo/packr), inspiration for packing strategies which fed into some of the tooling.
* [Byron](https://github.com/bh90210) - At times, Byron has single handedly kept this project alive. Without his incredible input, we never would have got to v1.
This project was mainly coded to the following albums:
@@ -122,3 +143,6 @@ This project was mainly coded to the following albums:
* [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
* [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)
## Licensing
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)

102
app.go
View File

@@ -1,7 +1,18 @@
package wails
import (
"os"
"runtime"
"syscall"
"github.com/syossan27/tebata"
"github.com/wailsapp/wails/cmd"
"github.com/wailsapp/wails/lib/binding"
"github.com/wailsapp/wails/lib/event"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/ipc"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/renderer"
)
// -------------------------------- Compile time Flags ------------------------------
@@ -13,15 +24,15 @@ var BuildMode = cmd.BuildModeProd
// App defines the main application struct
type App struct {
config *AppConfig // The Application configuration object
cli *cmd.Cli // In debug mode, we have a cli
renderer Renderer // The renderer is what we will render the app to
logLevel string // The log level of the app
ipc *ipcManager // Handles the IPC calls
log *CustomLogger // Logger
bindingManager *bindingManager // Handles binding of Go code to renderer
eventManager *eventManager // Handles all the events
runtime *Runtime // The runtime object for registered structs
config *AppConfig // The Application configuration object
cli *cmd.Cli // In debug mode, we have a cli
renderer interfaces.Renderer // The renderer is what we will render the app to
logLevel string // The log level of the app
ipc interfaces.IPCManager // Handles the IPC calls
log *logger.CustomLogger // Logger
bindingManager interfaces.BindingManager // Handles binding of Go code to renderer
eventManager interfaces.EventManager // Handles all the events
runtime interfaces.Runtime // The runtime object for registered structs
}
// CreateApp creates the application window with the given configuration
@@ -33,15 +44,15 @@ func CreateApp(optionalConfig ...*AppConfig) *App {
}
result := &App{
logLevel: "info",
renderer: &webViewRenderer{},
ipc: newIPCManager(),
bindingManager: newBindingManager(),
eventManager: newEventManager(),
log: newCustomLogger("App"),
logLevel: "debug",
renderer: renderer.NewWebView(),
ipc: ipc.NewManager(),
bindingManager: binding.NewManager(),
eventManager: event.NewManager(),
log: logger.NewCustomLogger("App"),
}
appconfig, err := newAppConfig(userConfig)
appconfig, err := newConfig(userConfig)
if err != nil {
result.log.Fatalf("Cannot use custom HTML: %s", err.Error())
}
@@ -55,11 +66,15 @@ func CreateApp(optionalConfig ...*AppConfig) *App {
result.config.DisableInspector = true
}
// Platform specific init
platformInit()
return result
}
// Run the app
func (a *App) Run() error {
if BuildMode != cmd.BuildModeProd {
return a.cli.Run()
}
@@ -75,14 +90,14 @@ func (a *App) Run() error {
func (a *App) start() error {
// Set the log level
setLogLevel(a.logLevel)
logger.SetLogLevel(a.logLevel)
// Log starup
a.log.Info("Starting")
// Check if we are to run in headless mode
// Check if we are to run in bridge mode
if BuildMode == cmd.BuildModeBridge {
a.renderer = &Headless{}
a.renderer = &renderer.Bridge{}
}
// Initialise the renderer
@@ -91,27 +106,64 @@ func (a *App) start() error {
return err
}
// Enable console for Windows debug builds
if runtime.GOOS == "windows" && BuildMode == cmd.BuildModeDebug {
a.renderer.EnableConsole()
}
// Start signal handler
t := tebata.New(os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
t.Reserve(func() {
a.log.Debug("SIGNAL CAUGHT! Starting Shutdown")
a.renderer.Close()
})
// Start event manager and give it our renderer
a.eventManager.start(a.renderer)
a.eventManager.Start(a.renderer)
// Start the IPC Manager and give it the event manager and binding manager
a.ipc.start(a.eventManager, a.bindingManager)
a.ipc.Start(a.eventManager, a.bindingManager)
// Create the runtime
a.runtime = newRuntime(a.eventManager, a.renderer)
a.runtime = NewRuntime(a.eventManager, a.renderer)
// Start binding manager and give it our renderer
err = a.bindingManager.start(a.renderer, a.runtime)
err = a.bindingManager.Start(a.renderer, a.runtime)
if err != nil {
return err
}
// Defer the shutdown
defer a.shutdown()
// Run the renderer
return a.renderer.Run()
err = a.renderer.Run()
if err != nil {
return err
}
return nil
}
// shutdown the app
func (a *App) shutdown() {
// Make sure this is only called once
a.log.Debug("Shutting down")
// Shutdown Binding Manager
a.bindingManager.Shutdown()
// Shutdown IPC Manager
a.ipc.Shutdown()
// Shutdown Event Manager
a.eventManager.Shutdown()
a.log.Debug("Cleanly Shutdown")
}
// Bind allows the user to bind the given object
// with the application
func (a *App) Bind(object interface{}) {
a.bindingManager.bind(object)
a.bindingManager.Bind(object)
}

View File

@@ -1,97 +0,0 @@
package wails
import (
"strings"
"github.com/dchest/htmlmin"
"github.com/leaanthony/mewn"
)
// AppConfig is the configuration structure used when creating a Wails App object
type AppConfig struct {
Width, Height int
Title string
defaultHTML string
HTML string
JS string
CSS string
Colour string
Resizable bool
DisableInspector bool
isHTMLFragment bool
}
func (a *AppConfig) merge(in *AppConfig) error {
if in.CSS != "" {
a.CSS = in.CSS
}
if in.Title != "" {
a.Title = in.Title
}
if in.HTML != "" {
minified, err := htmlmin.Minify([]byte(in.HTML), &htmlmin.Options{
MinifyScripts: true,
})
if err != nil {
return err
}
inlineHTML := string(minified)
inlineHTML = strings.Replace(inlineHTML, "'", "\\'", -1)
inlineHTML = strings.Replace(inlineHTML, "\n", " ", -1)
a.HTML = strings.TrimSpace(inlineHTML)
// Deduce whether this is a full html page or a fragment
// The document is determined to be a fragment if an HTML
// tag exists and is located before the first div tag
HTMLTagIndex := strings.Index(a.HTML, "<html")
DivTagIndex := strings.Index(a.HTML, "<div")
if HTMLTagIndex == -1 {
a.isHTMLFragment = true
} else {
if DivTagIndex < HTMLTagIndex {
a.isHTMLFragment = true
}
}
}
if in.Colour != "" {
a.Colour = in.Colour
}
if in.JS != "" {
a.JS = in.JS
}
if in.Width != 0 {
a.Width = in.Width
}
if in.Height != 0 {
a.Height = in.Height
}
a.Resizable = in.Resizable
a.DisableInspector = in.DisableInspector
return nil
}
// Creates the default configuration
func newAppConfig(userConfig *AppConfig) (*AppConfig, error) {
result := &AppConfig{
Width: 800,
Height: 600,
Resizable: true,
Title: "My Wails App",
Colour: "#FFF", // White by default
HTML: mewn.String("./wailsruntimeassets/default/default.html"),
}
if userConfig != nil {
err := result.merge(userConfig)
if err != nil {
return nil, err
}
}
return result, nil
}

7
app_other.go Normal file
View File

@@ -0,0 +1,7 @@
// +build +linux +darwin !windows
package wails
func platformInit() {
}

27
app_windows.go Normal file
View File

@@ -0,0 +1,27 @@
// +build windows !linux !darwin
package wails
import (
"fmt"
"log"
"syscall"
)
func platformInit() {
err := SetProcessDPIAware()
if err != nil {
log.Fatalf(err.Error())
}
}
// SetProcessDPIAware via user32.dll
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiaware
// Also, thanks Jack Mordaunt! https://github.com/wailsapp/wails/issues/293
func SetProcessDPIAware() error {
status, r, err := syscall.NewLazyDLL("user32.dll").NewProc("SetProcessDPIAware").Call()
if status == 0 {
return fmt.Errorf("exit status %d: %v %v", status, r, err)
}
return nil
}

138
azure-pipelines.yaml Normal file
View File

@@ -0,0 +1,138 @@
# avoid double trigger by applying some rules
# start a pipeline when push to 'master' branch
trigger:
- master
# or when pull request on 'develop' branch
pr:
- develop
# for now there is only one stage 'Build'
# in the future we could use multistage strategy for releases
stages:
- stage: Build
# there are 3 jobs
# one for each os
jobs:
- deployment: Linux
displayName: Lin
variables:
GOPATH: '$(Agent.BuildDirectory)/gopath' # Go workspace path
GOROOT: '$(Agent.BuildDirectory)/go' # Go installation path
GOBIN: '$(GOPATH)/bin' # Go binaries path
GOMODULE: 'on'
modulePath: '$(Agent.BuildDirectory)/wails' # Path to the module's code
pool:
vmImage: 'Ubuntu-16.04'
environment: 'linux-dev'
strategy:
runOnce:
deploy:
steps:
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
clean: true # whether to fetch clean each time
path: wails # path to check out source code, relative to the agent's build directory (e.g. \_work\1)
# go version 1.12.7
- script: |
wget "https://storage.googleapis.com/golang/go1.12.7.linux-amd64.tar.gz" --output-document "$(Agent.BuildDirectory)/go1.12.7.tar.gz"
tar -C '$(Agent.BuildDirectory)' -xzf "$(Agent.BuildDirectory)/go1.12.7.tar.gz"
displayName: 'Install Go 1.12.7 Linux'
- script: |
mkdir -p '$(GOBIN)'
mkdir -p '$(GOPATH)/pkg'
mkdir -p '$(GOROOT)'
shopt -s extglob
shopt -s dotglob
echo '##vso[task.prependpath]$(GOBIN)'
echo '##vso[task.prependpath]$(GOROOT)/bin'
displayName: 'Set up the Go workspace'
- script: |
go version
go get -v -d ./...
cd cmd/wails
go install
workingDirectory: '$(modulePath)'
displayName: 'Get dependencies, then build'
- script: |
wails version
workingDirectory: '$(modulePath)'
displayName: 'Check we have output'
- deployment: Mac
displayName: Mac
variables:
GOPATH: '$(Agent.BuildDirectory)/gopath' # Go workspace path
GOROOT: '$(Agent.BuildDirectory)/go' # Go installation path
GOBIN: '$(GOPATH)/bin' # Go binaries path
GOMODULE: 'on'
modulePath: '$(Agent.BuildDirectory)/wails' # Path to the module's code
pool:
vmImage: 'macOS-10.14'
environment: 'mac-dev'
strategy:
runOnce:
deploy:
steps:
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
clean: true # whether to fetch clean each time
path: wails # path to check out source code, relative to the agent's build directory (e.g. \_work\1)
# go version 1.12.7
- script: |
wget "https://storage.googleapis.com/golang/go1.12.7.darwin-amd64.tar.gz" --output-document "$(Agent.BuildDirectory)/go1.12.7.tar.gz"
tar -C '$(Agent.BuildDirectory)' -xzf "$(Agent.BuildDirectory)/go1.12.7.tar.gz"
displayName: 'Install Go 1.12.7 Linux'
- script: |
mkdir -p '$(GOBIN)'
mkdir -p '$(GOPATH)/pkg'
mkdir -p '$(GOROOT)'
shopt -s extglob
shopt -s dotglob
echo '##vso[task.prependpath]$(GOBIN)'
echo '##vso[task.prependpath]$(GOROOT)/bin'
displayName: 'Set up the Go workspace'
- script: |
go version
go get -v -d ./...
cd cmd/wails
go install
workingDirectory: '$(modulePath)'
displayName: 'Get dependencies, then build'
- script: |
wails version
workingDirectory: '$(modulePath)'
displayName: 'Check we have output'
- deployment: Win
displayName: Win
variables:
GOMODULE: 'on'
modulePath: '$(Agent.BuildDirectory)/wails' # Path to the module's code
pool:
vmImage: 'windows-2019'
environment: 'win-dev'
strategy:
runOnce:
deploy:
steps:
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
clean: true # whether to fetch clean each time
path: wails # path to check out source code, relative to the agent's build directory (e.g. \_work\1)
# Go tool installer
# Find in cache or download a specific version of Go and add it to the PATH
- task: GoTool@0
inputs:
version: '1.12.7'
goPath: '$(Agent.BuildDirectory)/go'
goBin: '$(Agent.BuildDirectory)/go/bin'
displayName: 'Set up the Go workspace'
- script: |
go version
go get -v -d ./...
cd cmd/wails
go install
workingDirectory: '$(modulePath)'
displayName: 'Get dependencies, then build'
- script: |
wails version
workingDirectory: '$(Agent.BuildDirectory)/go/bin'
displayName: 'Check we have output'

138
azure-pipelines.yml Normal file
View File

@@ -0,0 +1,138 @@
# avoid double trigger by applying some rules
# start a pipeline when push to 'master' branch
trigger:
- master
# or when pull request on 'develop' branch
pr:
- develop
# for now there is only one stage 'Build'
# in the future we could use multistage strategy for releases
stages:
- stage: Build
# there are 3 jobs
# one for each os
jobs:
- deployment: Linux
displayName: Lin
variables:
GOPATH: '$(Agent.BuildDirectory)/gopath' # Go workspace path
GOROOT: '$(Agent.BuildDirectory)/go' # Go installation path
GOBIN: '$(GOPATH)/bin' # Go binaries path
GOMODULE: 'on'
modulePath: '$(Agent.BuildDirectory)/wails' # Path to the module's code
pool:
vmImage: 'Ubuntu-16.04'
environment: 'linux-dev'
strategy:
runOnce:
deploy:
steps:
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
clean: true # whether to fetch clean each time
path: wails # path to check out source code, relative to the agent's build directory (e.g. \_work\1)
# go version 1.12.7
- script: |
wget "https://storage.googleapis.com/golang/go1.12.7.linux-amd64.tar.gz" --output-document "$(Agent.BuildDirectory)/go1.12.7.tar.gz"
tar -C '$(Agent.BuildDirectory)' -xzf "$(Agent.BuildDirectory)/go1.12.7.tar.gz"
displayName: 'Install Go 1.12.7 Linux'
- script: |
mkdir -p '$(GOBIN)'
mkdir -p '$(GOPATH)/pkg'
mkdir -p '$(GOROOT)'
shopt -s extglob
shopt -s dotglob
echo '##vso[task.prependpath]$(GOBIN)'
echo '##vso[task.prependpath]$(GOROOT)/bin'
displayName: 'Set up the Go workspace'
- script: |
go version
go get -v -d ./...
cd cmd/wails
go install
workingDirectory: '$(modulePath)'
displayName: 'Get dependencies, then build'
- script: |
wails version
workingDirectory: '$(modulePath)'
displayName: 'Check we have output'
- deployment: Mac
displayName: Mac
variables:
GOPATH: '$(Agent.BuildDirectory)/gopath' # Go workspace path
GOROOT: '$(Agent.BuildDirectory)/go' # Go installation path
GOBIN: '$(GOPATH)/bin' # Go binaries path
GOMODULE: 'on'
modulePath: '$(Agent.BuildDirectory)/wails' # Path to the module's code
pool:
vmImage: 'macOS-10.14'
environment: 'mac-dev'
strategy:
runOnce:
deploy:
steps:
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
clean: true # whether to fetch clean each time
path: wails # path to check out source code, relative to the agent's build directory (e.g. \_work\1)
# go version 1.12.7
- script: |
wget "https://storage.googleapis.com/golang/go1.12.7.darwin-amd64.tar.gz" --output-document "$(Agent.BuildDirectory)/go1.12.7.tar.gz"
tar -C '$(Agent.BuildDirectory)' -xzf "$(Agent.BuildDirectory)/go1.12.7.tar.gz"
displayName: 'Install Go 1.12.7 Linux'
- script: |
mkdir -p '$(GOBIN)'
mkdir -p '$(GOPATH)/pkg'
mkdir -p '$(GOROOT)'
shopt -s extglob
shopt -s dotglob
echo '##vso[task.prependpath]$(GOBIN)'
echo '##vso[task.prependpath]$(GOROOT)/bin'
displayName: 'Set up the Go workspace'
- script: |
go version
go get -v -d ./...
cd cmd/wails
go install
workingDirectory: '$(modulePath)'
displayName: 'Get dependencies, then build'
- script: |
wails version
workingDirectory: '$(modulePath)'
displayName: 'Check we have output'
- deployment: Win
displayName: Win
variables:
GOMODULE: 'on'
modulePath: '$(Agent.BuildDirectory)/wails' # Path to the module's code
pool:
vmImage: 'windows-2019'
environment: 'win-dev'
strategy:
runOnce:
deploy:
steps:
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
clean: true # whether to fetch clean each time
path: wails # path to check out source code, relative to the agent's build directory (e.g. \_work\1)
# Go tool installer
# Find in cache or download a specific version of Go and add it to the PATH
- task: GoTool@0
inputs:
version: '1.12.7'
goPath: '$(Agent.BuildDirectory)/go'
goBin: '$(Agent.BuildDirectory)/go/bin'
displayName: 'Set up the Go workspace'
- script: |
go version
go get -v -d ./...
cd cmd/wails
go install
workingDirectory: '$(modulePath)'
displayName: 'Get dependencies, then build'
- script: |
wails version
workingDirectory: '$(Agent.BuildDirectory)/go/bin'
displayName: 'Check we have output'

View File

@@ -11,10 +11,9 @@ func (app *App) setupCli() *cmd.Cli {
result := cmd.NewCli(app.config.Title, "Debug build")
result.Version(cmd.Version)
// Setup cli to handle loglevel and headless flags
// Setup cli to handle loglevel
result.
StringFlag("loglevel", "Sets the log level [debug|info|error|panic|fatal]. Default debug", &app.logLevel).
// BoolFlag("headless", "Runs the app in headless mode", &app.headless).
Action(app.start)
// Banner

File diff suppressed because one or more lines are too long

View File

@@ -18,7 +18,7 @@ func NewGitHubHelper() *GitHubHelper {
}
// GetVersionTags gets the list of tags on the Wails repo
// It retuns a list of sorted tags in descending order
// It returns a list of sorted tags in descending order
func (g *GitHubHelper) GetVersionTags() ([]*SemanticVersion, error) {
result := []*SemanticVersion{}

View File

@@ -7,9 +7,10 @@ import (
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
mewn "github.com/leaanthony/mewn"
"github.com/leaanthony/mewn"
"github.com/leaanthony/slicer"
"github.com/leaanthony/spinner"
)
@@ -87,6 +88,17 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
buildCommand.Add("build")
if binaryName != "" {
// Alter binary name based on OS
switch runtime.GOOS {
case "windows":
if !strings.HasSuffix(binaryName, ".exe") {
binaryName += ".exe"
}
default:
if strings.HasSuffix(binaryName, ".exe") {
binaryName = strings.TrimSuffix(binaryName, ".exe")
}
}
buildCommand.Add("-o")
buildCommand.Add(binaryName)
}
@@ -103,7 +115,7 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
}
// Add windows flags
if runtime.GOOS == "windows" {
if runtime.GOOS == "windows" && buildMode == BuildModeProd {
ldflags += "-H windowsgui "
}
@@ -219,6 +231,15 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
const md5sumFile = "package.json.md5"
// If node_modules does not exist, force a rebuild.
nodeModulesPath, err := filepath.Abs(filepath.Join(".", "node_modules"))
if err != nil {
return err
}
if !fs.DirExists(nodeModulesPath) {
forceRebuild = true
}
// If we aren't forcing the install and the md5sum file exists
if !forceRebuild && fs.FileExists(md5sumFile) {
// Yes - read contents
@@ -249,8 +270,8 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
ioutil.WriteFile(md5sumFile, []byte(packageJSONMD5), 0644)
}
// Install the bridge library
err = InstallBridge(caller, projectDir, projectOptions)
// Install the runtime
err = InstallRuntime(caller, projectDir, projectOptions)
if err != nil {
return err
}
@@ -263,22 +284,29 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
return nil
}
// InstallBridge installs the relevant bridge javascript library
func InstallBridge(caller string, projectDir string, projectOptions *ProjectOptions) error {
bridgeFile := "wailsbridge.prod.js"
if caller == "serve" {
bridgeFile = "wailsbridge.js"
// InstallRuntime installs the correct runtime for the type of build
func InstallRuntime(caller string, projectDir string, projectOptions *ProjectOptions) error {
if caller == "build" {
return InstallProdRuntime(projectDir, projectOptions)
}
// Copy bridge to project
bridgeAssets := mewn.Group("../wailsruntimeassets/bridge/")
bridgeFileData := bridgeAssets.Bytes(bridgeFile)
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, projectOptions.FrontEnd.Bridge, "wailsbridge.js")
err := fs.CreateFile(bridgeFileTarget, bridgeFileData)
if err != nil {
return err
}
return nil
return InstallBridge(projectDir, projectOptions)
}
// InstallBridge installs the relevant bridge javascript library
func InstallBridge(projectDir string, projectOptions *ProjectOptions) error {
bridgeFileData := mewn.String("../runtime/assets/bridge.js")
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, "node_modules", "@wailsapp", "runtime", "init.js")
err := fs.CreateFile(bridgeFileTarget, []byte(bridgeFileData))
return err
}
// InstallProdRuntime installs the production runtime
func InstallProdRuntime(projectDir string, projectOptions *ProjectOptions) error {
prodInit := mewn.String("../runtime/js/runtime/init.js")
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, "node_modules", "@wailsapp", "runtime", "init.js")
err := fs.CreateFile(bridgeFileTarget, []byte(prodInit))
return err
}
// ServeProject attempts to serve up the current project so that it may be connected to

View File

@@ -17,114 +17,144 @@ type LinuxDistribution int
const (
// Unknown is the catch-all distro
Unknown LinuxDistribution = iota
// Debian distribution
Debian
// Ubuntu distribution
Ubuntu
// Arch linux distribution
Arch
// RedHat linux distribution
RedHat
// Debian distribution
Debian
// CentOS linux distribution
CentOS
// Fedora linux distribution
Fedora
// Gentoo distribution
Gentoo
// Zorin distribution
Zorin
// Parrot distribution
Parrot
// Linuxmint distribution
Linuxmint
// VoidLinux distribution
VoidLinux
// Elementary distribution
Elementary
// Kali distribution
Kali
// Neon distribution
Neon
// Manjaro distribution
Manjaro
)
// DistroInfo contains all the information relating to a linux distribution
type DistroInfo struct {
Distribution LinuxDistribution
Description string
Release string
Codename string
DistributorID string
DiscoveredBy string
Distribution LinuxDistribution
Name string
ID string
Description string
Release string
}
// GetLinuxDistroInfo returns information about the running linux distribution
func GetLinuxDistroInfo() *DistroInfo {
result := &DistroInfo{Distribution: Unknown}
program := NewProgramHelper()
// Does lsb_release exist?
lsbRelease := program.FindProgram("lsb_release")
if lsbRelease != nil {
stdout, _, _, err := lsbRelease.Run("-a")
if err != nil {
return result
}
result.DiscoveredBy = "lsb"
for _, line := range strings.Split(stdout, "\n") {
if strings.Contains(line, ":") {
// Iterate lines a
details := strings.Split(line, ":")
key := strings.TrimSpace(details[0])
value := strings.TrimSpace(details[1])
switch key {
case "Distributor ID":
result.DistributorID = value
switch value {
case "Ubuntu":
result.Distribution = Ubuntu
case "Arch", "ManjaroLinux":
result.Distribution = Arch
case "Debian":
result.Distribution = Debian
case "Zorin":
result.Distribution = Zorin
}
case "Description":
result.Description = value
case "Release":
result.Release = value
case "Codename":
result.Codename = value
}
}
}
// check if /etc/os-release exists
} else if _, err := os.Stat("/etc/os-release"); !os.IsNotExist(err) {
// Default value
osName := "Unknown"
version := ""
// read /etc/os-release
result := &DistroInfo{
Distribution: Unknown,
ID: "unknown",
Name: "Unknown",
}
_, err := os.Stat("/etc/os-release")
if !os.IsNotExist(err) {
osRelease, _ := ioutil.ReadFile("/etc/os-release")
// Split into lines
lines := strings.Split(string(osRelease), "\n")
// Iterate lines
for _, line := range lines {
// Split each line by the equals char
splitLine := strings.SplitN(line, "=", 2)
// Check we have
if len(splitLine) != 2 {
continue
}
switch splitLine[0] {
case "NAME":
osName = strings.Trim(splitLine[1], "\"")
case "VERSION_ID":
version = strings.Trim(splitLine[1], "\"")
}
}
// Check distro name against list of distros
result.Release = version
result.DiscoveredBy = "os-release"
switch osName {
case "Fedora":
result.Distribution = RedHat
case "CentOS":
result.Distribution = RedHat
case "Arch Linux":
result.Distribution = Arch
case "Debian GNU/Linux":
result.Distribution = Debian
default:
result.Distribution = Unknown
result.DistributorID = osName
}
result = parseOsRelease(string(osRelease))
}
return result
}
// parseOsRelease parses the given os-release data and returns
// a DistroInfo struct with the details
func parseOsRelease(osRelease string) *DistroInfo {
result := &DistroInfo{Distribution: Unknown}
// Default value
osID := "unknown"
osNAME := "Unknown"
version := ""
// Split into lines
lines := strings.Split(osRelease, "\n")
// Iterate lines
for _, line := range lines {
// Split each line by the equals char
splitLine := strings.SplitN(line, "=", 2)
// Check we have
if len(splitLine) != 2 {
continue
}
switch splitLine[0] {
case "ID":
osID = strings.Trim(splitLine[1], "\"")
case "NAME":
osNAME = strings.Trim(splitLine[1], "\"")
case "VERSION_ID":
version = strings.Trim(splitLine[1], "\"")
}
}
// Check distro name against list of distros
switch osID {
case "fedora":
result.Distribution = Fedora
case "centos":
result.Distribution = CentOS
case "arch":
result.Distribution = Arch
case "debian":
result.Distribution = Debian
case "ubuntu":
result.Distribution = Ubuntu
case "gentoo":
result.Distribution = Gentoo
case "zorin":
result.Distribution = Zorin
case "parrot":
result.Distribution = Parrot
case "linuxmint":
result.Distribution = Linuxmint
case "void":
result.Distribution = VoidLinux
case "elementary":
result.Distribution = Elementary
case "kali":
result.Distribution = Kali
case "neon":
result.Distribution = Neon
case "manjaro":
result.Distribution = Manjaro
default:
result.Distribution = Unknown
}
result.Name = osNAME
result.ID = osID
result.Release = version
return result
}
// CheckPkgInstalled is all functions that use local programs to see if a package is installed
type CheckPkgInstalled func(string) (bool, error)
// EqueryInstalled uses equery to see if a package is installed
func EqueryInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
equery := program.FindProgram("equery")
if equery == nil {
return false, fmt.Errorf("cannont check dependencies: equery not found")
}
_, _, exitCode, _ := equery.Run("l", packageName)
return exitCode == 0, nil
}
// DpkgInstalled uses dpkg to see if a package is installed
func DpkgInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
@@ -147,6 +177,17 @@ func PacmanInstalled(packageName string) (bool, error) {
return exitCode == 0, nil
}
// XbpsInstalled uses pacman to see if a package is installed.
func XbpsInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
xbpsQuery := program.FindProgram("xbps-query")
if xbpsQuery == nil {
return false, fmt.Errorf("cannot check dependencies: xbps-query not found")
}
_, _, exitCode, _ := xbpsQuery.Run("-S", packageName)
return exitCode == 0, nil
}
// RpmInstalled uses rpm to see if a package is installed
func RpmInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
@@ -160,18 +201,18 @@ func RpmInstalled(packageName string) (bool, error) {
// RequestSupportForDistribution promts the user to submit a request to support their
// currently unsupported distribution
func RequestSupportForDistribution(distroInfo *DistroInfo, libraryName string) error {
func RequestSupportForDistribution(distroInfo *DistroInfo) error {
var logger = NewLogger()
defaultError := fmt.Errorf("unable to check libraries on distribution '%s'. Please ensure that the '%s' equivalent is installed", distroInfo.DistributorID, libraryName)
defaultError := fmt.Errorf("unable to check libraries on distribution '%s'", distroInfo.Name)
logger.Yellow("Distribution '%s' is not currently supported, but we would love to!", distroInfo.DistributorID)
q := fmt.Sprintf("Would you like to submit a request to support distribution '%s'?", distroInfo.DistributorID)
logger.Yellow("Distribution '%s' is not currently supported, but we would love to!", distroInfo.Name)
q := fmt.Sprintf("Would you like to submit a request to support distribution '%s'?", distroInfo.Name)
result := Prompt(q, "yes")
if strings.ToLower(result) != "yes" {
return defaultError
}
title := fmt.Sprintf("Support Distribution '%s'", distroInfo.DistributorID)
title := fmt.Sprintf("Support Distribution '%s'", distroInfo.Name)
var str strings.Builder
@@ -186,16 +227,15 @@ func RequestSupportForDistribution(distroInfo *DistroInfo, libraryName string) e
str.WriteString(fmt.Sprintf("| Platform | %s |\n", runtime.GOOS))
str.WriteString(fmt.Sprintf("| Arch | %s |\n", runtime.GOARCH))
str.WriteString(fmt.Sprintf("| GO111MODULE | %s |\n", gomodule))
str.WriteString(fmt.Sprintf("| Distribution ID | %s |\n", distroInfo.DistributorID))
str.WriteString(fmt.Sprintf("| Distribution ID | %s |\n", distroInfo.ID))
str.WriteString(fmt.Sprintf("| Distribution Name | %s |\n", distroInfo.Name))
str.WriteString(fmt.Sprintf("| Distribution Version | %s |\n", distroInfo.Release))
str.WriteString(fmt.Sprintf("| Discovered by | %s |\n", distroInfo.DiscoveredBy))
body := fmt.Sprintf("**Description**\nDistribution '%s' is currently unsupported.\n\n**Further Information**\n\n%s\n\n*Please add any extra information here, EG: libraries that are needed to make the distribution work, or commands to install them*", distroInfo.DistributorID, str.String())
body := fmt.Sprintf("**Description**\nDistribution '%s' is currently unsupported.\n\n**Further Information**\n\n%s\n\n*Please add any extra information here, EG: libraries that are needed to make the distribution work, or commands to install them*", distroInfo.ID, str.String())
fullURL := "https://github.com/wailsapp/wails/issues/new?"
params := "title=" + title + "&body=" + body
fmt.Println("Opening browser to file request.")
browser.OpenURL(fullURL + url.PathEscape(params))
return nil
}

26
cmd/linux_test.go Normal file
View File

@@ -0,0 +1,26 @@
package cmd
import "testing"
func TestUbuntuDetection(t *testing.T) {
osrelease := `
NAME="Ubuntu"
VERSION="18.04.2 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.2 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
`
result := parseOsRelease(osrelease)
if result.Distribution != Ubuntu {
t.Errorf("expected 'Ubuntu' ID but got '%d'", result.Distribution)
}
}

91
cmd/linuxdb.go Normal file
View File

@@ -0,0 +1,91 @@
package cmd
import (
"log"
"github.com/leaanthony/mewn"
"gopkg.in/yaml.v3"
)
// LinuxDB is the database for linux distribution data.
type LinuxDB struct {
Distributions map[string]*Distribution `yaml:"distributions"`
}
// Distribution holds the os-release ID and a map of releases.
type Distribution struct {
ID string `yaml:"id"`
Releases map[string]*Release `yaml:"releases"`
}
// GetRelease attempts to return the specific Release information
// for the given release name. If there is no specific match, the
// default release data is returned.
func (d *Distribution) GetRelease(version string) *Release {
result := d.Releases[version]
if result == nil {
result = d.Releases["default"]
}
return result
}
// Release holds the name and version of the release as given by
// os-release. Programs is a slice of dependant programs required
// to be present on the local installation for Wails to function.
// Libraries is a slice of libraries that must be present for Wails
// applications to compile.
type Release struct {
Name string `yaml:"name"`
Version string `yaml:"version"`
GccVersionCommand string `yaml:"gccversioncommand"`
Programs []*Prerequisite `yaml:"programs"`
Libraries []*Prerequisite `yaml:"libraries"`
}
// Prerequisite is a simple struct containing a program/library name
// plus the distribution specific help text indicating how to install
// it.
type Prerequisite struct {
Name string `yaml:"name"`
Help string `yaml:"help,omitempty"`
}
// Load will load the given filename from disk and attempt to
// import the data into the LinuxDB.
func (l *LinuxDB) Load(filename string) error {
if fs.FileExists(filename) {
data, err := fs.LoadAsBytes(filename)
if err != nil {
return err
}
return l.ImportData(data)
}
return nil
}
// ImportData will unmarshal the given YAML formatted data
// into the LinuxDB
func (l *LinuxDB) ImportData(data []byte) error {
return yaml.Unmarshal(data, l)
}
// GetDistro returns the Distribution information for the
// given distribution name. If the distribution is not supported,
// nil is returned.
func (l *LinuxDB) GetDistro(distro string) *Distribution {
return l.Distributions[distro]
}
// NewLinuxDB creates a new LinuxDB instance from the bundled
// linuxdb.yaml file.
func NewLinuxDB() *LinuxDB {
data := mewn.Bytes("./linuxdb.yaml")
result := LinuxDB{
Distributions: make(map[string]*Distribution),
}
err := result.ImportData(data)
if err != nil {
log.Fatal(err)
}
return &result
}

188
cmd/linuxdb.yaml Normal file
View File

@@ -0,0 +1,188 @@
---
distributions:
debian:
id: debian
releases:
default:
name: Debian
version: default
gccversioncommand: &gccdumpversion -dumpversion
programs: &debiandefaultprograms
- name: gcc
help: Please install with `sudo apt-get install build-essential` and try again
- name: pkg-config
help: Please install with `sudo apt-get install pkg-config` and try again
- name: npm
help: Please install with `curl -sL https://deb.nodesource.com/setup_12.x | sudo bash - && sudo apt-get install -y nodejs` and try again
libraries: &debiandefaultlibraries
- name: libgtk-3-dev
help: Please install with `sudo apt-get install libgtk-3-dev` and try again
- name: libwebkit2gtk-4.0-dev
help: Please install with `sudo apt-get install libwebkit2gtk-4.0-dev` and try again
ubuntu:
id: ubuntu
releases:
default:
version: default
name: Ubuntu
gccversioncommand: &gccdumpfullversion -dumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
kali:
id: kali
releases:
default:
version: default
name: Kali GNU/Linux
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
parrot:
id: parrot
releases:
default:
version: default
name: Parrot GNU/Linux
gccversioncommand: *gccdumpversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
zorin:
id: zorin
releases:
default:
version: default
name: Zorin
gccversioncommand: *gccdumpversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
linuxmint:
id: linuxmint
releases:
default:
version: default
name: Linux Mint
gccversioncommand: *gccdumpversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
elementary:
id: elementary
releases:
default:
version: default
name: elementary OS
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
neon:
id: neon
releases:
default:
version: default
name: KDE neon
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
void:
id: void
releases:
default:
version: default
name: VoidLinux
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install with `xbps-install base-devel` and try again
- name: pkg-config
help: Please install with `xbps-install pkg-config` and try again
- name: npm
help: Please install with `xbps-install nodejs` and try again
libraries:
- name: gtk+3-devel
help: Please install with `xbps-install gtk+3-devel` and try again
- name: webkit2gtk-devel
help: Please install with `xbps-install webkit2gtk-devel` and try again
centos:
id: centos
releases:
default:
version: default
name: CentOS Linux
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install with `sudo yum install gcc-c++ make` and try again
- name: pkg-config
help: Please install with `sudo yum install pkgconf-pkg-config` and try again
- name: npm
help: Please install with `sudo yum install epel-release && sudo yum install nodejs` and try again
libraries:
- name: gtk3-devel
help: Please install with `sudo yum install gtk3-devel` and try again
- name: webkitgtk3-devel
help: Please install with `sudo yum install webkitgtk3-devel` and try again
fedora:
id: fedora
releases:
default:
version: default
name: Fedora
gccversioncommand: *gccdumpfullversion
programs:
- name: gcc
help: Please install with `sudo yum install gcc-c++ make` and try again
- name: pkg-config
help: Please install with `sudo yum install pkgconf-pkg-config` and try again
- name: npm
help: Please install `sudo yum install nodejs` and try again
libraries:
- name: gtk3-devel
help: Please install with `sudo yum install gtk3-devel` and try again
- name: webkit2gtk3-devel
help: Please install with `sudo yum install webkit2gtk3-devel` and try again
arch:
id: arch
releases:
default:
version: default
name: Arch Linux
gccversioncommand: *gccdumpversion
programs: &archdefaultprograms
- name: gcc
help: Please install with `sudo pacman -S gcc` and try again
- name: pkgconf
help: Please install with `sudo pacman -S pkgconf` and try again
- name: npm
help: Please install with `sudo pacman -S npm` and try again
libraries: &archdefaultlibraries
- name: gtk3
help: Please install with `sudo pacman -S gtk3` and try again
- name: webkit2gtk
help: Please install with `sudo pacman -S webkit2gtk` and try again
manjaro:
id: manjaro
releases:
default:
version: default
name: Manjaro Linux
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
gentoo:
id: gentoo
releases:
default:
version: default
name: Gentoo
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install using your system's package manager
- name: pkg-config
help: Please install using your system's package manager
- name: npm
help: Please install using your system's package manager
libraries:
- name: gtk+:3
help: Please install with `sudo emerge gtk+:3` and try again
- name: webkit-gtk
help: Please install with `sudo emerge webkit-gtk` and try again

81
cmd/linuxdb_test.go Normal file
View File

@@ -0,0 +1,81 @@
package cmd
import "testing"
func TestNewLinuxDB(t *testing.T) {
_ = NewLinuxDB()
}
func TestKnownDistro(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("ubuntu")
if result == nil {
t.Error("Cannot get distro 'ubuntu'")
}
}
func TestUnknownDistro(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("unknown")
if result != nil {
t.Error("Should get nil for distribution 'unknown'")
}
}
func TestDefaultRelease(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("ubuntu")
if result == nil {
t.Error("Cannot get distro 'ubuntu'")
}
release := result.GetRelease("default")
if release == nil {
t.Error("Cannot get release 'default' for distro 'ubuntu'")
}
}
func TestUnknownRelease(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("ubuntu")
if result == nil {
t.Error("Cannot get distro 'ubuntu'")
}
release := result.GetRelease("16.04")
if release == nil {
t.Error("Failed to get release 'default' for unknown release version '16.04'")
}
if release.Version != "default" {
t.Errorf("Got version '%s' instead of 'default' for unknown release version '16.04'", result.ID)
}
}
func TestGetPrerequisites(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("debian")
if result == nil {
t.Error("Cannot get distro 'debian'")
}
release := result.GetRelease("default")
if release == nil {
t.Error("Failed to get release 'default' for unknown release version '16.04'")
}
if release.Version != "default" {
t.Errorf("Got version '%s' instead of 'default' for unknown release version '16.04'", result.ID)
}
if release.Name != "Debian" {
t.Errorf("Got Release Name '%s' instead of 'debian' for unknown release version '16.04'", release.Name)
}
if len(release.Programs) != 3 {
t.Errorf("Expected %d programs for unknown release version '16.04'", len(release.Programs))
}
if len(release.Libraries) != 2 {
t.Errorf("Expected %d libraries for unknown release version '16.04'", len(release.Libraries))
}
}

View File

@@ -191,8 +191,15 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
// Build syso
sysofile := filepath.Join(b.fs.Cwd(), basename+"-res.syso")
windresCommand := []string{"windres", "-o", sysofile, tgtRCFile}
err := NewProgramHelper().RunCommandArray(windresCommand)
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
}

View File

@@ -5,13 +5,6 @@ import (
"runtime"
)
// Prerequisite defines a Prerequisite!
type Prerequisite struct {
Name string
Help string
Path string
}
func newPrerequisite(name, help string) *Prerequisite {
return &Prerequisite{Name: name, Help: help}
}
@@ -48,16 +41,13 @@ func getRequiredProgramsOSX() *Prerequisites {
func getRequiredProgramsLinux() *Prerequisites {
result := &Prerequisites{}
distroInfo := GetLinuxDistroInfo()
switch distroInfo.Distribution {
case Ubuntu, Debian, Zorin:
result.Add(newPrerequisite("gcc", "Please install with `sudo apt install build-essentials` and try again"))
result.Add(newPrerequisite("pkg-config", "Please install with `sudo apt install pkg-config` and try again"))
result.Add(newPrerequisite("npm", "Please install with `sudo snap install node --channel=12/stable --classic` and try again"))
default:
result.Add(newPrerequisite("gcc", "Please install with your system package manager and try again"))
result.Add(newPrerequisite("pkg-config", "Please install with your system package manager and try again"))
result.Add(newPrerequisite("npm", "Please install from https://nodejs.org/en/download/ and try again"))
if distroInfo.Distribution != Unknown {
var linuxDB = NewLinuxDB()
distro := linuxDB.GetDistro(distroInfo.ID)
release := distro.GetRelease(distroInfo.Release)
for _, program := range release.Programs {
result.Add(program)
}
}
return result
}
@@ -91,20 +81,15 @@ func getRequiredLibrariesOSX() (*Prerequisites, error) {
func getRequiredLibrariesLinux() (*Prerequisites, error) {
result := &Prerequisites{}
// The Linux Distribution DB
distroInfo := GetLinuxDistroInfo()
switch distroInfo.Distribution {
case Ubuntu, Debian, Zorin:
result.Add(newPrerequisite("libgtk-3-dev", "Please install with `sudo apt install libgtk-3-dev` and try again"))
result.Add(newPrerequisite("libwebkit2gtk-4.0-dev", "Please install with `sudo apt install libwebkit2gtk-4.0-dev` and try again"))
case Arch:
result.Add(newPrerequisite("gtk3", "Please install with `sudo pacman -S gtk3` and try again"))
result.Add(newPrerequisite("webkit2gtk", "Please install with `sudo pacman -S webkit2gtk` and try again"))
case RedHat:
result.Add(newPrerequisite("gtk3-devel", "Please install with `sudo yum install gtk3-devel` and try again"))
result.Add(newPrerequisite("webkit2gtk3-devel", "Please install with `sudo yum install webkit2gtk3-devel` and try again"))
default:
result.Add(newPrerequisite("libgtk-3-dev", "Please install with your system package manager and try again"))
result.Add(newPrerequisite("libwebkit2gtk-4.0-dev", "Please install with your system package manager and try again"))
if distroInfo.Distribution != Unknown {
var linuxDB = NewLinuxDB()
distro := linuxDB.GetDistro(distroInfo.ID)
release := distro.GetRelease(distroInfo.Release)
for _, library := range release.Libraries {
result.Add(library)
}
}
return result, nil
}

View File

@@ -100,6 +100,19 @@ func (p *ProgramHelper) InstallGoPackage(packageName string) error {
return err
}
// InstallNPMPackage installs the given npm package
func (p *ProgramHelper) InstallNPMPackage(packageName string, save bool) error {
args := strings.Split("install "+packageName, " ")
if save {
args = append(args, "--save")
}
_, stderr, err := p.shell.Run("npm", args...)
if err != nil {
fmt.Println(stderr)
}
return err
}
// RunCommand runs the given command
func (p *ProgramHelper) RunCommand(command string) error {
args := strings.Split(command, " ")
@@ -115,16 +128,18 @@ func (p *ProgramHelper) RunCommandArray(args []string, dir ...string) error {
fmt.Printf("ERROR: Looks like '%s' isn't installed. Please install and try again.", program)
return err
}
args = args[1:]
var stderr string
// fmt.Printf("RunCommandArray = %s %+v\n", program, args)
var stdout string
if len(dir) > 0 {
_, stderr, err = p.shell.RunInDirectory(dir[0], program, args...)
stdout, stderr, err = p.shell.RunInDirectory(dir[0], program, args...)
} else {
_, stderr, err = p.shell.Run(program, args...)
stdout, stderr, err = p.shell.Run(program, args...)
}
if err != nil {
fmt.Println(stderr)
fmt.Println(stdout)
}
return err
}

View File

@@ -13,6 +13,18 @@ import (
"github.com/leaanthony/slicer"
)
// PackageManager indicates different package managers
type PackageManager int
const (
// UNKNOWN package manager
UNKNOWN PackageManager = iota
// NPM package manager
NPM
// YARN package manager
YARN
)
type author struct {
Name string `json:"name"`
Email string `json:"email"`
@@ -112,9 +124,9 @@ func (ph *ProjectHelper) NewProjectOptions() *ProjectOptions {
Description: "Enter your project description",
Version: "0.1.0",
BinaryName: "",
system: NewSystemHelper(),
log: NewLogger(),
templates: NewTemplateHelper(),
system: ph.system,
log: ph.log,
templates: ph.templates,
Author: &author{},
}
@@ -153,6 +165,23 @@ func (po *ProjectOptions) Defaults() {
po.WailsVersion = Version
}
// GetNPMBinaryName returns the type of package manager used by the project
func (po *ProjectOptions) GetNPMBinaryName() (PackageManager, error) {
if po.FrontEnd == nil {
return UNKNOWN, fmt.Errorf("No frontend specified in project options")
}
if strings.Index(po.FrontEnd.Install, "npm") > -1 {
return NPM, nil
}
if strings.Index(po.FrontEnd.Install, "yarn") > -1 {
return YARN, nil
}
return UNKNOWN, nil
}
// PromptForInputs asks the user to input project details
func (po *ProjectOptions) PromptForInputs() error {

View File

@@ -4,7 +4,6 @@ import (
"bufio"
"fmt"
"os"
"runtime"
"strconv"
"strings"
)
@@ -20,11 +19,7 @@ func Prompt(question string, defaultValue ...string) string {
fmt.Printf(question + ": ")
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
EOL := "\n"
if runtime.GOOS == "windows" {
EOL = "\r\n"
}
input = strings.Replace(input, EOL, "", -1)
input = strings.TrimSpace(input)
if input != "" {
answer = input

View File

@@ -3,7 +3,7 @@ package cmd
import (
"fmt"
"github.com/masterminds/semver"
"github.com/Masterminds/semver"
)
// SemanticVersion is a struct containing a semantic version

View File

@@ -269,44 +269,35 @@ func CheckDependencies(logger *Logger) (bool, error) {
if err != nil {
return false, err
}
var libraryChecker CheckPkgInstalled
distroInfo := GetLinuxDistroInfo()
switch distroInfo.Distribution {
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon:
libraryChecker = DpkgInstalled
case Arch, Manjaro:
libraryChecker = PacmanInstalled
case CentOS, Fedora:
libraryChecker = RpmInstalled
case Gentoo:
libraryChecker = EqueryInstalled
case VoidLinux:
libraryChecker = XbpsInstalled
default:
return false, RequestSupportForDistribution(distroInfo)
}
for _, library := range *requiredLibraries {
switch distroInfo.Distribution {
case Ubuntu, Zorin, Debian:
installed, err := DpkgInstalled(library.Name)
if err != nil {
return false, err
}
if !installed {
errors = true
logger.Error("Library '%s' not found. %s", library.Name, library.Help)
} else {
logger.Green("Library '%s' installed.", library.Name)
}
case Arch:
installed, err := PacmanInstalled(library.Name)
if err != nil {
return false, err
}
if !installed {
errors = true
logger.Error("Library '%s' not found. %s", library.Name, library.Help)
} else {
logger.Green("Library '%s' installed.", library.Name)
}
case RedHat:
installed, err := RpmInstalled(library.Name)
if err != nil {
return false, err
}
if !installed {
errors = true
logger.Error("Library '%s' not found. %s", library.Name, library.Help)
} else {
logger.Green("Library '%s' installed.", library.Name)
}
default:
return false, RequestSupportForDistribution(distroInfo, library.Name)
installed, err := libraryChecker(library.Name)
if err != nil {
return false, err
}
if !installed {
errors = true
logger.Error("Library '%s' not found. %s", library.Name, library.Help)
} else {
logger.Green("Library '%s' installed.", library.Name)
}
}
}

View File

@@ -67,7 +67,7 @@ func NewTemplateHelper() *TemplateHelper {
}
}
// IsValidTemplate returns true if the given tempalte name resides on disk
// IsValidTemplate returns true if the given template name resides on disk
func (t *TemplateHelper) IsValidTemplate(templateName string) bool {
pathToTemplate := filepath.Join(t.templateDir.fullPath, templateName)
return t.fs.DirExists(pathToTemplate)

View File

@@ -9,4 +9,4 @@
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.
IE 9-11 # For IE 9-11 support, remove 'not'.

View File

@@ -21,6 +21,8 @@
"@angular/platform-browser": "~8.0.1",
"@angular/platform-browser-dynamic": "~8.0.1",
"@angular/router": "~8.0.1",
"@wailsapp/runtime": "^1.0.0",
"core-js": "^3.4.4",
"ngx-build-plus": "^8.0.3",
"rxjs": "~6.4.0",
"tslib": "^1.9.0",

View File

@@ -1,3 +1,4 @@
import 'core-js/stable';
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
@@ -6,13 +7,13 @@ import { environment } from './environments/environment';
import 'zone.js'
import Bridge from './wailsbridge';
import * as Wails from '@wailsapp/runtime';
if (environment.production) {
enableProdMode();
}
Bridge.Start(() => {
Wails.Init(() => {
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
});

View File

@@ -1,17 +0,0 @@
/*
Wails Bridge (c) 2019-present Lea Anthony
This prod version is to get around having to rewrite your code
for production. When doing a release build, this file will be used
instead of the full version.
*/
export default {
// The main function
// Passes the main Wails object to the callback if given.
Start: function(callback) {
if (callback) {
window.wails.events.on("wails:ready", callback);
}
}
};

View File

@@ -11,7 +11,7 @@
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
@@ -20,4 +20,4 @@
"dom"
]
}
}
}

View File

@@ -11,7 +11,7 @@ func basic() string {
func main() {
js := mewn.String("./frontend/dist/my-app/main-es2015.js")
js := mewn.String("./frontend/dist/my-app/main.js")
css := mewn.String("./frontend/dist/my-app/styles.css")
app := wails.CreateApp(&wails.AppConfig{

View File

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

View File

@@ -1,10 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom';
import 'core-js/stable';
import './index.css';
import App from './App';
import Bridge from "./wailsbridge";
import * as Wails from '@wailsapp/runtime';
Bridge.Start(() => {
Wails.Init(() => {
ReactDOM.render(<App />, document.getElementById('app'));
});

View File

@@ -1,17 +0,0 @@
/*
Wails Bridge (c) 2019-present Lea Anthony
This prod version is to get around having to rewrite your code
for production. When doing a release build, this file will be used
instead of the full version.
*/
export default {
// The main function
// Passes the main Wails object to the callback if given.
Start: function (callback) {
if (callback) {
window.wails.Events.On("wails:ready", callback);
}
}
};

View File

@@ -9,7 +9,8 @@
},
"dependencies": {
"core-js": "^2.6.4",
"vue": "^2.5.22"
"vue": "^2.5.22",
"@wailsapp/runtime": "^1.0.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.4.0",

View File

@@ -1,13 +1,13 @@
import Vue from "vue";
import App from "./App.vue";
import Vue from 'vue';
import App from './App.vue';
Vue.config.productionTip = false;
Vue.config.devtools = true;
import Bridge from "./wailsbridge";
import * as Wails from '@wailsapp/runtime';
Bridge.Start(() => {
new Vue({
render: h => h(App)
}).$mount("#app");
Wails.Init(() => {
new Vue({
render: h => h(App)
}).$mount('#app');
});

View File

@@ -1,17 +0,0 @@
/*
Wails Bridge (c) 2019-present Lea Anthony
This prod version is to get around having to rewrite your code
for production. When doing a release build, this file will be used
instead of the full version.
*/
export default {
// The main function
// Passes the main Wails object to the callback if given.
Start: function (callback) {
if (callback) {
window.wails.Events.On("wails:ready", callback);
}
}
};

View File

@@ -12,7 +12,8 @@
"core-js": "^2.6.4",
"material-design-icons-iconfont": "^5.0.1",
"vue": "^2.5.22",
"vuetify": "^1.5.14"
"vuetify": "^1.5.14",
"@wailsapp/runtime": "^1.0.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.4.0",

View File

@@ -1,5 +1,5 @@
import 'babel-polyfill';
import Vue from "vue";
import Vue from 'vue';
// Setup Vuetify
import Vuetify from 'vuetify';
@@ -7,15 +7,15 @@ Vue.use(Vuetify);
import 'vuetify/dist/vuetify.min.css';
import 'material-design-icons-iconfont';
import App from "./App.vue";
import App from './App.vue';
Vue.config.productionTip = false;
Vue.config.devtools = true;
import Bridge from "./wailsbridge";
import * as Wails from '@wailsapp/runtime';
Bridge.Start(() => {
new Vue({
render: h => h(App)
}).$mount("#app");
Wails.Init(() => {
new Vue({
render: h => h(App)
}).$mount('#app');
});

View File

@@ -1,4 +1,4 @@
package cmd
// Version - Wails version
const Version = "v0.17.0"
const Version = "v1.0.0"

View File

@@ -1,7 +1,6 @@
package main
import (
"fmt"
"runtime"
"github.com/wailsapp/wails/cmd"
@@ -24,7 +23,7 @@ func init() {
system := cmd.NewSystemHelper()
err = system.Initialise()
if err == nil {
if err != nil {
return err
}
@@ -33,23 +32,9 @@ Create your first project by running 'wails init'.`
if runtime.GOOS != "windows" {
successMessage = "🚀 " + successMessage
}
// Platform check
err = platformCheck()
if err != nil {
return err
}
// Check we have a cgo capable environment
logger.Yellow("Checking for prerequisites...")
var requiredProgramErrors bool
requiredProgramErrors, err = checkRequiredPrograms()
if err != nil {
return err
}
// Linux has library deps
var libraryErrors bool
libraryErrors, err = checkLibraries()
// Chrck for programs and libraries dependencies
errors, err := cmd.CheckDependencies(logger)
if err != nil {
return err
}
@@ -60,76 +45,14 @@ Create your first project by running 'wails init'.`
return err
}
logger.White("")
// Check for errors
var errors = libraryErrors || requiredProgramErrors
if !errors {
// CheckDependencies() returns !errors
// so to get the right message in this
// check we have to do it in reversed
if errors {
logger.Yellow(successMessage)
}
return err
})
}
func platformCheck() error {
switch runtime.GOOS {
case "darwin":
logger.Yellow("Detected Platform: OSX")
case "windows":
logger.Yellow("Detected Platform: Windows")
case "linux":
logger.Yellow("Detected Platform: Linux")
default:
return fmt.Errorf("Platform %s is currently not supported", runtime.GOOS)
}
return nil
}
func checkLibraries() (errors bool, err error) {
if runtime.GOOS == "linux" {
// Check library prerequisites
requiredLibraries, err := cmd.GetRequiredLibraries()
if err != nil {
return false, err
}
distroInfo := cmd.GetLinuxDistroInfo()
for _, library := range *requiredLibraries {
switch distroInfo.Distribution {
case cmd.Ubuntu, cmd.Zorin, cmd.Debian:
installed, err := cmd.DpkgInstalled(library.Name)
if err != nil {
return false, err
}
if !installed {
errors = true
logger.Red("Library '%s' not found. %s", library.Name, library.Help)
} else {
logger.Green("Library '%s' installed.", library.Name)
}
default:
return false, cmd.RequestSupportForDistribution(distroInfo, library.Name)
}
}
}
return false, nil
}
func checkRequiredPrograms() (errors bool, err error) {
requiredPrograms, err := cmd.GetRequiredPrograms()
if err != nil {
return true, err
}
errors = false
programHelper := cmd.NewProgramHelper()
for _, program := range *requiredPrograms {
bin := programHelper.FindProgram(program.Name)
if bin == nil {
errors = true
logger.Red("Program '%s' not found. %s", program.Name, program.Help)
} else {
logger.Green("Program '%s' found: %s", program.Name, bin.Path)
}
}
return
}

408
cmd/wails/15_migrate.go Normal file
View File

@@ -0,0 +1,408 @@
package main
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/Masterminds/semver"
"github.com/leaanthony/spinner"
"github.com/wailsapp/wails/cmd"
)
// Constants
var checkSpinner = spinner.NewSpinner()
var migrateProjectOptions = &cmd.ProjectOptions{}
var migrateFS = cmd.NewFSHelper()
var migrateGithub = cmd.NewGitHubHelper()
var programHelper = cmd.NewProgramHelper()
var lessThanV1 *semver.Constraints
// The user's go.mod
var goMod string
var goModFile string
// The user's main.js
var mainJSFile string
var mainJSContents string
// Frontend directory
var frontEndDir string
func init() {
var dryrun bool
var err error
lessThanV1, err = semver.NewConstraint("< v1.0.0")
if err != nil {
log.Fatal(err)
}
// var forceRebuild = false
checkSpinner.SetSpinSpeed(50)
commandDescription := `EXPERIMENTAL - This command attempts to migrate projects to the latest Wails version.`
updateCmd := app.Command("migrate", "Migrate projects to latest Wails release").
LongDescription(commandDescription).
BoolFlag("dryrun", "Only display what would be done", &dryrun)
updateCmd.Action(func() error {
message := "Migrate Project"
logger.PrintSmallBanner(message)
logger.Red("WARNING: This is an experimental command. Ensure you have backups of your project!")
logger.Red("It currently only supports npm based projects.")
fmt.Println()
// Check project directory
err := checkProjectDirectory()
if err != nil {
return err
}
// Find Wails version from go.mod
wailsVersion, err := getWailsVersion()
if err != nil {
return err
}
// Get latest stable version
var latestVersion *semver.Version
latestVersion, err = getLatestWailsVersion()
if err != nil {
return err
}
var canMigrate bool
canMigrate, err = canMigrateVersion(wailsVersion, latestVersion)
if err != nil {
return err
}
if !canMigrate {
return nil
}
// Check for wailsbridge
wailsBridge, err := checkWailsBridge()
if err != nil {
return err
}
// Is main.js using bridge.Init()
canUpdateMainJS, err := checkMainJS()
if err != nil {
return err
}
// TODO: Check if we are using legacy js runtime
// Operations
logger.Yellow("Operations to perform:")
logger.Yellowf(" - Update to Wails v%s\n", latestVersion)
if len(wailsBridge) > 0 {
logger.Yellow(" - Delete wailsbridge.js")
}
if canUpdateMainJS {
logger.Yellow(" - Patch main.js")
}
logger.Yellow(" - Ensure '@wailsapp/runtime` module is installed")
if dryrun {
logger.White("Exiting: Dry Run")
return nil
}
logger.Red("*WARNING* About to modify your project!")
logger.Red("Type 'YES' to continue: ")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
input := scanner.Text()
if input != "YES" {
logger.Red("ABORTED!")
return nil
}
logger.Yellow("Let's do this!")
err = updateWailsVersion(wailsVersion, latestVersion)
if err != nil {
return err
}
if len(wailsBridge) > 0 {
err = deleteWailsBridge(wailsBridge)
if err != nil {
return err
}
}
if canUpdateMainJS {
err = patchMainJS()
if err != nil {
return err
}
}
// Install runtime
err = installWailsRuntime()
if err != nil {
return err
}
fmt.Println()
logger.Yellow("Migration complete! Check project by running `wails build`.")
return nil
})
}
func checkProjectDirectory() error {
// Get versions
checkSpinner.Start("Check Project Directory")
// Check we are in project directory
err := migrateProjectOptions.LoadConfig(migrateFS.Cwd())
if err != nil {
checkSpinner.Error()
return fmt.Errorf("Unable to find 'project.json'. Please check you are in a Wails project directory")
}
checkSpinner.Success()
return nil
}
func getWailsVersion() (*semver.Version, error) {
checkSpinner.Start("Get Wails Version")
var result *semver.Version
// Load file
var err error
goModFile, err = filepath.Abs(filepath.Join(".", "go.mod"))
if err != nil {
checkSpinner.Error()
return nil, fmt.Errorf("Unable to load go.mod at %s", goModFile)
}
goMod, err = migrateFS.LoadAsString(goModFile)
if err != nil {
checkSpinner.Error()
return nil, fmt.Errorf("Unable to load go.mod")
}
// Find wails version
versionRegexp := regexp.MustCompile(`.*github.com/wailsapp/wails.*(v\d+.\d+.\d+)`)
versions := versionRegexp.FindStringSubmatch(goMod)
if len(versions) != 2 {
return nil, fmt.Errorf("Unable to determine Wails version")
}
version := versions[1]
result, err = semver.NewVersion(version)
if err != nil {
return nil, fmt.Errorf("Unable to parse Wails version: %s", version)
}
checkSpinner.Success("Found Wails Version: " + version)
return result, nil
}
func canMigrateVersion(wailsVersion *semver.Version, latestVersion *semver.Version) (bool, error) {
checkSpinner.Start("Checking ability to Migrate")
// Check if we are at the latest version!!!!
if wailsVersion.Equal(latestVersion) || wailsVersion.GreaterThan(latestVersion) {
checkSpinner.Errorf("Checking ability to Migrate: No! (v%s >= v%s)", wailsVersion, latestVersion)
return false, nil
}
// Check for < v1.0.0
if lessThanV1.Check(wailsVersion) {
checkSpinner.Successf("Checking ability to Migrate: Yes! (v%s < v1.0.0)", wailsVersion)
return true, nil
}
checkSpinner.Error("Unable to migrate")
return false, fmt.Errorf("No migration rules for version %s", wailsVersion)
}
func checkWailsBridge() (string, error) {
checkSpinner.Start("Checking if legacy Wails Bridge present")
// Check frontend dir is available
if migrateProjectOptions.FrontEnd == nil ||
len(migrateProjectOptions.FrontEnd.Dir) == 0 ||
!migrateFS.DirExists(migrateProjectOptions.FrontEnd.Dir) {
checkSpinner.Error("Unable to determine frontend directory")
return "", fmt.Errorf("Unable to determine frontend directory")
}
frontEndDir = migrateProjectOptions.FrontEnd.Dir
wailsBridgePath, err := filepath.Abs(filepath.Join(".", frontEndDir, "src", "wailsbridge.js"))
if err != nil {
checkSpinner.Error(err.Error())
return "", err
}
// If it doesn't exist, return blank string
if !migrateFS.FileExists(wailsBridgePath) {
checkSpinner.Success("Checking if legacy Wails Bridge present: No")
return "", nil
}
checkSpinner.Success("Checking if legacy Wails Bridge present: Yes")
return wailsBridgePath, nil
}
// This function determines if the main.js file using wailsbridge can be auto-updated
func checkMainJS() (bool, error) {
checkSpinner.Start("Checking if main.js can be migrated")
var err error
// Check main.js is there
if migrateProjectOptions.FrontEnd == nil ||
len(migrateProjectOptions.FrontEnd.Dir) == 0 ||
!migrateFS.DirExists(migrateProjectOptions.FrontEnd.Dir) {
checkSpinner.Error("Unable to determine frontend directory")
return false, fmt.Errorf("Unable to determine frontend directory")
}
frontEndDir = migrateProjectOptions.FrontEnd.Dir
mainJSFile, err = filepath.Abs(filepath.Join(".", frontEndDir, "src", "main.js"))
if err != nil {
checkSpinner.Error("Unable to find main.js")
return false, err
}
mainJSContents, err = migrateFS.LoadAsString(mainJSFile)
if err != nil {
checkSpinner.Error("Unable to load main.js")
return false, err
}
// Check we have a line like: import Bridge from "./wailsbridge";
if strings.Index(mainJSContents, `import Bridge from "./wailsbridge";`) == -1 {
checkSpinner.Success("Checking if main.js can be migrated: No - Cannot find `import Bridge`")
return false, nil
}
// Check we have a line like: Bridge.Start(() => {
if strings.Index(mainJSContents, `Bridge.Start(`) == -1 {
checkSpinner.Success("Checking if main.js can be migrated: No - Cannot find `Bridge.Start`")
return false, nil
}
checkSpinner.Success("Checking if main.js can be migrated: Yes")
return true, nil
}
func getLatestWailsVersion() (*semver.Version, error) {
checkSpinner.Start("Checking GitHub for latest Wails version")
version, err := migrateGithub.GetLatestStableRelease()
if err != nil {
checkSpinner.Error("Checking GitHub for latest Wails version: Failed")
return nil, err
}
checkSpinner.Successf("Checking GitHub for latest Wails version: v%s", version)
return version.Version, nil
}
func updateWailsVersion(currentVersion, latestVersion *semver.Version) error {
// Patch go.mod
checkSpinner.Start("Patching go.mod")
wailsModule := "github.com/wailsapp/wails"
old := fmt.Sprintf("%s v%s", wailsModule, currentVersion)
new := fmt.Sprintf("%s v%s", wailsModule, latestVersion)
goMod = strings.Replace(goMod, old, new, -1)
err := ioutil.WriteFile(goModFile, []byte(goMod), 0600)
if err != nil {
checkSpinner.Error()
return err
}
checkSpinner.Success()
return nil
}
func deleteWailsBridge(bridgeFilename string) error {
// Patch go.mod
checkSpinner.Start("Delete legacy wailsbridge.js")
err := migrateFS.RemoveFile(bridgeFilename)
if err != nil {
checkSpinner.Error()
return err
}
checkSpinner.Success()
return nil
}
func patchMainJS() error {
// Patch main.js
checkSpinner.Start("Patching main.js")
// Patch import line
oldImportLine := `import Bridge from "./wailsbridge";`
newImportLine := `import * as Wails from "@wailsapp/runtime";`
mainJSContents = strings.Replace(mainJSContents, oldImportLine, newImportLine, -1)
// Patch Start line
oldStartLine := `Bridge.Start`
newStartLine := `Wails.Init`
mainJSContents = strings.Replace(mainJSContents, oldStartLine, newStartLine, -1)
err := ioutil.WriteFile(mainJSFile, []byte(mainJSContents), 0600)
if err != nil {
checkSpinner.Error()
return err
}
checkSpinner.Success()
return nil
}
func installWailsRuntime() error {
checkSpinner.Start("Installing @wailsapp/runtime module")
// Change to the frontend directory
err := os.Chdir(frontEndDir)
if err != nil {
checkSpinner.Error()
return nil
}
// Determine package manager
packageManager, err := migrateProjectOptions.GetNPMBinaryName()
if err != nil {
checkSpinner.Error()
return nil
}
switch packageManager {
case cmd.NPM:
// npm install --save @wailsapp/runtime
programHelper.InstallNPMPackage("@wailsapp/runtime", true)
default:
checkSpinner.Error()
return fmt.Errorf("Unknown package manager")
}
checkSpinner.Success()
return nil
}

View File

@@ -72,8 +72,17 @@ func init() {
if err != nil {
return err
}
// Ensure that runtime init.js is the production version
err = cmd.InstallProdRuntime(projectDir, projectOptions)
if err != nil {
return err
}
}
// Move to project directory
err = os.Chdir(projectDir)
if err != nil {

View File

@@ -45,7 +45,7 @@ func init() {
projectDir := fs.Cwd()
// Install the bridge library
err = cmd.InstallBridge("serve", projectDir, projectOptions)
err = cmd.InstallBridge(projectDir, projectOptions)
if err != nil {
return err
}

View File

@@ -42,12 +42,65 @@ To help you in this process, we will ask for some information, add Go/Wails deta
gomodule = "(Not Set)"
}
// get version numbers for GCC, node & npm
program := cmd.NewProgramHelper()
// string helpers
var gccVersion, nodeVersion, npmVersion string
// choose between OS (mac,linux,win)
switch runtime.GOOS {
case "darwin":
gcc := program.FindProgram("gcc")
if gcc != nil {
stdout, _, _, _ := gcc.Run("-dumpversion")
gccVersion = strings.TrimSpace(stdout)
}
case "linux":
// for linux we have to collect
// the distribution name
distroInfo := cmd.GetLinuxDistroInfo()
linuxDB := cmd.NewLinuxDB()
distro := linuxDB.GetDistro(distroInfo.ID)
release := distro.GetRelease(distroInfo.Release)
gccVersionCommand := release.GccVersionCommand
gcc := program.FindProgram("gcc")
if gcc != nil {
stdout, _, _, _ := gcc.Run(gccVersionCommand)
gccVersion = strings.TrimSpace(stdout)
}
case "windows":
gcc := program.FindProgram("gcc")
if gcc != nil {
stdout, _, _, _ := gcc.Run("-dumpversion")
gccVersion = strings.TrimSpace(stdout)
}
}
npm := program.FindProgram("npm")
if npm != nil {
stdout, _, _, _ := npm.Run("--version")
npmVersion = stdout
npmVersion = npmVersion[:len(npmVersion)-1]
npmVersion = strings.TrimSpace(npmVersion)
}
node := program.FindProgram("node")
if node != nil {
stdout, _, _, _ := node.Run("--version")
nodeVersion = stdout
nodeVersion = nodeVersion[:len(nodeVersion)-1]
}
str.WriteString("\n| Name | Value |\n| ----- | ----- |\n")
str.WriteString(fmt.Sprintf("| Wails Version | %s |\n", cmd.Version))
str.WriteString(fmt.Sprintf("| Go Version | %s |\n", runtime.Version()))
str.WriteString(fmt.Sprintf("| Platform | %s |\n", runtime.GOOS))
str.WriteString(fmt.Sprintf("| Arch | %s |\n", runtime.GOARCH))
str.WriteString(fmt.Sprintf("| GO111MODULE | %s |\n", gomodule))
str.WriteString(fmt.Sprintf("| GCC | %s |\n", gccVersion))
str.WriteString(fmt.Sprintf("| Npm | %s |\n", npmVersion))
str.WriteString(fmt.Sprintf("| Node | %s |\n", nodeVersion))
fmt.Println()
fmt.Println("Processing template and preparing for upload.")

View File

@@ -1,6 +1,9 @@
package main
import (
"os"
"os/exec"
"github.com/wailsapp/wails/cmd"
)
@@ -15,5 +18,9 @@ func main() {
err := app.Run()
if err != nil {
logger.Error(err.Error())
if exitErr, ok := err.(*exec.ExitError); ok {
os.Exit(exitErr.ExitCode())
}
os.Exit(1)
}
}

1
cmd/windres.bat Normal file
View File

@@ -0,0 +1 @@
windres.exe -o %1 %2

113
config.go Normal file
View File

@@ -0,0 +1,113 @@
package wails
import (
"github.com/leaanthony/mewn"
"github.com/wailsapp/wails/runtime"
)
// AppConfig is the configuration structure used when creating a Wails App object
type AppConfig struct {
Width, Height int
Title string
defaultHTML string
HTML string
JS string
CSS string
Colour string
Resizable bool
DisableInspector bool
}
// GetWidth returns the desired width
func (a *AppConfig) GetWidth() int {
return a.Width
}
// GetHeight returns the desired height
func (a *AppConfig) GetHeight() int {
return a.Height
}
// GetTitle returns the desired window title
func (a *AppConfig) GetTitle() string {
return a.Title
}
// GetDefaultHTML returns the default HTML
func (a *AppConfig) GetDefaultHTML() string {
return a.defaultHTML
}
// GetResizable returns true if the window should be resizable
func (a *AppConfig) GetResizable() bool {
return a.Resizable
}
// GetDisableInspector returns true if the inspector should be disabled
func (a *AppConfig) GetDisableInspector() bool {
return a.DisableInspector
}
// GetColour returns the colour
func (a *AppConfig) GetColour() string {
return a.Colour
}
// GetCSS returns the user CSS
func (a *AppConfig) GetCSS() string {
return a.CSS
}
// GetJS returns the user Javascript
func (a *AppConfig) GetJS() string {
return a.JS
}
func (a *AppConfig) merge(in *AppConfig) error {
if in.CSS != "" {
a.CSS = in.CSS
}
if in.Title != "" {
a.Title = runtime.ProcessEncoding(in.Title)
}
if in.Colour != "" {
a.Colour = in.Colour
}
if in.JS != "" {
a.JS = in.JS
}
if in.Width != 0 {
a.Width = in.Width
}
if in.Height != 0 {
a.Height = in.Height
}
a.Resizable = in.Resizable
a.DisableInspector = in.DisableInspector
return nil
}
// Creates the default configuration
func newConfig(userConfig *AppConfig) (*AppConfig, error) {
result := &AppConfig{
Width: 800,
Height: 600,
Resizable: true,
Title: "My Wails App",
Colour: "#FFF", // White by default
HTML: mewn.String("./runtime/assets/default.html"),
}
if userConfig != nil {
err := result.merge(userConfig)
if err != nil {
return nil, err
}
}
return result, nil
}

10
go.mod
View File

@@ -1,7 +1,8 @@
module github.com/wailsapp/wails
require (
github.com/Masterminds/semver v1.4.2 // indirect
github.com/Masterminds/semver v1.4.2
github.com/abadojack/whatlanggo v1.0.1
github.com/dchest/cssmin v0.0.0-20151210170030-fb8d9b44afdc // indirect
github.com/dchest/htmlmin v0.0.0-20150526090704-e254725e81ac
github.com/dchest/jsmin v0.0.0-20160823214000-faeced883947 // indirect
@@ -14,7 +15,6 @@ require (
github.com/leaanthony/mewn v0.10.7
github.com/leaanthony/slicer v1.3.2
github.com/leaanthony/spinner v0.5.3
github.com/masterminds/semver v1.4.2
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
@@ -23,9 +23,13 @@ require (
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/wailsapp/webview v0.2.7
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/text v0.3.0
gopkg.in/AlecAivazis/survey.v1 v1.8.4
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22
)
go 1.13

20
go.sum
View File

@@ -2,6 +2,8 @@ github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITg
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/abadojack/whatlanggo v1.0.1 h1:19N6YogDnf71CTHm3Mp2qhYfkRdyvbgwWdd2EPxJRG4=
github.com/abadojack/whatlanggo v1.0.1/go.mod h1:66WiQbSbJBIlOZMsvbKe5m6pzQovxCH9B/K8tQB2uoc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -25,7 +27,6 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -41,14 +42,10 @@ github.com/leaanthony/synx v0.1.0 h1:R0lmg2w6VMb8XcotOwAe5DLyzwjLrskNkwU7LLWsyL8
github.com/leaanthony/synx v0.1.0/go.mod h1:Iz7eybeeG8bdq640iR+CwYb8p+9EOsgMWghkSRyZcqs=
github.com/leaanthony/wincursor v0.1.0 h1:Dsyp68QcF5cCs65AMBmxoYNEm0n8K7mMchG6a8fYxf8=
github.com/leaanthony/wincursor v0.1.0/go.mod h1:7TVwwrzSH/2Y9gLOGH+VhA+bZhoWXBRgbGNTMk+yimE=
github.com/masterminds/semver v1.4.2 h1:BgrAYDjlAebjtOwS7C/1QZoh5WgyXx4b59ydc+Ph8xI=
github.com/masterminds/semver v1.4.2/go.mod h1:s7KNT9fnd7edGzwwP7RBX4H0v/CYd5qdOLfkL1V75yg=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
@@ -73,11 +70,9 @@ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/wailsapp/webview v0.2.7 h1:fN5L5H9Oivg9IJPk7uaXQnjqB68Fny11ZWkIaTIZHmk=
github.com/wailsapp/webview v0.2.7/go.mod h1:XO9HJbKWokDxUYTWQEBCYg95n/To1v7PxvanDNVf8hY=
github.com/zserge/webview v0.0.0-20190123072648-16c93bcaeaeb/go.mod h1:a1CV8KR4Dd1eP2g+mEijGOp+HKczwdKHWyx0aPHKvo4=
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba h1:2DHfQOxcpWdGf5q5IzCUFPNvRX9Icf+09RvQK2VnJq0=
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 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
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=
@@ -86,14 +81,17 @@ golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR17
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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 h1:pf3XwC90UUdNPYWZdFjhGBE7DUFuK3Ct1zWmZ65QN30=
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 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
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/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=
gopkg.in/AlecAivazis/survey.v1 v1.8.4/go.mod h1:iBNOmqKz/NUbZx3bA+4hAGLRC7fSK7tgtVDT4tB22XA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 h1:0efs3hwEZhFKsCoP8l6dDB1AZWMgnEl3yWXWRZTOaEA=
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,4 +1,4 @@
package wails
package binding
import (
"bytes"
@@ -6,6 +6,8 @@ import (
"fmt"
"reflect"
"runtime"
"github.com/wailsapp/wails/lib/logger"
)
type boundFunction struct {
@@ -14,7 +16,7 @@ type boundFunction struct {
functionType reflect.Type
inputs []reflect.Type
returnTypes []reflect.Type
log *CustomLogger
log *logger.CustomLogger
hasErrorReturnType bool
}
@@ -30,7 +32,7 @@ func newBoundFunction(object interface{}) (*boundFunction, error) {
fullName: name,
function: objectValue,
functionType: objectType,
log: newCustomLogger(name),
log: logger.NewCustomLogger(name),
}
err := result.processParameters()
@@ -55,7 +57,7 @@ func (b *boundFunction) processParameters() error {
b.inputs[index] = param
typ := param
index := index
b.log.DebugFields("Input param", Fields{
b.log.DebugFields("Input param", logger.Fields{
"index": index,
"name": name,
"kind": kind,

View File

@@ -1,27 +1,33 @@
package wails
package binding
import "strings"
import "fmt"
import (
"fmt"
"strings"
type internalMethods struct{
log *CustomLogger
browser *RuntimeBrowser
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
"github.com/wailsapp/wails/runtime"
)
type internalMethods struct {
log *logger.CustomLogger
browser *runtime.Browser
}
func newInternalMethods() *internalMethods {
return &internalMethods{
log: newCustomLogger("InternalCall"),
browser: newRuntimeBrowser(),
log: logger.NewCustomLogger("InternalCall"),
browser: runtime.NewBrowser(),
}
}
func (i *internalMethods) processCall(callData *callData) (interface{}, error) {
func (i *internalMethods) processCall(callData *messages.CallData) (interface{}, error) {
if !strings.HasPrefix(callData.BindingName, ".wails.") {
return nil, fmt.Errorf("Invalid call signature '%s'", callData.BindingName)
}
// Strip prefix
var splitCall = strings.Split(callData.BindingName,".")[2:]
var splitCall = strings.Split(callData.BindingName, ".")[2:]
if len(splitCall) != 2 {
return nil, fmt.Errorf("Invalid call signature '%s'", callData.BindingName)
}
@@ -37,14 +43,14 @@ func (i *internalMethods) processCall(callData *callData) (interface{}, error) {
func (i *internalMethods) processBrowserCommand(command string, data interface{}) (interface{}, error) {
switch command {
case "OpenURL":
case "OpenURL":
url := data.(string)
// Strip string quotes. Credit: https://stackoverflow.com/a/44222648
if url[0] == '"' {
url = url[1:]
}
if i := len(url)-1; url[i] == '"' {
url = url[:i]
if i := len(url) - 1; url[i] == '"' {
url = url[:i]
}
i.log.Debugf("Calling Browser.OpenURL with '%s'", url)
return nil, i.browser.OpenURL(url)
@@ -54,12 +60,12 @@ func (i *internalMethods) processBrowserCommand(command string, data interface{}
if filename[0] == '"' {
filename = filename[1:]
}
if i := len(filename)-1; filename[i] == '"' {
filename = filename[:i]
if i := len(filename) - 1; filename[i] == '"' {
filename = filename[:i]
}
i.log.Debugf("Calling Browser.OpenFile with '%s'", filename)
return nil, i.browser.OpenFile(filename)
default:
return nil, fmt.Errorf("Unknown Browser command '%s'", command)
}
}
}

View File

@@ -1,47 +1,47 @@
package wails
package binding
import (
"fmt"
"reflect"
"unicode"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
)
/**
binding:
Name() // Full name (package+name)
Call(params)
**/
type bindingManager struct {
// Manager handles method binding
type Manager struct {
methods map[string]*boundMethod
functions map[string]*boundFunction
internalMethods *internalMethods
initMethods []*boundMethod
log *CustomLogger
renderer Renderer
runtime *Runtime // The runtime object to pass to bound structs
shutdownMethods []*boundMethod
log *logger.CustomLogger
renderer interfaces.Renderer
runtime interfaces.Runtime // The runtime object to pass to bound structs
objectsToBind []interface{}
bindPackageNames bool // Package name should be considered when binding
}
func newBindingManager() *bindingManager {
result := &bindingManager{
// NewManager creates a new Manager struct
func NewManager() interfaces.BindingManager {
result := &Manager{
methods: make(map[string]*boundMethod),
functions: make(map[string]*boundFunction),
log: newCustomLogger("Bind"),
log: logger.NewCustomLogger("Bind"),
internalMethods: newInternalMethods(),
}
return result
}
// Sets flag to indicate package names should be considered when binding
func (b *bindingManager) BindPackageNames() {
// BindPackageNames sets a flag to indicate package names should be considered when binding
func (b *Manager) BindPackageNames() {
b.bindPackageNames = true
}
func (b *bindingManager) start(renderer Renderer, runtime *Runtime) error {
// Start the binding manager
func (b *Manager) Start(renderer interfaces.Renderer, runtime interfaces.Runtime) error {
b.log.Info("Starting")
b.renderer = renderer
b.runtime = runtime
@@ -54,7 +54,7 @@ func (b *bindingManager) start(renderer Renderer, runtime *Runtime) error {
return err
}
func (b *bindingManager) initialise() error {
func (b *Manager) initialise() error {
var err error
// var binding *boundMethod
@@ -92,7 +92,7 @@ func (b *bindingManager) initialise() error {
}
// bind the given struct method
func (b *bindingManager) bindMethod(object interface{}) error {
func (b *Manager) bindMethod(object interface{}) error {
objectType := reflect.TypeOf(object)
baseName := objectType.String()
@@ -128,6 +128,9 @@ func (b *bindingManager) bindMethod(object interface{}) error {
if newMethod.isWailsInit {
b.log.Debugf("Detected WailsInit function: %s", fullMethodName)
b.initMethods = append(b.initMethods, newMethod)
} else if newMethod.isWailsShutdown {
b.log.Debugf("Detected WailsShutdown function: %s", fullMethodName)
b.shutdownMethods = append(b.shutdownMethods, newMethod)
} else {
// Save boundMethod
b.log.Infof("Bound Method: %s()", fullMethodName)
@@ -142,7 +145,7 @@ func (b *bindingManager) bindMethod(object interface{}) error {
}
// bind the given function object
func (b *bindingManager) bindFunction(object interface{}) error {
func (b *Manager) bindFunction(object interface{}) error {
newFunction, err := newBoundFunction(object)
if err != nil {
@@ -159,18 +162,18 @@ func (b *bindingManager) bindFunction(object interface{}) error {
return nil
}
// Save the given object to be bound at start time
func (b *bindingManager) bind(object interface{}) {
// Bind saves the given object to be bound at start time
func (b *Manager) Bind(object interface{}) {
// Store binding
b.objectsToBind = append(b.objectsToBind, object)
}
func (b *bindingManager) processInternalCall(callData *callData) (interface{}, error) {
func (b *Manager) processInternalCall(callData *messages.CallData) (interface{}, error) {
// Strip prefix
return b.internalMethods.processCall(callData)
}
func (b *bindingManager) processFunctionCall(callData *callData) (interface{}, error) {
func (b *Manager) processFunctionCall(callData *messages.CallData) (interface{}, error) {
// Return values
var result []reflect.Value
var err error
@@ -196,10 +199,14 @@ func (b *bindingManager) processFunctionCall(callData *callData) (interface{}, e
return nil, errorResult.Interface().(error)
}
}
return result[0].Interface(), nil
// fmt.Printf("result = '%+v'\n", result)
if len(result) > 0 {
return result[0].Interface(), nil
}
return nil, nil
}
func (b *bindingManager) processMethodCall(callData *callData) (interface{}, error) {
func (b *Manager) processMethodCall(callData *messages.CallData) (interface{}, error) {
// Return values
var result []reflect.Value
var err error
@@ -233,8 +240,8 @@ func (b *bindingManager) processMethodCall(callData *callData) (interface{}, err
return nil, nil
}
// process an incoming call request
func (b *bindingManager) processCall(callData *callData) (result interface{}, err error) {
// ProcessCall processes the given call request
func (b *Manager) ProcessCall(callData *messages.CallData) (result interface{}, err error) {
b.log.Debugf("Wanting to call %s", callData.BindingName)
// Determine if this is function call or method call by the number of
@@ -272,7 +279,7 @@ func (b *bindingManager) processCall(callData *callData) (result interface{}, er
// callWailsInitMethods calls all of the WailsInit methods that were
// registered with the runtime object
func (b *bindingManager) callWailsInitMethods() error {
func (b *Manager) callWailsInitMethods() error {
// Create reflect value for runtime object
runtimeValue := reflect.ValueOf(b.runtime)
params := []reflect.Value{runtimeValue}
@@ -289,3 +296,13 @@ func (b *bindingManager) callWailsInitMethods() error {
}
return nil
}
// Shutdown the binding manager
func (b *Manager) Shutdown() {
b.log.Debug("Shutdown called")
for _, method := range b.shutdownMethods {
b.log.Debugf("Calling Shutdown for method: %s", method.fullName)
method.call("[]")
}
b.log.Debug("Shutdown complete")
}

View File

@@ -1,10 +1,12 @@
package wails
package binding
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"github.com/wailsapp/wails/lib/logger"
)
type boundMethod struct {
@@ -13,9 +15,10 @@ type boundMethod struct {
method reflect.Value
inputs []reflect.Type
returnTypes []reflect.Type
log *CustomLogger
log *logger.CustomLogger
hasErrorReturnType bool // Indicates if there is an error return type
isWailsInit bool
isWailsShutdown bool
}
// Creates a new bound method based on the given method + type
@@ -27,7 +30,7 @@ func newBoundMethod(name string, fullName string, method reflect.Value, objectTy
}
// Setup logger
result.log = newCustomLogger(result.fullName)
result.log = logger.NewCustomLogger(result.fullName)
// Check if Parameters are valid
err := result.processParameters()
@@ -37,6 +40,11 @@ func newBoundMethod(name string, fullName string, method reflect.Value, objectTy
err = result.processWailsInit()
}
// Are we a WailsShutdown method?
if result.Name == "WailsShutdown" {
err = result.processWailsShutdown()
}
return result, err
}
@@ -57,7 +65,7 @@ func (b *boundMethod) processParameters() error {
b.inputs[index] = param
typ := param
index := index
b.log.DebugFields("Input param", Fields{
b.log.DebugFields("Input param", logger.Fields{
"index": index,
"name": name,
"kind": kind,
@@ -166,10 +174,10 @@ func (b *boundMethod) setInputValue(index int, typ reflect.Type, val interface{}
reflect.Map,
reflect.Ptr,
reflect.Slice:
logger.Debug("Converting nil to type")
b.log.Debug("Converting nil to type")
result = reflect.ValueOf(val).Convert(typ)
default:
logger.Debug("Cannot convert nil to type, returning error")
b.log.Debug("Cannot convert nil to type, returning error")
return reflect.Zero(typ), fmt.Errorf("Unable to use null value for parameter %d of method %s", index+1, b.fullName)
}
} else {
@@ -209,3 +217,20 @@ func (b *boundMethod) processWailsInit() error {
return nil
}
func (b *boundMethod) processWailsShutdown() error {
// We must have only 1 input, it must be *wails.Runtime
if len(b.inputs) != 0 {
return fmt.Errorf("Invalid WailsShutdown() definition. Expected 0 inputs, but got %d", len(b.inputs))
}
// We must have only 1 output, it must be error
if len(b.returnTypes) != 0 {
return fmt.Errorf("Invalid WailsShutdown() definition. Expected 0 return types, but got %d", len(b.returnTypes))
}
// We are indeed a wails Shutdown method
b.isWailsShutdown = true
return nil
}

View File

@@ -1,31 +1,37 @@
package wails
package event
import (
"fmt"
"sync"
"time"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
)
// eventManager handles and processes events
type eventManager struct {
incomingEvents chan *eventData
// Manager handles and processes events
type Manager struct {
incomingEvents chan *messages.EventData
listeners map[string][]*eventListener
exit bool
log *CustomLogger
renderer Renderer // Messages will be dispatched to the frontend
running bool
log *logger.CustomLogger
renderer interfaces.Renderer // Messages will be dispatched to the frontend
wg sync.WaitGroup
}
// newEventManager creates a new event manager with a 100 event buffer
func newEventManager() *eventManager {
return &eventManager{
incomingEvents: make(chan *eventData, 100),
// NewManager creates a new event manager with a 100 event buffer
func NewManager() interfaces.EventManager {
return &Manager{
incomingEvents: make(chan *messages.EventData, 100),
listeners: make(map[string][]*eventListener),
exit: false,
log: newCustomLogger("Events"),
running: false,
log: logger.NewCustomLogger("Events"),
}
}
// PushEvent places the given event on to the event queue
func (e *eventManager) PushEvent(eventData *eventData) {
func (e *Manager) PushEvent(eventData *messages.EventData) {
e.incomingEvents <- eventData
}
@@ -40,7 +46,7 @@ type eventListener struct {
}
// Creates a new event listener from the given callback function
func (e *eventManager) addEventListener(eventName string, callback func(...interface{}), counter int) error {
func (e *Manager) addEventListener(eventName string, callback func(...interface{}), counter int) error {
// Sanity check inputs
if callback == nil {
@@ -65,37 +71,37 @@ func (e *eventManager) addEventListener(eventName string, callback func(...inter
return nil
}
func (e *eventManager) On(eventName string, callback func(...interface{})) {
// On adds a listener for the given event
func (e *Manager) On(eventName string, callback func(...interface{})) {
// Add a persistent eventListener (counter = 0)
e.addEventListener(eventName, callback, 0)
}
// Emit broadcasts the given event to the subscribed listeners
func (e *eventManager) Emit(eventName string, optionalData ...interface{}) {
e.incomingEvents <- &eventData{Name: eventName, Data: optionalData}
func (e *Manager) Emit(eventName string, optionalData ...interface{}) {
e.incomingEvents <- &messages.EventData{Name: eventName, Data: optionalData}
}
// Starts the event manager's queue processing
func (e *eventManager) start(renderer Renderer) {
// Start the event manager's queue processing
func (e *Manager) Start(renderer interfaces.Renderer) {
e.log.Info("Starting")
// Store renderer
e.renderer = renderer
// Set up waitgroup so we can wait for goroutine to start
var wg sync.WaitGroup
wg.Add(1)
// Set up waitgroup so we can wait for goroutine to quit
e.running = true
e.wg.Add(1)
// Run main loop in separate goroutine
go func() {
wg.Done()
e.log.Info("Listening")
for e.exit == false {
for e.running {
// TODO: Listen for application exit
select {
case event := <-e.incomingEvents:
e.log.DebugFields("Got Event", Fields{
e.log.DebugFields("Got Event", logger.Fields{
"data": event.Data,
"name": event.Name,
})
@@ -135,14 +141,18 @@ func (e *eventManager) start(renderer Renderer) {
}
}
}
default:
time.Sleep(1 * time.Millisecond)
}
}
e.wg.Done()
}()
// Wait for goroutine to start
wg.Wait()
}
func (e *eventManager) stop() {
e.exit = true
// Shutdown is called when exiting the Application
func (e *Manager) Shutdown() {
e.log.Debug("Shutting Down")
e.running = false
e.log.Debug("Waiting for main loop to exit")
e.wg.Wait()
}

View File

@@ -0,0 +1,14 @@
package interfaces
// AppConfig is the application config interface
type AppConfig interface {
GetWidth() int
GetHeight() int
GetTitle() string
GetResizable() bool
GetDefaultHTML() string
GetDisableInspector() bool
GetColour() string
GetCSS() string
GetJS() string
}

View File

@@ -0,0 +1,11 @@
package interfaces
import "github.com/wailsapp/wails/lib/messages"
// BindingManager is the binding manager interface
type BindingManager interface {
Bind(object interface{})
Start(renderer Renderer, runtime Runtime) error
ProcessCall(callData *messages.CallData) (result interface{}, err error)
Shutdown()
}

View File

@@ -0,0 +1,12 @@
package interfaces
import "github.com/wailsapp/wails/lib/messages"
// EventManager is the event manager interface
type EventManager interface {
PushEvent(*messages.EventData)
Emit(eventName string, optionalData ...interface{})
On(eventName string, callback func(...interface{}))
Start(Renderer)
Shutdown()
}

View File

@@ -0,0 +1,9 @@
package interfaces
// IPCManager is the event manager interface
type IPCManager interface {
BindRenderer(Renderer)
Dispatch(message string)
Start(eventManager EventManager, bindingManager BindingManager)
Shutdown()
}

View File

@@ -1,16 +1,21 @@
package wails
package interfaces
import (
"github.com/wailsapp/wails/lib/messages"
)
// Renderer is an interface describing a Wails target to render the app to
type Renderer interface {
Initialise(*AppConfig, *ipcManager, *eventManager) error
Initialise(AppConfig, IPCManager, EventManager) error
Run() error
EnableConsole()
// Binding
NewBinding(bindingName string) error
Callback(data string) error
// Events
NotifyEvent(eventData *eventData) error
NotifyEvent(eventData *messages.EventData) error
// Dialog Runtime
SelectFile() string

View File

@@ -0,0 +1,4 @@
package interfaces
// Runtime interface
type Runtime interface {}

View File

@@ -1,13 +1,10 @@
package wails
package ipc
import (
"fmt"
)
type callData struct {
BindingName string `json:"bindingName"`
Data string `json:"data,omitempty"`
}
"github.com/wailsapp/wails/lib/messages"
)
func init() {
messageProcessors["call"] = processCallData
@@ -15,7 +12,7 @@ func init() {
func processCallData(message *ipcMessage) (*ipcMessage, error) {
var payload callData
var payload messages.CallData
// Decode binding call data
payloadMap := message.Payload.(map[string]interface{})

View File

@@ -1,13 +1,10 @@
package wails
package ipc
import (
"encoding/json"
)
type eventData struct {
Name string `json:"name"`
Data interface{} `json:"data"`
}
"github.com/wailsapp/wails/lib/messages"
)
// Register the message handler
func init() {
@@ -19,7 +16,7 @@ func processEventData(message *ipcMessage) (*ipcMessage, error) {
// TODO: Is it worth double checking this is actually an event message,
// even though that's done by the caller?
var payload eventData
var payload messages.EventData
// Decode event data
payloadMap := message.Payload.(map[string]interface{})

View File

@@ -1,9 +1,6 @@
package wails
package ipc
type logData struct {
Level string `json:"level"`
Message string `json:"string"`
}
import "github.com/wailsapp/wails/lib/messages"
// Register the message handler
func init() {
@@ -13,7 +10,7 @@ func init() {
// This processes the given log message
func processLogData(message *ipcMessage) (*ipcMessage, error) {
var payload logData
var payload messages.LogData
// Decode event data
payloadMap := message.Payload.(map[string]interface{})

View File

@@ -1,35 +1,46 @@
package wails
package ipc
import (
"fmt"
"sync"
"time"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
)
type ipcManager struct {
renderer Renderer // The renderer
// Manager manages the IPC subsystem
type Manager struct {
renderer interfaces.Renderer // The renderer
messageQueue chan *ipcMessage
// quitChannel chan struct{}
quitChannel chan struct{}
// signals chan os.Signal
log *CustomLogger
eventManager *eventManager
bindingManager *bindingManager
log *logger.CustomLogger
eventManager interfaces.EventManager
bindingManager interfaces.BindingManager
running bool
wg sync.WaitGroup
}
func newIPCManager() *ipcManager {
result := &ipcManager{
// NewManager creates a new IPC Manager
func NewManager() interfaces.IPCManager {
result := &Manager{
messageQueue: make(chan *ipcMessage, 100),
// quitChannel: make(chan struct{}),
quitChannel: make(chan struct{}),
// signals: make(chan os.Signal, 1),
log: newCustomLogger("IPC"),
log: logger.NewCustomLogger("IPC"),
}
return result
}
// Sets the renderer, returns the dispatch function
func (i *ipcManager) bindRenderer(renderer Renderer) {
// BindRenderer sets the renderer, returns the dispatch function
func (i *Manager) BindRenderer(renderer interfaces.Renderer) {
i.renderer = renderer
}
func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingManager) {
// Start the IPC Manager
func (i *Manager) Start(eventManager interfaces.EventManager, bindingManager interfaces.BindingManager) {
// Store manager references
i.eventManager = eventManager
@@ -37,41 +48,44 @@ func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingMa
i.log.Info("Starting")
// signal.Notify(manager.signals, os.Interrupt)
i.running = true
// Keep track of this goroutine
i.wg.Add(1)
go func() {
running := true
for running {
for i.running {
select {
case incomingMessage := <-i.messageQueue:
i.log.DebugFields("Processing message", Fields{
i.log.DebugFields("Processing message", logger.Fields{
"1D": &incomingMessage,
})
switch incomingMessage.Type {
case "call":
callData := incomingMessage.Payload.(*callData)
i.log.DebugFields("Processing call", Fields{
callData := incomingMessage.Payload.(*messages.CallData)
i.log.DebugFields("Processing call", logger.Fields{
"1D": &incomingMessage,
"bindingName": callData.BindingName,
"data": callData.Data,
})
go func() {
result, err := bindingManager.processCall(callData)
i.log.DebugFields("processed call", Fields{"result": result, "err": err})
result, err := bindingManager.ProcessCall(callData)
i.log.DebugFields("processed call", logger.Fields{"result": result, "err": err})
if err != nil {
incomingMessage.ReturnError(err.Error())
} else {
incomingMessage.ReturnSuccess(result)
}
i.log.DebugFields("Finished processing call", Fields{
i.log.DebugFields("Finished processing call", logger.Fields{
"1D": &incomingMessage,
})
}()
case "event":
// Extract event data
eventData := incomingMessage.Payload.(*eventData)
eventData := incomingMessage.Payload.(*messages.EventData)
// Log
i.log.DebugFields("Processing event", Fields{
i.log.DebugFields("Processing event", logger.Fields{
"name": eventData.Name,
"data": eventData.Data,
})
@@ -80,24 +94,24 @@ func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingMa
i.eventManager.PushEvent(eventData)
// Log
i.log.DebugFields("Finished processing event", Fields{
i.log.DebugFields("Finished processing event", logger.Fields{
"name": eventData.Name,
})
case "log":
logdata := incomingMessage.Payload.(*logData)
logdata := incomingMessage.Payload.(*messages.LogData)
switch logdata.Level {
case "info":
logger.Info(logdata.Message)
logger.GlobalLogger.Info(logdata.Message)
case "debug":
logger.Debug(logdata.Message)
logger.GlobalLogger.Debug(logdata.Message)
case "warning":
logger.Warning(logdata.Message)
logger.GlobalLogger.Warn(logdata.Message)
case "error":
logger.Error(logdata.Message)
logger.GlobalLogger.Error(logdata.Message)
case "fatal":
logger.Fatal(logdata.Message)
logger.GlobalLogger.Fatal(logdata.Message)
default:
i.log.ErrorFields("Invalid log level sent", Fields{
logger.ErrorFields("Invalid log level sent", logger.Fields{
"level": logdata.Level,
"message": logdata.Message,
})
@@ -107,25 +121,22 @@ func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingMa
}
// Log
i.log.DebugFields("Finished processing message", Fields{
i.log.DebugFields("Finished processing message", logger.Fields{
"1D": &incomingMessage,
})
// case <-manager.quitChannel:
// Debug("[MessageQueue] Quit caught")
// running = false
// case <-manager.signals:
// Debug("[MessageQueue] Signal caught")
// running = false
default:
time.Sleep(1 * time.Millisecond)
}
}
i.log.Debug("Stopping")
i.wg.Done()
}()
}
// Dispatch receives JSON encoded messages from the renderer.
// It processes the message to ensure that it is valid and places
// the processed message on the message queue
func (i *ipcManager) Dispatch(message string) {
func (i *Manager) Dispatch(message string) {
// Create a new IPC Message
incomingMessage, err := newIPCMessage(message, i.SendResponse)
@@ -148,7 +159,7 @@ func (i *ipcManager) Dispatch(message string) {
}
// SendResponse sends the given response back to the frontend
func (i *ipcManager) SendResponse(response *ipcResponse) error {
func (i *Manager) SendResponse(response *ipcResponse) error {
// Serialise the Message
data, err := response.Serialise()
@@ -160,3 +171,11 @@ func (i *ipcManager) SendResponse(response *ipcResponse) error {
// Call back to the front end
return i.renderer.Callback(data)
}
// Shutdown is called when exiting the Application
func (i *Manager) Shutdown() {
i.log.Debug("Shutdown called")
i.running = false
i.log.Debug("Waiting of main loop shutdown")
i.wg.Wait()
}

View File

@@ -1,4 +1,4 @@
package wails
package ipc
import (
"encoding/json"

View File

@@ -1,4 +1,4 @@
package wails
package ipc
import (
"encoding/hex"

View File

@@ -1,4 +1,4 @@
package wails
package logger
// CustomLogger is a wrapper object to logrus
type CustomLogger struct {
@@ -6,7 +6,8 @@ type CustomLogger struct {
errorOnly bool
}
func newCustomLogger(prefix string) *CustomLogger {
// NewCustomLogger creates a new custom logger with the given prefix
func NewCustomLogger(prefix string) *CustomLogger {
return &CustomLogger{
prefix: "[" + prefix + "] ",
}
@@ -14,90 +15,90 @@ func newCustomLogger(prefix string) *CustomLogger {
// Info level message
func (c *CustomLogger) Info(message string) {
logger.Info(c.prefix + message)
GlobalLogger.Info(c.prefix + message)
}
// Infof - formatted message
func (c *CustomLogger) Infof(message string, args ...interface{}) {
logger.Infof(c.prefix+message, args...)
GlobalLogger.Infof(c.prefix+message, args...)
}
// InfoFields - message with fields
func (c *CustomLogger) InfoFields(message string, fields Fields) {
logger.WithFields(map[string]interface{}(fields)).Info(c.prefix + message)
GlobalLogger.WithFields(map[string]interface{}(fields)).Info(c.prefix + message)
}
// Debug level message
func (c *CustomLogger) Debug(message string) {
logger.Debug(c.prefix + message)
GlobalLogger.Debug(c.prefix + message)
}
// Debugf - formatted message
func (c *CustomLogger) Debugf(message string, args ...interface{}) {
logger.Debugf(c.prefix+message, args...)
GlobalLogger.Debugf(c.prefix+message, args...)
}
// DebugFields - message with fields
func (c *CustomLogger) DebugFields(message string, fields Fields) {
logger.WithFields(map[string]interface{}(fields)).Debug(c.prefix + message)
GlobalLogger.WithFields(map[string]interface{}(fields)).Debug(c.prefix + message)
}
// Warn level message
func (c *CustomLogger) Warn(message string) {
logger.Warn(c.prefix + message)
GlobalLogger.Warn(c.prefix + message)
}
// Warnf - formatted message
func (c *CustomLogger) Warnf(message string, args ...interface{}) {
logger.Warnf(c.prefix+message, args...)
GlobalLogger.Warnf(c.prefix+message, args...)
}
// WarnFields - message with fields
func (c *CustomLogger) WarnFields(message string, fields Fields) {
logger.WithFields(map[string]interface{}(fields)).Warn(c.prefix + message)
GlobalLogger.WithFields(map[string]interface{}(fields)).Warn(c.prefix + message)
}
// Error level message
func (c *CustomLogger) Error(message string) {
logger.Error(c.prefix + message)
GlobalLogger.Error(c.prefix + message)
}
// Errorf - formatted message
func (c *CustomLogger) Errorf(message string, args ...interface{}) {
logger.Errorf(c.prefix+message, args...)
GlobalLogger.Errorf(c.prefix+message, args...)
}
// ErrorFields - message with fields
func (c *CustomLogger) ErrorFields(message string, fields Fields) {
logger.WithFields(map[string]interface{}(fields)).Error(c.prefix + message)
GlobalLogger.WithFields(map[string]interface{}(fields)).Error(c.prefix + message)
}
// Fatal level message
func (c *CustomLogger) Fatal(message string) {
logger.Fatal(c.prefix + message)
GlobalLogger.Fatal(c.prefix + message)
}
// Fatalf - formatted message
func (c *CustomLogger) Fatalf(message string, args ...interface{}) {
logger.Fatalf(c.prefix+message, args...)
GlobalLogger.Fatalf(c.prefix+message, args...)
}
// FatalFields - message with fields
func (c *CustomLogger) FatalFields(message string, fields Fields) {
logger.WithFields(map[string]interface{}(fields)).Fatal(c.prefix + message)
GlobalLogger.WithFields(map[string]interface{}(fields)).Fatal(c.prefix + message)
}
// Panic level message
func (c *CustomLogger) Panic(message string) {
logger.Panic(c.prefix + message)
GlobalLogger.Panic(c.prefix + message)
}
// Panicf - formatted message
func (c *CustomLogger) Panicf(message string, args ...interface{}) {
logger.Panicf(c.prefix+message, args...)
GlobalLogger.Panicf(c.prefix+message, args...)
}
// PanicFields - message with fields
func (c *CustomLogger) PanicFields(message string, fields Fields) {
logger.WithFields(map[string]interface{}(fields)).Panic(c.prefix + message)
GlobalLogger.WithFields(map[string]interface{}(fields)).Panic(c.prefix + message)
}

47
lib/logger/log.go Normal file
View File

@@ -0,0 +1,47 @@
package logger
import (
"os"
"strings"
"github.com/sirupsen/logrus"
)
// GlobalLogger is the global logger
var GlobalLogger = logrus.New()
// Fields is used by the customLogger object to output
// fields along with a message
type Fields map[string]interface{}
// Default options for the global logger
func init() {
GlobalLogger.SetOutput(os.Stdout)
GlobalLogger.SetLevel(logrus.DebugLevel)
}
// ErrorFields is a helper for logging fields to the global logger
func ErrorFields(message string, fields Fields) {
GlobalLogger.WithFields(map[string]interface{}(fields)).Error(message)
}
// SetLogLevel sets the log level to the given level
func SetLogLevel(level string) {
switch strings.ToLower(level) {
case "info":
GlobalLogger.SetLevel(logrus.InfoLevel)
case "debug":
GlobalLogger.SetLevel(logrus.DebugLevel)
case "warn":
GlobalLogger.SetLevel(logrus.WarnLevel)
case "error":
GlobalLogger.SetLevel(logrus.ErrorLevel)
case "fatal":
GlobalLogger.SetLevel(logrus.FatalLevel)
case "panic":
GlobalLogger.SetLevel(logrus.PanicLevel)
default:
GlobalLogger.SetLevel(logrus.DebugLevel)
GlobalLogger.Warnf("Log level '%s' not recognised. Setting to Debug.", level)
}
}

7
lib/messages/calldata.go Normal file
View File

@@ -0,0 +1,7 @@
package messages
// CallData represents a call to a Go function/method
type CallData struct {
BindingName string `json:"bindingName"`
Data string `json:"data,omitempty"`
}

View File

@@ -0,0 +1,7 @@
package messages
// EventData represents an event sent from the frontend
type EventData struct {
Name string `json:"name"`
Data interface{} `json:"data"`
}

7
lib/messages/logdata.go Normal file
View File

@@ -0,0 +1,7 @@
package messages
// LogData represents a call to log from the frontend
type LogData struct {
Level string `json:"level"`
Message string `json:"string"`
}

View File

@@ -1,4 +1,4 @@
package wails
package renderer
import (
"encoding/json"
@@ -10,6 +10,9 @@ import (
"github.com/dchest/htmlmin"
"github.com/gorilla/websocket"
"github.com/leaanthony/mewn"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
)
type messageType int
@@ -28,17 +31,17 @@ func (m messageType) toString() string {
return [...]string{"j", "s", "h", "n", "b", "c", "w"}[m]
}
// Headless is a backend that opens a local web server
// Bridge is a backend that opens a local web server
// and renders the files over a websocket
type Headless struct {
type Bridge struct {
// Common
log *CustomLogger
ipcManager *ipcManager
appConfig *AppConfig
eventManager *eventManager
log *logger.CustomLogger
ipcManager interfaces.IPCManager
appConfig interfaces.AppConfig
eventManager interfaces.EventManager
bindingCache []string
// Headless specific
// Bridge specific
initialisationJS []string
server *http.Server
theConnection *websocket.Conn
@@ -47,17 +50,17 @@ type Headless struct {
lock sync.Mutex
}
// Initialise the Headless Renderer
func (h *Headless) Initialise(appConfig *AppConfig, ipcManager *ipcManager, eventManager *eventManager) error {
// Initialise the Bridge Renderer
func (h *Bridge) Initialise(appConfig interfaces.AppConfig, ipcManager interfaces.IPCManager, eventManager interfaces.EventManager) error {
h.ipcManager = ipcManager
h.appConfig = appConfig
h.eventManager = eventManager
ipcManager.bindRenderer(h)
h.log = newCustomLogger("Bridge")
ipcManager.BindRenderer(h)
h.log = logger.NewCustomLogger("Bridge")
return nil
}
func (h *Headless) evalJS(js string, mtype messageType) error {
func (h *Bridge) evalJS(js string, mtype messageType) error {
message := mtype.toString() + js
@@ -71,7 +74,11 @@ func (h *Headless) evalJS(js string, mtype messageType) error {
return nil
}
func (h *Headless) injectCSS(css string) {
// EnableConsole not needed for bridge!
func (h *Bridge) EnableConsole() {
}
func (h *Bridge) injectCSS(css string) {
// Minify css to overcome issues in the browser with carriage returns
minified, err := htmlmin.Minify([]byte(css), &htmlmin.Options{
MinifyStyles: true,
@@ -83,11 +90,11 @@ func (h *Headless) injectCSS(css string) {
minifiedCSS = strings.Replace(minifiedCSS, "\\", "\\\\", -1)
minifiedCSS = strings.Replace(minifiedCSS, "'", "\\'", -1)
minifiedCSS = strings.Replace(minifiedCSS, "\n", " ", -1)
inject := fmt.Sprintf("wails._.injectCSS('%s')", minifiedCSS)
inject := fmt.Sprintf("wails._.InjectCSS('%s')", minifiedCSS)
h.evalJS(inject, cssMessage)
}
func (h *Headless) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
func (h *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
if err != nil {
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
@@ -102,7 +109,7 @@ func (h *Headless) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
go h.start(conn)
}
func (h *Headless) sendMessage(conn *websocket.Conn, msg string) {
func (h *Bridge) sendMessage(conn *websocket.Conn, msg string) {
h.lock.Lock()
defer h.lock.Unlock()
@@ -112,12 +119,12 @@ func (h *Headless) sendMessage(conn *websocket.Conn, msg string) {
}
}
func (h *Headless) start(conn *websocket.Conn) {
func (h *Bridge) start(conn *websocket.Conn) {
// set external.invoke
h.log.Infof("Connected to frontend.")
wailsRuntime := mewn.String("./wailsruntimeassets/default/wails.min.js")
wailsRuntime := mewn.String("../../runtime/assets/wails.js")
h.evalJS(wailsRuntime, wailsRuntimeMessage)
// Inject bindings
@@ -144,8 +151,8 @@ func (h *Headless) start(conn *websocket.Conn) {
}
}
// Run the app in headless mode!
func (h *Headless) Run() error {
// Run the app in Bridge mode!
func (h *Bridge) Run() error {
h.server = &http.Server{Addr: ":34115"}
http.HandleFunc("/bridge", h.wsBridgeHandler)
@@ -153,52 +160,52 @@ func (h *Headless) Run() error {
h.log.Info("The frontend will connect automatically.")
err := h.server.ListenAndServe()
if err != nil {
if err != nil && err != http.ErrServerClosed {
h.log.Fatal(err.Error())
}
return err
}
// NewBinding creates a new binding with the frontend
func (h *Headless) NewBinding(methodName string) error {
func (h *Bridge) NewBinding(methodName string) error {
h.bindingCache = append(h.bindingCache, methodName)
return nil
}
// SelectFile is unsupported for Headless but required
// SelectFile is unsupported for Bridge but required
// for the Renderer interface
func (h *Headless) SelectFile() string {
func (h *Bridge) SelectFile() string {
h.log.Warn("SelectFile() unsupported in bridge mode")
return ""
}
// SelectDirectory is unsupported for Headless but required
// SelectDirectory is unsupported for Bridge but required
// for the Renderer interface
func (h *Headless) SelectDirectory() string {
func (h *Bridge) SelectDirectory() string {
h.log.Warn("SelectDirectory() unsupported in bridge mode")
return ""
}
// SelectSaveFile is unsupported for Headless but required
// SelectSaveFile is unsupported for Bridge but required
// for the Renderer interface
func (h *Headless) SelectSaveFile() string {
func (h *Bridge) SelectSaveFile() string {
h.log.Warn("SelectSaveFile() unsupported in bridge mode")
return ""
}
// Callback sends a callback to the frontend
func (h *Headless) Callback(data string) error {
func (h *Bridge) Callback(data string) error {
return h.evalJS(data, callbackMessage)
}
// NotifyEvent notifies the frontend of an event
func (h *Headless) NotifyEvent(event *eventData) error {
func (h *Bridge) NotifyEvent(event *messages.EventData) error {
// Look out! Nils about!
var err error
if event == nil {
err = fmt.Errorf("Sent nil event to renderer.webViewRenderer")
logger.Error(err)
h.log.Error(err.Error())
return err
}
@@ -215,37 +222,41 @@ func (h *Headless) NotifyEvent(event *eventData) error {
}
}
message := fmt.Sprintf("window.wails._.notify('%s','%s')", event.Name, data)
message := fmt.Sprintf("window.wails._.Notify('%s','%s')", event.Name, data)
return h.evalJS(message, notifyMessage)
}
// SetColour is unsupported for Headless but required
// SetColour is unsupported for Bridge but required
// for the Renderer interface
func (h *Headless) SetColour(colour string) error {
h.log.WarnFields("SetColour ignored for headless more", Fields{"col": colour})
func (h *Bridge) SetColour(colour string) error {
h.log.WarnFields("SetColour ignored for Bridge more", logger.Fields{"col": colour})
return nil
}
// Fullscreen is unsupported for Headless but required
// Fullscreen is unsupported for Bridge but required
// for the Renderer interface
func (h *Headless) Fullscreen() {
func (h *Bridge) Fullscreen() {
h.log.Warn("Fullscreen() unsupported in bridge mode")
}
// UnFullscreen is unsupported for Headless but required
// UnFullscreen is unsupported for Bridge but required
// for the Renderer interface
func (h *Headless) UnFullscreen() {
func (h *Bridge) UnFullscreen() {
h.log.Warn("UnFullscreen() unsupported in bridge mode")
}
// SetTitle is currently unsupported for Headless but required
// SetTitle is currently unsupported for Bridge but required
// for the Renderer interface
func (h *Headless) SetTitle(title string) {
h.log.WarnFields("SetTitle() unsupported in bridge mode", Fields{"title": title})
func (h *Bridge) SetTitle(title string) {
h.log.WarnFields("SetTitle() unsupported in bridge mode", logger.Fields{"title": title})
}
// Close is unsupported for Headless but required
// Close is unsupported for Bridge but required
// for the Renderer interface
func (h *Headless) Close() {
h.log.Warn("Close() unsupported in bridge mode")
func (h *Bridge) Close() {
h.log.Debug("Shutting down")
err := h.server.Close()
if err != nil {
h.log.Errorf(err.Error())
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,53 +1,63 @@
package wails
package renderer
import (
"encoding/json"
"fmt"
"math/rand"
"strings"
"sync"
"time"
"github.com/go-playground/colors"
"github.com/leaanthony/mewn"
"github.com/wailsapp/webview"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
wv "github.com/wailsapp/wails/lib/renderer/webview"
)
// Window defines the main application window
// WebView defines the main webview application window
// Default values in []
type webViewRenderer struct {
window webview.WebView // The webview object
ipc *ipcManager
log *CustomLogger
config *AppConfig
eventManager *eventManager
bindingCache []string
type WebView struct {
window wv.WebView // The webview object
ipc interfaces.IPCManager
log *logger.CustomLogger
config interfaces.AppConfig
eventManager interfaces.EventManager
bindingCache []string
enableConsole bool
}
// NewWebView returns a new WebView struct
func NewWebView() *WebView {
return &WebView{}
}
// Initialise sets up the WebView
func (w *webViewRenderer) Initialise(config *AppConfig, ipc *ipcManager, eventManager *eventManager) error {
func (w *WebView) Initialise(config interfaces.AppConfig, ipc interfaces.IPCManager, eventManager interfaces.EventManager) error {
// Store reference to eventManager
w.eventManager = eventManager
// Set up logger
w.log = newCustomLogger("WebView")
w.log = logger.NewCustomLogger("WebView")
// Set up the dispatcher function
w.ipc = ipc
ipc.bindRenderer(w)
ipc.BindRenderer(w)
// Save the config
w.config = config
// Create the WebView instance
w.window = webview.NewWebview(webview.Settings{
Width: config.Width,
Height: config.Height,
Title: config.Title,
Resizable: config.Resizable,
URL: config.defaultHTML,
Debug: !config.DisableInspector,
ExternalInvokeCallback: func(_ webview.WebView, message string) {
w.window = wv.NewWebview(wv.Settings{
Width: config.GetWidth(),
Height: config.GetHeight(),
Title: config.GetTitle(),
Resizable: config.GetResizable(),
URL: config.GetDefaultHTML(),
Debug: !config.GetDisableInspector(),
ExternalInvokeCallback: func(_ wv.WebView, message string) {
w.ipc.Dispatch(message)
},
})
@@ -55,7 +65,7 @@ func (w *webViewRenderer) Initialise(config *AppConfig, ipc *ipcManager, eventMa
// SignalManager.OnExit(w.Exit)
// Set colour
err := w.SetColour(config.Colour)
err := w.SetColour(config.GetColour())
if err != nil {
return err
}
@@ -64,7 +74,8 @@ func (w *webViewRenderer) Initialise(config *AppConfig, ipc *ipcManager, eventMa
return nil
}
func (w *webViewRenderer) SetColour(colour string) error {
// SetColour sets the window colour
func (w *WebView) SetColour(colour string) error {
color, err := colors.Parse(colour)
if err != nil {
return err
@@ -80,12 +91,12 @@ func (w *webViewRenderer) SetColour(colour string) error {
// evalJS evaluates the given js in the WebView
// I should rename this to evilJS lol
func (w *webViewRenderer) evalJS(js string) error {
func (w *WebView) evalJS(js string) error {
outputJS := fmt.Sprintf("%.45s", js)
if len(js) > 45 {
outputJS += "..."
}
w.log.DebugFields("Eval", Fields{"js": outputJS})
w.log.DebugFields("Eval", logger.Fields{"js": outputJS})
//
w.window.Dispatch(func() {
w.window.Eval(js)
@@ -93,12 +104,26 @@ func (w *webViewRenderer) evalJS(js string) error {
return nil
}
// EnableConsole enables the console!
func (w *WebView) EnableConsole() {
w.enableConsole = true
}
// Escape the Javascripts!
func escapeJS(js string) (string, error) {
result := strings.Replace(js, "\\", "\\\\", -1)
result = strings.Replace(result, "'", "\\'", -1)
result = strings.Replace(result, "\n", "\\n", -1)
return result, nil
}
// evalJSSync evaluates the given js in the WebView synchronously
// Do not call this from the main thread or you'll nuke your app because
// you won't get the callback.
func (w *webViewRenderer) evalJSSync(js string) error {
func (w *WebView) evalJSSync(js string) error {
minified, err := escapeJS(js)
if err != nil {
return err
}
@@ -107,7 +132,7 @@ func (w *webViewRenderer) evalJSSync(js string) error {
if len(js) > 45 {
outputJS += "..."
}
w.log.DebugFields("EvalSync", Fields{"js": outputJS})
w.log.DebugFields("EvalSync", logger.Fields{"js": outputJS})
ID := fmt.Sprintf("syncjs:%d:%d", time.Now().Unix(), rand.Intn(9999))
var wg sync.WaitGroup
@@ -122,7 +147,7 @@ func (w *webViewRenderer) evalJSSync(js string) error {
wg.Done()
exit = true
})
command := fmt.Sprintf("wails._.addScript('%s', '%s')", minified, ID)
command := fmt.Sprintf("wails._.AddScript('%s', '%s')", minified, ID)
w.window.Dispatch(func() {
w.window.Eval(command)
})
@@ -137,24 +162,31 @@ func (w *webViewRenderer) evalJSSync(js string) error {
}
// injectCSS adds the given CSS to the WebView
func (w *webViewRenderer) injectCSS(css string) {
func (w *WebView) injectCSS(css string) {
w.window.Dispatch(func() {
w.window.InjectCSS(css)
})
}
// Quit the window
func (w *webViewRenderer) Exit() {
// Exit closes the window
func (w *WebView) Exit() {
w.window.Exit()
}
// Run the window main loop
func (w *webViewRenderer) Run() error {
func (w *WebView) Run() error {
w.log.Info("Run()")
w.log.Info("Running...")
// Inject firebug in debug mode on Windows
if w.enableConsole {
w.log.Debug("Enabling Wails console")
console := mewn.String("../../runtime/assets/console.js")
w.evalJS(console)
}
// Runtime assets
wailsRuntime := mewn.String("./wailsruntimeassets/default/wails.min.js")
wailsRuntime := mewn.String("../../runtime/assets/wails.js")
w.evalJS(wailsRuntime)
// Ping the wait channel when the wails runtime is loaded
@@ -168,38 +200,30 @@ func (w *webViewRenderer) Run() error {
w.evalJSSync(binding)
}
// // Inject Framework
// if w.frameworkJS != "" {
// w.evalJSSync(w.frameworkJS)
// }
// if w.frameworkCSS != "" {
// w.injectCSS(w.frameworkCSS)
// }
// Inject user CSS
if w.config.CSS != "" {
outputCSS := fmt.Sprintf("%.45s", w.config.CSS)
if w.config.GetCSS() != "" {
outputCSS := fmt.Sprintf("%.45s", w.config.GetCSS())
if len(outputCSS) > 45 {
outputCSS += "..."
}
w.log.DebugFields("Inject User CSS", Fields{"css": outputCSS})
w.injectCSS(w.config.CSS)
w.log.DebugFields("Inject User CSS", logger.Fields{"css": outputCSS})
w.injectCSS(w.config.GetCSS())
} else {
// Use default wails css
w.log.Debug("Injecting Default Wails CSS")
defaultCSS := mewn.String("./wailsruntimeassets/default/wails.css")
defaultCSS := mewn.String("../../runtime/assets/wails.css")
w.injectCSS(defaultCSS)
}
// Inject user JS
if w.config.JS != "" {
outputJS := fmt.Sprintf("%.45s", w.config.JS)
if w.config.GetJS() != "" {
outputJS := fmt.Sprintf("%.45s", w.config.GetJS())
if len(outputJS) > 45 {
outputJS += "..."
}
w.log.DebugFields("Inject User JS", Fields{"js": outputJS})
w.evalJSSync(w.config.JS)
w.log.DebugFields("Inject User JS", logger.Fields{"js": outputJS})
w.evalJSSync(w.config.GetJS())
}
// Emit that everything is loaded and ready
@@ -213,14 +237,15 @@ func (w *webViewRenderer) Run() error {
return nil
}
// Binds the given method name with the front end
func (w *webViewRenderer) NewBinding(methodName string) error {
objectCode := fmt.Sprintf("window.wails._.newBinding('%s');", methodName)
// NewBinding registers a new binding with the frontend
func (w *WebView) NewBinding(methodName string) error {
objectCode := fmt.Sprintf("window.wails._.NewBinding('%s');", methodName)
w.bindingCache = append(w.bindingCache, objectCode)
return nil
}
func (w *webViewRenderer) SelectFile() string {
// SelectFile opens a dialog that allows the user to select a file
func (w *WebView) SelectFile() string {
var result string
// We need to run this on the main thread, however Dispatch is
@@ -230,7 +255,7 @@ func (w *webViewRenderer) SelectFile() string {
wg.Add(1)
go func() {
w.window.Dispatch(func() {
result = w.window.Dialog(webview.DialogTypeOpen, 0, "Select File", "")
result = w.window.Dialog(wv.DialogTypeOpen, 0, "Select File", "")
wg.Done()
})
}()
@@ -238,7 +263,8 @@ func (w *webViewRenderer) SelectFile() string {
return result
}
func (w *webViewRenderer) SelectDirectory() string {
// SelectDirectory opens a dialog that allows the user to select a directory
func (w *WebView) SelectDirectory() 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
@@ -247,7 +273,7 @@ func (w *webViewRenderer) SelectDirectory() string {
wg.Add(1)
go func() {
w.window.Dispatch(func() {
result = w.window.Dialog(webview.DialogTypeOpen, webview.DialogFlagDirectory, "Select Directory", "")
result = w.window.Dialog(wv.DialogTypeOpen, wv.DialogFlagDirectory, "Select Directory", "")
wg.Done()
})
}()
@@ -255,7 +281,8 @@ func (w *webViewRenderer) SelectDirectory() string {
return result
}
func (w *webViewRenderer) SelectSaveFile() string {
// SelectSaveFile opens a dialog that allows the user to select a file to save
func (w *WebView) SelectSaveFile() 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
@@ -264,7 +291,7 @@ func (w *webViewRenderer) SelectSaveFile() string {
wg.Add(1)
go func() {
w.window.Dispatch(func() {
result = w.window.Dialog(webview.DialogTypeSave, 0, "Save file", "")
result = w.window.Dialog(wv.DialogTypeSave, 0, "Save file", "")
wg.Done()
})
}()
@@ -273,18 +300,19 @@ func (w *webViewRenderer) SelectSaveFile() string {
}
// Callback sends a callback to the frontend
func (w *webViewRenderer) Callback(data string) error {
callbackCMD := fmt.Sprintf("window.wails._.callback('%s');", data)
func (w *WebView) Callback(data string) error {
callbackCMD := fmt.Sprintf("window.wails._.Callback('%s');", data)
return w.evalJS(callbackCMD)
}
func (w *webViewRenderer) NotifyEvent(event *eventData) error {
// NotifyEvent notifies the frontend about a backend runtime event
func (w *WebView) NotifyEvent(event *messages.EventData) error {
// Look out! Nils about!
var err error
if event == nil {
err = fmt.Errorf("Sent nil event to renderer.webViewRenderer")
logger.Error(err)
err = fmt.Errorf("Sent nil event to renderer.WebView")
w.log.Error(err.Error())
return err
}
@@ -301,13 +329,13 @@ func (w *webViewRenderer) NotifyEvent(event *eventData) error {
}
}
message := fmt.Sprintf("wails._.notify('%s','%s')", event.Name, data)
message := fmt.Sprintf("wails._.Notify('%s','%s')", event.Name, data)
return w.evalJS(message)
}
// Window
func (w *webViewRenderer) Fullscreen() {
if w.config.Resizable == false {
// Fullscreen makes the main window go fullscreen
func (w *WebView) Fullscreen() {
if w.config.GetResizable() == false {
w.log.Warn("Cannot call Fullscreen() - App.Resizable = false")
return
}
@@ -316,8 +344,9 @@ func (w *webViewRenderer) Fullscreen() {
})
}
func (w *webViewRenderer) UnFullscreen() {
if w.config.Resizable == false {
// UnFullscreen returns the window to the position prior to a fullscreen call
func (w *WebView) UnFullscreen() {
if w.config.GetResizable() == false {
w.log.Warn("Cannot call UnFullscreen() - App.Resizable = false")
return
}
@@ -326,13 +355,15 @@ func (w *webViewRenderer) UnFullscreen() {
})
}
func (w *webViewRenderer) SetTitle(title string) {
// SetTitle sets the window title
func (w *WebView) SetTitle(title string) {
w.window.Dispatch(func() {
w.window.SetTitle(title)
})
}
func (w *webViewRenderer) Close() {
// Close closes the window
func (w *WebView) Close() {
w.window.Dispatch(func() {
w.window.Terminate()
})

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Serge Zaitsev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

371
lib/renderer/webview/webview.go Executable file
View File

@@ -0,0 +1,371 @@
// Package webview implements Go bindings to https://github.com/zserge/webview C library.
// It is a modified version of webview.go from that repository
// Bindings closely repeat the C APIs and include both, a simplified
// single-function API to just open a full-screen webview window, and a more
// advanced and featureful set of APIs, including Go-to-JavaScript bindings.
//
// The library uses gtk-webkit, Cocoa/Webkit and MSHTML (IE8..11) as a browser
// engine and supports Linux, MacOS and Windows 7..10 respectively.
//
package webview
/*
#cgo linux openbsd freebsd CFLAGS: -DWEBVIEW_GTK=1 -Wno-deprecated-declarations
#cgo linux openbsd freebsd pkg-config: gtk+-3.0 webkit2gtk-4.0
#cgo windows CFLAGS: -DWEBVIEW_WINAPI=1 -std=c99
#cgo windows LDFLAGS: -lole32 -lcomctl32 -loleaut32 -luuid -lgdi32
#cgo darwin CFLAGS: -DWEBVIEW_COCOA=1 -x objective-c
#cgo darwin LDFLAGS: -framework Cocoa -framework WebKit
#include <stdlib.h>
#include <stdint.h>
#define WEBVIEW_STATIC
#define WEBVIEW_IMPLEMENTATION
#include "webview.h"
extern void _webviewExternalInvokeCallback(void *, void *);
static inline void CgoWebViewFree(void *w) {
free((void *)((struct webview *)w)->title);
free((void *)((struct webview *)w)->url);
free(w);
}
static inline void *CgoWebViewCreate(int width, int height, char *title, char *url, int resizable, int debug) {
struct webview *w = (struct webview *) calloc(1, sizeof(*w));
w->width = width;
w->height = height;
w->title = title;
w->url = url;
w->resizable = resizable;
w->debug = debug;
w->external_invoke_cb = (webview_external_invoke_cb_t) _webviewExternalInvokeCallback;
if (webview_init(w) != 0) {
CgoWebViewFree(w);
return NULL;
}
return (void *)w;
}
static inline int CgoWebViewLoop(void *w, int blocking) {
return webview_loop((struct webview *)w, blocking);
}
static inline void CgoWebViewTerminate(void *w) {
webview_terminate((struct webview *)w);
}
static inline void CgoWebViewExit(void *w) {
webview_exit((struct webview *)w);
}
static inline void CgoWebViewSetTitle(void *w, char *title) {
webview_set_title((struct webview *)w, title);
}
static inline void CgoWebViewSetFullscreen(void *w, int fullscreen) {
webview_set_fullscreen((struct webview *)w, fullscreen);
}
static inline void CgoWebViewSetColor(void *w, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
webview_set_color((struct webview *)w, r, g, b, a);
}
static inline void CgoDialog(void *w, int dlgtype, int flags,
char *title, char *arg, char *res, size_t ressz) {
webview_dialog(w, dlgtype, flags,
(const char*)title, (const char*) arg, res, ressz);
}
static inline int CgoWebViewEval(void *w, char *js) {
return webview_eval((struct webview *)w, js);
}
static inline void CgoWebViewInjectCSS(void *w, char *css) {
webview_inject_css((struct webview *)w, css);
}
extern void _webviewDispatchGoCallback(void *);
static inline void _webview_dispatch_cb(struct webview *w, void *arg) {
_webviewDispatchGoCallback(arg);
}
static inline void CgoWebViewDispatch(void *w, uintptr_t arg) {
webview_dispatch((struct webview *)w, _webview_dispatch_cb, (void *)arg);
}
*/
import "C"
import (
"errors"
"runtime"
"sync"
"unsafe"
)
func init() {
// Ensure that main.main is called from the main thread
runtime.LockOSThread()
}
// Open is a simplified API to open a single native window with a full-size webview in
// it. It can be helpful if you want to communicate with the core app using XHR
// or WebSockets (as opposed to using JavaScript bindings).
//
// Window appearance can be customized using title, width, height and resizable parameters.
// URL must be provided and can user either a http or https protocol, or be a
// local file:// URL. On some platforms "data:" URLs are also supported
// (Linux/MacOS).
func Open(title, url string, w, h int, resizable bool) error {
titleStr := C.CString(title)
defer C.free(unsafe.Pointer(titleStr))
urlStr := C.CString(url)
defer C.free(unsafe.Pointer(urlStr))
resize := C.int(0)
if resizable {
resize = C.int(1)
}
r := C.webview(titleStr, urlStr, C.int(w), C.int(h), resize)
if r != 0 {
return errors.New("failed to create webview")
}
return nil
}
// ExternalInvokeCallbackFunc is a function type that is called every time
// "window.external.invoke()" is called from JavaScript. Data is the only
// obligatory string parameter passed into the "invoke(data)" function from
// JavaScript. To pass more complex data serialized JSON or base64 encoded
// string can be used.
type ExternalInvokeCallbackFunc func(w WebView, data string)
// Settings is a set of parameters to customize the initial WebView appearance
// and behavior. It is passed into the webview.New() constructor.
type Settings struct {
// WebView main window title
Title string
// URL to open in a webview
URL string
// Window width in pixels
Width int
// Window height in pixels
Height int
// Allows/disallows window resizing
Resizable bool
// Enable debugging tools (Linux/BSD/MacOS, on Windows use Firebug)
Debug bool
// A callback that is executed when JavaScript calls "window.external.invoke()"
ExternalInvokeCallback ExternalInvokeCallbackFunc
}
// WebView is an interface that wraps the basic methods for controlling the UI
// loop, handling multithreading and providing JavaScript bindings.
type WebView interface {
// Run() starts the main UI loop until the user closes the webview window or
// Terminate() is called.
Run()
// Loop() runs a single iteration of the main UI.
Loop(blocking bool) bool
// SetTitle() changes window title. This method must be called from the main
// thread only. See Dispatch() for more details.
SetTitle(title string)
// SetFullscreen() controls window full-screen mode. This method must be
// called from the main thread only. See Dispatch() for more details.
SetFullscreen(fullscreen bool)
// SetColor() changes window background color. This method must be called from
// the main thread only. See Dispatch() for more details.
SetColor(r, g, b, a uint8)
// Eval() evaluates an arbitrary JS code inside the webview. This method must
// be called from the main thread only. See Dispatch() for more details.
Eval(js string) error
// InjectJS() injects an arbitrary block of CSS code using the JS API. This
// method must be called from the main thread only. See Dispatch() for more
// details.
InjectCSS(css string)
// 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
// Terminate() breaks the main UI loop. This method must be called from the main thread
// only. See Dispatch() for more details.
Terminate()
// Dispatch() schedules some arbitrary function to be executed on the main UI
// thread. This may be helpful if you want to run some JavaScript from
// background threads/goroutines, or to terminate the app.
Dispatch(func())
// Exit() closes the window and cleans up the resources. Use Terminate() to
// forcefully break out of the main UI loop.
Exit()
}
// DialogType is an enumeration of all supported system dialog types
type DialogType int
const (
// DialogTypeOpen is a system file open dialog
DialogTypeOpen DialogType = iota
// DialogTypeSave is a system file save dialog
DialogTypeSave
// DialogTypeAlert is a system alert dialog (message box)
DialogTypeAlert
)
const (
// DialogFlagFile is a normal file picker dialog
DialogFlagFile = C.WEBVIEW_DIALOG_FLAG_FILE
// DialogFlagDirectory is an open directory dialog
DialogFlagDirectory = C.WEBVIEW_DIALOG_FLAG_DIRECTORY
// DialogFlagInfo is an info alert dialog
DialogFlagInfo = C.WEBVIEW_DIALOG_FLAG_INFO
// DialogFlagWarning is a warning alert dialog
DialogFlagWarning = C.WEBVIEW_DIALOG_FLAG_WARNING
// DialogFlagError is an error dialog
DialogFlagError = C.WEBVIEW_DIALOG_FLAG_ERROR
)
var (
m sync.Mutex
index uintptr
fns = map[uintptr]func(){}
cbs = map[WebView]ExternalInvokeCallbackFunc{}
)
type webview struct {
w unsafe.Pointer
}
var _ WebView = &webview{}
func boolToInt(b bool) int {
if b {
return 1
}
return 0
}
// NewWebview creates and opens a new webview window using the given settings. The
// returned object implements the WebView interface. This function returns nil
// if a window can not be created.
func NewWebview(settings Settings) WebView {
if settings.Width == 0 {
settings.Width = 640
}
if settings.Height == 0 {
settings.Height = 480
}
if settings.Title == "" {
settings.Title = "WebView"
}
w := &webview{}
w.w = C.CgoWebViewCreate(C.int(settings.Width), C.int(settings.Height),
C.CString(settings.Title), C.CString(settings.URL),
C.int(boolToInt(settings.Resizable)), C.int(boolToInt(settings.Debug)))
m.Lock()
if settings.ExternalInvokeCallback != nil {
cbs[w] = settings.ExternalInvokeCallback
} else {
cbs[w] = func(w WebView, data string) {}
}
m.Unlock()
return w
}
func (w *webview) Loop(blocking bool) bool {
block := C.int(0)
if blocking {
block = 1
}
return C.CgoWebViewLoop(w.w, block) == 0
}
func (w *webview) Run() {
for w.Loop(true) {
}
}
func (w *webview) Exit() {
C.CgoWebViewExit(w.w)
}
func (w *webview) Dispatch(f func()) {
m.Lock()
for ; fns[index] != nil; index++ {
}
fns[index] = f
m.Unlock()
C.CgoWebViewDispatch(w.w, C.uintptr_t(index))
}
func (w *webview) SetTitle(title string) {
p := C.CString(title)
defer C.free(unsafe.Pointer(p))
C.CgoWebViewSetTitle(w.w, p)
}
func (w *webview) SetColor(r, g, b, a uint8) {
C.CgoWebViewSetColor(w.w, C.uint8_t(r), C.uint8_t(g), C.uint8_t(b), C.uint8_t(a))
}
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 {
const maxPath = 4096
titlePtr := C.CString(title)
defer C.free(unsafe.Pointer(titlePtr))
argPtr := C.CString(arg)
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))
C.CgoDialog(w.w, C.int(dlgType), C.int(flags), titlePtr,
argPtr, resultPtr, C.size_t(maxPath))
return C.GoString(resultPtr)
}
func (w *webview) Eval(js string) error {
p := C.CString(js)
defer C.free(unsafe.Pointer(p))
switch C.CgoWebViewEval(w.w, p) {
case -1:
return errors.New("evaluation failed")
}
return nil
}
func (w *webview) InjectCSS(css string) {
p := C.CString(css)
defer C.free(unsafe.Pointer(p))
C.CgoWebViewInjectCSS(w.w, p)
}
func (w *webview) Terminate() {
C.CgoWebViewTerminate(w.w)
}
//export _webviewDispatchGoCallback
func _webviewDispatchGoCallback(index unsafe.Pointer) {
var f func()
m.Lock()
f = fns[uintptr(index)]
delete(fns, uintptr(index))
m.Unlock()
f()
}
//export _webviewExternalInvokeCallback
func _webviewExternalInvokeCallback(w unsafe.Pointer, data unsafe.Pointer) {
m.Lock()
var (
cb ExternalInvokeCallbackFunc
wv WebView
)
for wv, cb = range cbs {
if wv.(*webview).w == w {
break
}
}
m.Unlock()
cb(wv, C.GoString((*C.char)(data)))
}

File diff suppressed because it is too large Load Diff

42
log.go
View File

@@ -1,42 +0,0 @@
package wails
import (
"os"
"strings"
log "github.com/sirupsen/logrus"
)
// Global logger reference
var logger = log.New()
// Fields is used by the customLogger object to output
// fields along with a message
type Fields map[string]interface{}
// Default options for the global logger
func init() {
logger.SetOutput(os.Stdout)
logger.SetLevel(log.DebugLevel)
}
// Sets the log level to the given level
func setLogLevel(level string) {
switch strings.ToLower(level) {
case "info":
logger.SetLevel(log.InfoLevel)
case "debug":
logger.SetLevel(log.DebugLevel)
case "warn":
logger.SetLevel(log.WarnLevel)
case "error":
logger.SetLevel(log.ErrorLevel)
case "fatal":
logger.SetLevel(log.FatalLevel)
case "panic":
logger.SetLevel(log.PanicLevel)
default:
logger.SetLevel(log.DebugLevel)
logger.Warnf("Log level '%s' not recognised. Setting to Debug.", level)
}
}

BIN
logo_cropped.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View File

@@ -1,22 +1,32 @@
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 *RuntimeEvents
Log *RuntimeLog
Dialog *RuntimeDialog
Window *RuntimeWindow
Browser *RuntimeBrowser
FileSystem *RuntimeFileSystem
Events *runtime.Events
Log *runtime.Log
Dialog *runtime.Dialog
Window *runtime.Window
Browser *runtime.Browser
FileSystem *runtime.FileSystem
}
func newRuntime(eventManager *eventManager, renderer Renderer) *Runtime {
// NewRuntime creates a new Runtime struct
func NewRuntime(eventManager interfaces.EventManager, renderer interfaces.Renderer) *Runtime {
return &Runtime{
Events: newRuntimeEvents(eventManager),
Log: newRuntimeLog(),
Dialog: newRuntimeDialog(renderer),
Window: newRuntimeWindow(renderer),
Browser: newRuntimeBrowser(),
FileSystem: newRuntimeFileSystem(),
Events: runtime.NewEvents(eventManager),
Log: runtime.NewLog(),
Dialog: runtime.NewDialog(renderer),
Window: runtime.NewWindow(renderer),
Browser: runtime.NewBrowser(),
FileSystem: runtime.NewFileSystem(),
}
}

View File

@@ -1,43 +1,38 @@
/*
Wails Bridge (c) 2019-present Lea Anthony
This library creates a bridge between your application
and the frontend, allowing you to develop your app using
standard tooling (browser extensions, live reload, etc).
Usage:
```
import Bridge from "./wailsbridge";
Bridge.Start(startApp);
```
The given callback (startApp in the example) will be called
when the bridge has successfully initialised. It passes the
window.wails object back, in case it is not accessible directly.
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The lightweight framework for web-like apps
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 6 */
// Bridge object
window.wailsbridge = {
reconnectOverlay: null,
reconnectTimer: 300,
wsURL: 'ws://localhost:34115/bridge',
connectionState: null,
config: {},
websocket: null,
callback: null,
overlayHTML:
'<div class="wails-reconnect-overlay"><div class="wails-reconnect-overlay-content"><div class="wails-reconnect-overlay-title">Wails Bridge</div><br><div class="wails-reconnect-overlay-loadingspinner"></div><br><div id="wails-reconnect-overlay-message">Waiting for backend</div></div></div>',
overlayCSS:
'.wails-reconnect-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.6);font-family:sans-serif;display:none;z-index:999999}.wails-reconnect-overlay-content{padding:20px 30px;text-align:center;width:20em;position:relative;height:14em;border-radius:1em;margin:5% auto 0;background-color:#fff;box-shadow:1px 1px 20px 3px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAAuCAMAAACPpbA7AAAAqFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAAAAAAAAEBAQAAAAAAAAAAAAEBAQEBAQDAwMBAQEAAAABAQEAAAAAAAAAAAABAQEAAAAAAAACAgICAgIBAQEAAAAAAAAAAAAAAAAAAAAAAAABAQEAAAACAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQWKCj6oAAAAN3RSTlMALiIqDhkGBAswJjP0GxP6NR4W9/ztjRDMhWU50G9g5eHXvbZ9XEI9xZTcqZl2aldKo55QwoCvZUgzhAAAAs9JREFUSMeNleeWqjAUhU0BCaH3Itiw9zKT93+zG02QK1hm/5HF+jzZJ6fQe6cyXE+jg9X7o9wxuylIIf4Tv2V3+bOrEXnf8dwQ/KQIGDN2/S+4OmVCVXL/ScBnfibxURqIByP/hONE8r8T+bDMlQ98KSl7Y8hzjpS8v1qtDh8u5f8KQpGpfnPPhqG8JeogN37Hq9eaN2xRhIwAaGnvws8F1ShxqK5ob2twYi1FAMD4rXsYtnC/JEiRbl4cUrCWhnMCLRFemXezXbb59QK4WASOsm6n2W1+4CBT2JmtzQ6fsrbGubR/NFbd2g5Y179+5w/GEHaKsHjYCet7CgrXU3txarNC7YxOVJtIj4/ERzMdZfzc31hp+8cD6eGILgarZY9uZ12hAs03vfBD9C171gS5Omz7OcvxALQIn4u8RRBBBcsi9WW2woO9ipLgfzpYlggg3ZRdROUC8KT7QLqq3W9KB5BbdFVg4929kdwp6+qaZnMCCNBdj+NyN1W885Ry/AL3D4AQbsVV4noCiM/C83kyYq80XlDAYQtralOiDzoRAHlotWl8q2tjvYlOgcg1A8jEApZa+C06TBdAz2Qv0wu11I/zZOyJQ6EwGez2P2b8PIQr1hwwnAZsAxwA4UAYOyXUxM/xp6tHAn4GUmPGM9R28oVxgC0e/zQJJI6DyhyZ1r7uzRQhpcW7x7vTaWSzKSG6aep77kroTEl3U81uSVaUTtgEINfC8epx+Q4F9SpplHG84Ek6m4RAq9/TLkOBrxyeuddZhHvGIp1XXfFy3Z3vtwNblKGiDn+J+92vwwABHghj7HnzlS1H5kB49AZvdGCFgiBPq69qfXPr3y++yilF0ON4R8eR7spAsLpZ95NqAW5tab1c4vkZm6aleajchMwYTdILQQTwE2OV411ZM9WztDjPql12caBi6gDpUKmDd4U1XNdQxZ4LIXQ5/Tr4P7I9tYcFrDK3AAAAAElFTkSuQmCC);background-repeat:no-repeat;background-position:center}.wails-reconnect-overlay-title{font-size:2em}.wails-reconnect-overlay-message{font-size:1.3em}.wails-reconnect-overlay-loadingspinner{pointer-events:none;width:2.5em;height:2.5em;border:.4em solid transparent;border-color:#3E67EC #eee #eee;border-radius:50%;animation:loadingspin 1s linear infinite;margin:auto;padding:2.5em}@keyframes loadingspin{100%{transform:rotate(360deg)}}',
log: function (message) {
// eslint-disable-next-line
console.log(
'%c wails bridge %c ' + message + ' ',
'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem',
'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem'
);
}
};
function init() {
// Bridge object
window.wailsbridge = {
reconnectOverlay: null,
reconnectTimer: 300,
wsURL: 'ws://localhost:34115/bridge',
connectionState: null,
config: {},
websocket: null,
callback: null,
overlayHTML:
'<div class="wails-reconnect-overlay"><div class="wails-reconnect-overlay-content"><div class="wails-reconnect-overlay-title">Wails Bridge</div><br><div class="wails-reconnect-overlay-loadingspinner"></div><br><div id="wails-reconnect-overlay-message">Waiting for backend</div></div></div>',
overlayCSS:
'.wails-reconnect-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.6);font-family:sans-serif;display:none;z-index:999999}.wails-reconnect-overlay-content{padding:20px 30px;text-align:center;width:20em;position:relative;height:14em;border-radius:1em;margin:5% auto 0;background-color:#fff;box-shadow:1px 1px 20px 3px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAAuCAMAAACPpbA7AAAAqFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAAAAAAAAEBAQAAAAAAAAAAAAEBAQEBAQDAwMBAQEAAAABAQEAAAAAAAAAAAABAQEAAAAAAAACAgICAgIBAQEAAAAAAAAAAAAAAAAAAAAAAAABAQEAAAACAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQWKCj6oAAAAN3RSTlMALiIqDhkGBAswJjP0GxP6NR4W9/ztjRDMhWU50G9g5eHXvbZ9XEI9xZTcqZl2aldKo55QwoCvZUgzhAAAAs9JREFUSMeNleeWqjAUhU0BCaH3Itiw9zKT93+zG02QK1hm/5HF+jzZJ6fQe6cyXE+jg9X7o9wxuylIIf4Tv2V3+bOrEXnf8dwQ/KQIGDN2/S+4OmVCVXL/ScBnfibxURqIByP/hONE8r8T+bDMlQ98KSl7Y8hzjpS8v1qtDh8u5f8KQpGpfnPPhqG8JeogN37Hq9eaN2xRhIwAaGnvws8F1ShxqK5ob2twYi1FAMD4rXsYtnC/JEiRbl4cUrCWhnMCLRFemXezXbb59QK4WASOsm6n2W1+4CBT2JmtzQ6fsrbGubR/NFbd2g5Y179+5w/GEHaKsHjYCet7CgrXU3txarNC7YxOVJtIj4/ERzMdZfzc31hp+8cD6eGILgarZY9uZ12hAs03vfBD9C171gS5Omz7OcvxALQIn4u8RRBBBcsi9WW2woO9ipLgfzpYlggg3ZRdROUC8KT7QLqq3W9KB5BbdFVg4929kdwp6+qaZnMCCNBdj+NyN1W885Ry/AL3D4AQbsVV4noCiM/C83kyYq80XlDAYQtralOiDzoRAHlotWl8q2tjvYlOgcg1A8jEApZa+C06TBdAz2Qv0wu11I/zZOyJQ6EwGez2P2b8PIQr1hwwnAZsAxwA4UAYOyXUxM/xp6tHAn4GUmPGM9R28oVxgC0e/zQJJI6DyhyZ1r7uzRQhpcW7x7vTaWSzKSG6aep77kroTEl3U81uSVaUTtgEINfC8epx+Q4F9SpplHG84Ek6m4RAq9/TLkOBrxyeuddZhHvGIp1XXfFy3Z3vtwNblKGiDn+J+92vwwABHghj7HnzlS1H5kB49AZvdGCFgiBPq69qfXPr3y++yilF0ON4R8eR7spAsLpZ95NqAW5tab1c4vkZm6aleajchMwYTdILQQTwE2OV411ZM9WztDjPql12caBi6gDpUKmDd4U1XNdQxZ4LIXQ5/Tr4P7I9tYcFrDK3AAAAAElFTkSuQmCC);background-repeat:no-repeat;background-position:center}.wails-reconnect-overlay-title{font-size:2em}.wails-reconnect-overlay-message{font-size:1.3em}.wails-reconnect-overlay-loadingspinner{pointer-events:none;width:2.5em;height:2.5em;border:.4em solid transparent;border-color:#3E67EC #eee #eee;border-radius:50%;animation:loadingspin 1s linear infinite;margin:auto;padding:2.5em}@keyframes loadingspin{100%{transform:rotate(360deg)}}',
log: function (message) {
// eslint-disable-next-line
console.log(
'%c wails bridge %c ' + message + ' ',
'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem',
'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem'
);
}
};
}
// Adapted from webview - thanks zserge!
function injectCSS(css) {
@@ -100,13 +95,6 @@ function startBridge() {
window.wailsbridge.reconnectOverlay.style.display = 'none';
}
// Bridge external.invoke
window.external = {
invoke: function (msg) {
window.wailsbridge.websocket.send(msg);
}
};
// Adds a script to the Dom.
// Removes it if second parameter is true.
function addScript(script, remove) {
@@ -184,12 +172,12 @@ function startBridge() {
case 'b':
var binding = message.data.slice(1);
//log("Binding: " + binding)
window.wails._.newBinding(binding);
window.wails._.NewBinding(binding);
break;
// Call back
case 'c':
var callbackData = message.data.slice(1);
window.wails._.callback(callbackData);
window.wails._.Callback(callbackData);
break;
default:
window.wails.Log.Error('Unknown message type received: ' + message.data[0]);
@@ -203,14 +191,20 @@ function startBridge() {
connect();
}
export default {
// The main function
// Passes the main Wails object to the callback if given.
Start: function (callback) {
// Save the callback
window.wailsbridge.callback = callback;
function start(callback) {
// Start Bridge
startBridge();
}
};
// Set up the bridge
init();
// Save the callback
window.wailsbridge.callback = callback;
// Start Bridge
startBridge();
}
function Init(callback) {
start(callback);
}
module.exports = Init;

175
runtime/assets/console.js Normal file
View File

@@ -0,0 +1,175 @@
(function () {
window.wailsconsole = {};
var debugconsole = document.createElement("div");
var header = document.createElement("div");
var consoleOut = document.createElement("div");
document.addEventListener('keyup', logKey);
debugconsole.id = "wailsdebug";
debugconsole.style.fontSize = "18px";
debugconsole.style.width = "100%";
debugconsole.style.height = "35%";
debugconsole.style.maxHeight = "35%";
debugconsole.style.position = "fixed";
debugconsole.style.left = "0px";
debugconsole.style.backgroundColor = "rgba(255,255,255,0.8)";
debugconsole.style.borderTop = '1px solid black';
debugconsole.style.color = "black";
debugconsole.style.display = "none";
header.style.width = "100%";
header.style.height = "30px";
header.style.display = "block";
// header.style.paddingTop = "3px";
header.style.verticalAlign = "middle";
header.style.paddingLeft = "10px";
header.style.background = "rgba(255,255,255,0.8)";
header.innerHTML = " <span style='vertical-align: middle'> Wails Console > <input id='conin' style='border: solid 1px black; width: 50%'></input><span style='padding-left: 5px; cursor:pointer;' onclick='window.wailsconsole.clearConsole()'>Clear</span></span>";
consoleOut.style.position = "absolute";
consoleOut.style.width = "100%";
consoleOut.style.height = "auto";
consoleOut.style.top = "30px";
// consoleOut.style.paddingLeft = "10px";
consoleOut.style.bottom = "0px";
consoleOut.style.backgroundColor = "rgba(200,200,200,1)";
consoleOut.style.overflowY = "scroll";
consoleOut.style.msOverflowStyle = "-ms-autohiding-scrollbar";
debugconsole.appendChild(header);
debugconsole.appendChild(consoleOut);
document.body.appendChild(debugconsole);
console.log(debugconsole.style.display)
function logKey(e) {
var conin = document.getElementById('conin');
if (e.which == 27 && e.shiftKey) {
toggleConsole(conin);
}
if (e.which == 13 && consoleVisible()) {
var command = conin.value.trim();
if (command.length > 0) {
console.log("> " + command)
try {
evaluateInput(command);
} catch (e) {
console.error(e.message);
}
conin.value = "";
}
}
};
function consoleVisible() {
return debugconsole.style.display == "block";
}
function toggleConsole(conin) {
var display = "none"
if (debugconsole.style.display == "none") display = "block";
debugconsole.style.display = display;
if (display == "block") {
conin.focus();
}
}
function evaluateExpression(expression) {
var pathSegments = [].concat(expression.split('.'));
if (pathSegments[0] == 'window') {
pathSegments.shift()
}
var currentObject = window;
for (var i = 0; i < pathSegments.length; i++) {
var pathSegment = pathSegments[i];
if (currentObject[pathSegment] == undefined) {
return false;
}
currentObject = currentObject[pathSegment];
}
console.log(JSON.stringify(currentObject));
return true;
}
function evaluateInput(command) {
try {
if (evaluateExpression(command)) {
return
} else {
eval(command);
}
} catch (e) {
console.error(e.message)
}
}
// Set us up as a listener
function hookIntoIPC() {
if (window.wails && window.wails._ && window.wails._.AddIPCListener) {
window.wails._.AddIPCListener(processIPCMessage);
} else {
setTimeout(hookIntoIPC, 100);
}
}
hookIntoIPC();
function processIPCMessage(message) {
console.log(message);
var parsedMessage;
try {
parsedMessage = JSON.parse(message);
} catch (e) {
console.error("Error in parsing IPC message: " + e.message);
return false;
}
var logmessage = "[IPC] "
switch (parsedMessage.type) {
case 'call':
logmessage += " Call: " + parsedMessage.payload.bindingName;
var params = "";
var parsedParams = JSON.parse(parsedMessage.payload.data);
if (parsedParams.length > 0) {
params = parsedParams;
}
logmessage += "(" + params + ")";
break;
case 'log':
logmessage += "Log (" + parsedMessage.payload.level + "): " + parsedMessage.payload.message;
break;
default:
logmessage = message;
}
console.log(logmessage);
}
window.wailsconsole.clearConsole = function () {
consoleOut.innerHTML = "";
}
console.log = function (message) {
consoleOut.innerHTML = consoleOut.innerHTML + "<span style='padding-left: 5px'>" + message + '</span><br/>';
consoleOut.scrollTop = consoleOut.scrollHeight;
};
console.error = function (message) {
consoleOut.innerHTML = consoleOut.innerHTML + "<span style='color:red; padding-left: 5px'> Error: " + message + '</span><br/>';
consoleOut.scrollTop = consoleOut.scrollHeight;
};
// var h = document.getElementsByTagName("html")[0];
// console.log("html margin: " + h.style.marginLeft);
// console.log("html padding: " + h.style.paddingLeft);
// setInterval(function() { console.log("test");}, 1000);
// setInterval(function() { console.error("oops");}, 3000);
// var script = document.createElement('script');
// script.src = "https://cdnjs.cloudflare.com/ajax/libs/firebug-lite/1.4.0/firebug-lite.js#startOpened=true";
// document.body.appendChild(script);
})();

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>
<body>
<div id="app"></div>
<script type="text/javascript">function AddScript(js, callbackID) {
var script = document.createElement('script');
script.text = js;
document.body.appendChild(script);
}</script>
</body>
</html>

1
runtime/assets/wails.js Normal file

File diff suppressed because one or more lines are too long

21
runtime/browser.go Normal file
View File

@@ -0,0 +1,21 @@
package runtime
import "github.com/pkg/browser"
// Browser exposes browser methods to the runtime
type Browser struct{}
// NewBrowser creates a new runtime Browser struct
func NewBrowser() *Browser {
return &Browser{}
}
// OpenURL opens the given url in the system's default browser
func (r *Browser) OpenURL(url string) error {
return browser.OpenURL(url)
}
// OpenFile opens the given file in the system's default browser
func (r *Browser) OpenFile(filePath string) error {
return browser.OpenFile(filePath)
}

30
runtime/dialog.go Normal file
View File

@@ -0,0 +1,30 @@
package runtime
import "github.com/wailsapp/wails/lib/interfaces"
// Dialog exposes an interface to native dialogs
type Dialog struct {
renderer interfaces.Renderer
}
// NewDialog creates a new Dialog struct
func NewDialog(renderer interfaces.Renderer) *Dialog {
return &Dialog{
renderer: renderer,
}
}
// SelectFile prompts the user to select a file
func (r *Dialog) SelectFile() string {
return r.renderer.SelectFile()
}
// SelectDirectory prompts the user to select a directory
func (r *Dialog) SelectDirectory() string {
return r.renderer.SelectDirectory()
}
// SelectSaveFile prompts the user to select a file for saving
func (r *Dialog) SelectSaveFile() string {
return r.renderer.SelectSaveFile()
}

25
runtime/events.go Normal file
View File

@@ -0,0 +1,25 @@
package runtime
import "github.com/wailsapp/wails/lib/interfaces"
// Events exposes the events interface
type Events struct {
eventManager interfaces.EventManager
}
// NewEvents creates a new Events struct
func NewEvents(eventManager interfaces.EventManager) *Events {
return &Events{
eventManager: eventManager,
}
}
// On pass through
func (r *Events) On(eventName string, callback func(optionalData ...interface{})) {
r.eventManager.On(eventName, callback)
}
// Emit pass through
func (r *Events) Emit(eventName string, optionalData ...interface{}) {
r.eventManager.Emit(eventName, optionalData...)
}

16
runtime/filesystem.go Normal file
View File

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

28
runtime/js/.eslintrc Normal file
View File

@@ -0,0 +1,28 @@
{
"env": {
"browser": true,
"es6": true,
"amd": true,
"node": true,
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2016,
"sourceType": "module",
},
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": 0,
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}

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