Compare commits
205 Commits
Create-con
...
91-create-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8432f725a9 | ||
|
|
7015b80888 | ||
|
|
1ac16d1933 | ||
|
|
0f36a88f0e | ||
|
|
b30031d025 | ||
|
|
cd152f0cd7 | ||
|
|
12280b51b9 | ||
|
|
29256c5766 | ||
|
|
8b13c0b197 | ||
|
|
50e2037fba | ||
|
|
f86c10af02 | ||
|
|
00e165b139 | ||
|
|
02199bbe9d | ||
|
|
d591a55140 | ||
|
|
508295b558 | ||
|
|
bdc33e1430 | ||
|
|
d55dcb2dfb | ||
|
|
79ac4c1d45 | ||
|
|
c6bfa85a1a | ||
|
|
82289714ba | ||
|
|
77142bd99e | ||
|
|
538f5c6501 | ||
|
|
3ff02fb183 | ||
|
|
30298b4237 | ||
|
|
9f2c9a989f | ||
|
|
146147bed8 | ||
|
|
76499b20c4 | ||
|
|
b21e79daf5 | ||
|
|
71194108de | ||
|
|
11fd50f78d | ||
|
|
c08d1d7b3c | ||
|
|
2febf5a97a | ||
|
|
cea7b1e494 | ||
|
|
25962e2e53 | ||
|
|
43f9b141fe | ||
|
|
e1b729ea96 | ||
|
|
f9a18817b7 | ||
|
|
23cb97d314 | ||
|
|
32e085b609 | ||
|
|
389dee8db9 | ||
|
|
dba6fdf7e4 | ||
|
|
cc45dcf91e | ||
|
|
4aeb554f1b | ||
|
|
3d2268420b | ||
|
|
ca388be121 | ||
|
|
f9ca13ff30 | ||
|
|
319dbcdc49 | ||
|
|
bffbbd59ce | ||
|
|
8bd1d0ec92 | ||
|
|
0daec29fab | ||
|
|
bb3dbe0510 | ||
|
|
2fa9e3b0ee | ||
|
|
40d1750345 | ||
|
|
d2d4ea3033 | ||
|
|
ff55170002 | ||
|
|
444db6a560 | ||
|
|
b015f27e14 | ||
|
|
67a1f23b13 | ||
|
|
9c98a7a9e3 | ||
|
|
5aa5ad8ad3 | ||
|
|
ac203ec931 | ||
|
|
3fd73186f4 | ||
|
|
46307469e5 | ||
|
|
31a67f3aed | ||
|
|
44919d2750 | ||
|
|
cff87c641b | ||
|
|
abbd71d057 | ||
|
|
aacfe8386a | ||
|
|
97944d771a | ||
|
|
3f1b616a5e | ||
|
|
fdcc2fd2e5 | ||
|
|
855032ed1e | ||
|
|
760e109aab | ||
|
|
4c799bca8f | ||
|
|
2d08ebc054 | ||
|
|
91ab2c2b31 | ||
|
|
13ad57d49f | ||
|
|
a109e3078d | ||
|
|
2e61a3c309 | ||
|
|
0373bea4e5 | ||
|
|
2d5825d73d | ||
|
|
c4a042cb1d | ||
|
|
205f9476fa | ||
|
|
a1230fcbb6 | ||
|
|
ba5c32a4a1 | ||
|
|
58eee64326 | ||
|
|
41d786a13c | ||
|
|
a2b7906c89 | ||
|
|
eeb6fa4677 | ||
|
|
6a36d75774 | ||
|
|
6078f3c780 | ||
|
|
fc11197725 | ||
|
|
8c40b99194 | ||
|
|
de53fc6510 | ||
|
|
d4f4feb429 | ||
|
|
02973c49ff | ||
|
|
01dce9f139 | ||
|
|
6c0906e87d | ||
|
|
d7cfc4c71a | ||
|
|
fec3e7eac5 | ||
|
|
9f5e2c7dd4 | ||
|
|
c5276cca6c | ||
|
|
74dbbbed8a | ||
|
|
629ac4b93c | ||
|
|
5ece7e84b3 | ||
|
|
eaba857676 | ||
|
|
db489a3cae | ||
|
|
4821ab8597 | ||
|
|
670b769f82 | ||
|
|
eb53399824 | ||
|
|
24e4fbfb68 | ||
|
|
7f54ca4ac3 | ||
|
|
b224803e4d | ||
|
|
28b2025aaa | ||
|
|
77e85705d1 | ||
|
|
9fd24595c7 | ||
|
|
69067e85f5 | ||
|
|
c8db58e00e | ||
|
|
e96e0e0999 | ||
|
|
2da21ec528 | ||
|
|
cda0b40414 | ||
|
|
7f4229dd6b | ||
|
|
68651b77f4 | ||
|
|
bf001f5ad2 | ||
|
|
d1907b4ce5 | ||
|
|
56363d193d | ||
|
|
8553f43080 | ||
|
|
587681bb8d | ||
|
|
afbf80ea4a | ||
|
|
c180d7dccb | ||
|
|
c20aabc8f8 | ||
|
|
4cccb628c9 | ||
|
|
5d487347d4 | ||
|
|
fe8b7ac5c9 | ||
|
|
732c70777b | ||
|
|
753c5fd337 | ||
|
|
2dad29673d | ||
|
|
5e466893cf | ||
|
|
c15fd822c1 | ||
|
|
42b1c0befa | ||
|
|
6ef8744e02 | ||
|
|
cf916c8e8b | ||
|
|
cdc1d4be3e | ||
|
|
1c5284db3e | ||
|
|
073cdc3a55 | ||
|
|
fd9363e842 | ||
|
|
3051628fa2 | ||
|
|
cac97e8652 | ||
|
|
2ccabc772b | ||
|
|
ff91241592 | ||
|
|
2257b1cab1 | ||
|
|
bdcf98fc15 | ||
|
|
3025a94a77 | ||
|
|
d971495ad3 | ||
|
|
a4b1f469e9 | ||
|
|
b18f04b30d | ||
|
|
94e9447e1c | ||
|
|
03c479c890 | ||
|
|
c95a3a795e | ||
|
|
1d8e99d846 | ||
|
|
8e909fc9f4 | ||
|
|
3bc86a4f50 | ||
|
|
2c7913c202 | ||
|
|
d1c57ddb5f | ||
|
|
9ffb517183 | ||
|
|
27f852ac6a | ||
|
|
20c0b48634 | ||
|
|
6cf01b4239 | ||
|
|
3ae88f8822 | ||
|
|
5994eb605f | ||
|
|
9694dc57aa | ||
|
|
b5b78fddee | ||
|
|
c905185467 | ||
|
|
47ca7879cd | ||
|
|
6202b3bf3e | ||
|
|
ea94c2de1f | ||
|
|
eb0d4bc42f | ||
|
|
b323c3db20 | ||
|
|
1670ac6567 | ||
|
|
c941176018 | ||
|
|
a060d9dcc0 | ||
|
|
ba208dce44 | ||
|
|
9bbac46b3f | ||
|
|
d8c591e64c | ||
|
|
2c28a8f550 | ||
|
|
d6c5586159 | ||
|
|
08a7893b1d | ||
|
|
fa6cf17079 | ||
|
|
fe2a20f92a | ||
|
|
b713d57168 | ||
|
|
17ca06693e | ||
|
|
243d738d64 | ||
|
|
3f50b95f26 | ||
|
|
f0d8ce99a1 | ||
|
|
259eec97d6 | ||
|
|
8b2168abe7 | ||
|
|
a51e127309 | ||
|
|
c5cee79ff7 | ||
|
|
9393b08c3f | ||
|
|
0ca039e914 | ||
|
|
cd8b4f088f | ||
|
|
847842504b | ||
|
|
8ab91d31fe | ||
|
|
bb4d891549 | ||
|
|
529e4cc07e |
42
.chglog/CHANGELOG.tpl.md
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
{{ 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 -}}
|
||||||
27
.chglog/config.yml
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
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
|
||||||
30
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Description**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behaviour:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behaviour**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**System Details**
|
||||||
|
Please paste the output of `wails report` here.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
4
.gitignore
vendored
@@ -14,4 +14,6 @@
|
|||||||
examples/**/example*
|
examples/**/example*
|
||||||
!examples/**/*.*
|
!examples/**/*.*
|
||||||
cmd/wails/wails
|
cmd/wails/wails
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
tmp
|
||||||
|
dist
|
||||||
34
.goreleaser.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# This is an example goreleaser.yaml file with some sane defaults.
|
||||||
|
# Make sure to check the documentation at http://goreleaser.com
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
goos:
|
||||||
|
- windows
|
||||||
|
- linux
|
||||||
|
- darwin
|
||||||
|
goarch:
|
||||||
|
- 386
|
||||||
|
- amd64
|
||||||
|
ignore:
|
||||||
|
- goos: darwin
|
||||||
|
goarch: 386
|
||||||
|
main: ./cmd/wails/main.go
|
||||||
|
archive:
|
||||||
|
replacements:
|
||||||
|
darwin: Darwin
|
||||||
|
linux: Linux
|
||||||
|
windows: Windows
|
||||||
|
386: i386
|
||||||
|
amd64: x86_64
|
||||||
|
checksum:
|
||||||
|
name_template: 'checksums.txt'
|
||||||
|
snapshot:
|
||||||
|
name_template: "{{ .Tag }}-next"
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
||||||
|
filters:
|
||||||
|
exclude:
|
||||||
|
- '^docs:'
|
||||||
|
- '^test:'
|
||||||
26
.vscode/launch.json
vendored
@@ -5,14 +5,36 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "Launch",
|
"name": "Wails Init",
|
||||||
"type": "go",
|
"type": "go",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mode": "auto",
|
"mode": "auto",
|
||||||
"program": "${workspaceFolder}/cmd/wails/main.go",
|
"program": "${workspaceFolder}/cmd/wails/main.go",
|
||||||
"env": {},
|
"env": {},
|
||||||
|
"cwd": "/tmp",
|
||||||
"args": [
|
"args": [
|
||||||
"setup"
|
"init",
|
||||||
|
"-name",
|
||||||
|
"runtime",
|
||||||
|
"-dir",
|
||||||
|
"runtime",
|
||||||
|
"-output",
|
||||||
|
"runtime",
|
||||||
|
"-template",
|
||||||
|
"vuebasic"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Wails Update Pre",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "${workspaceFolder}/cmd/wails/main.go",
|
||||||
|
"env": {},
|
||||||
|
"cwd": "/tmp",
|
||||||
|
"args": [
|
||||||
|
"update",
|
||||||
|
"-pre"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"go.formatTool": "goimports"
|
||||||
|
}
|
||||||
19
CHANGELOG.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
<a name="v0.13.0"></a>
|
||||||
|
## [v0.13.0] - 2019-05-12
|
||||||
|
|
||||||
|
### Feat
|
||||||
|
- revamped 'update' system
|
||||||
|
- no need for explicit GO111MODULE=on
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
- documentation typo fixes
|
||||||
|
- windows init project
|
||||||
|
- windows 10 colour
|
||||||
|
- leave windows assets on -p flag
|
||||||
|
- show prerequisite errors
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
- updated contributors
|
||||||
|
- added awesomego logo
|
||||||
|
- added Redhat distro
|
||||||
12
CONTRIBUTORS.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Contributors
|
||||||
|
|
||||||
|
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)
|
||||||
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2018 wailsapp
|
Copyright (c) 2018-Present Lea Anthony
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
121
README.md
@@ -1 +1,120 @@
|
|||||||
# Coming Soon
|
<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/>
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
A framework for building desktop applications using Go & Web Technologies.<br/><br/>
|
||||||
|
<a href="https://github.com/wailsapp/wails/blob/master/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg"></a>
|
||||||
|
<a href="https://goreportcard.com/report/github.com/wailsapp/wails"><img src="https://goreportcard.com/badge/github.com/wailsapp/wails"/></a>
|
||||||
|
<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://houndci.com"><img src="https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg"/></a>
|
||||||
|
<a href="https://github.com/sindresorhus/awesome" rel="nofollow"><img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome"></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!
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Use standard Go libraries/frameworks for the backend
|
||||||
|
- Use any frontend technology to build your UI
|
||||||
|
- Expose Go methods/functions to the frontend via a single bind command
|
||||||
|
- Uses native rendering engines - no embedded browser
|
||||||
|
- Shared events system
|
||||||
|
- Native file dialogs
|
||||||
|
- 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
|
||||||
|
|
||||||
|
Wails uses cgo to bind to the native rendering engines so a number of platform dependent libraries are needed as well as an installation of Go. The basic requirements are:
|
||||||
|
|
||||||
|
- Go 1.11 or above
|
||||||
|
- npm
|
||||||
|
|
||||||
|
### MacOS
|
||||||
|
|
||||||
|
Make sure you have the xcode command line tools installed. This can be done by running:
|
||||||
|
|
||||||
|
`xcode-select --install`
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
#### Ubuntu 18.04
|
||||||
|
|
||||||
|
`sudo apt install pkg-config build-essential libgtk-3-dev libwebkit2gtk-4.0-dev`
|
||||||
|
|
||||||
|
#### Arch Linux
|
||||||
|
|
||||||
|
`sudo pacman -S webkit2gtk gtk3`
|
||||||
|
|
||||||
|
#### Red Hat Based Distros
|
||||||
|
|
||||||
|
`sudo yum install webkit2gtk-devel gtk3-devel`
|
||||||
|
|
||||||
|
Note: If you have successfully installed these dependencies on a different flavour of Linux, please consider submitting a PR.
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
Windows requires gcc and related tooling. The recommended download is from [http://tdm-gcc.tdragon.net/download](http://tdm-gcc.tdragon.net/download). Once this is installed, you are good to go.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Installation is as simple as running the following command:
|
||||||
|
|
||||||
|
<pre style='color:white'>
|
||||||
|
go get github.com/wailsapp/wails/cmd/wails
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
It is recommended at this stage to read the comprehensive documentation at [https://wails.app](https://wails.app).
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
* Is this an alternative to Electron?
|
||||||
|
|
||||||
|
Depends on your requirements. It's designed to make it easy for Go programmers to make lightweight desktop applications or add a frontend to their existing applications. Whilst Wails does not currently offer hooks into native elements such as menus, this may change in the future.
|
||||||
|
|
||||||
|
* Who is this project aimed at?
|
||||||
|
|
||||||
|
Go programmers who want to bundle an HTML/JS/CSS frontend with their applications, without resorting to creating a server and opening a browser to view it.
|
||||||
|
|
||||||
|
* What's with the name?
|
||||||
|
|
||||||
|
When I saw WebView, I thought "What I really want is tooling around building a WebView app, a bit like Rails is to Ruby". So initially it was a play on words (Webview on Rails). It just so happened to also be a homophone of the English name for the [Country](https://en.wikipedia.org/wiki/Wales) I am from. So it stuck.
|
||||||
|
|
||||||
|
## Shoulders of Giants
|
||||||
|
|
||||||
|
Without the following people, this project would never have existed:
|
||||||
|
|
||||||
|
* [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot) - His support and feedback has been immense. More patience than you can throw a stick at (Not long now Dustin!).
|
||||||
|
* [Serge Zaitsev](https://github.com/zserge) - Creator of [Webview](https://github.com/zserge/webview) which Wails uses for the windowing.
|
||||||
|
|
||||||
|
And without [these people](CONTRIBUTORS.md), it wouldn't be what it is today.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
This project was mainly coded to the following albums:
|
||||||
|
|
||||||
|
* [Manic Street Preachers - Resistance Is Futile](https://open.spotify.com/album/1R2rsEUqXjIvAbzM0yHrxA)
|
||||||
|
* [Manic Street Preachers - This Is My Truth, Tell Me Yours](https://open.spotify.com/album/4VzCL9kjhgGQeKCiojK1YN)
|
||||||
|
* [The Midnight - Endless Summer](https://open.spotify.com/album/4Krg8zvprquh7TVn9OxZn8)
|
||||||
|
* [Gary Newman - Savage (Songs from a Broken World)](https://open.spotify.com/album/3kMfsD07Q32HRWKRrpcexr)
|
||||||
|
* [Steve Vai - Passion & Warfare](https://open.spotify.com/album/0oL0OhrE2rYVns4IGj8h2m)
|
||||||
|
* [Ben Howard - Every Kingdom](https://open.spotify.com/album/1nJsbWm3Yy2DW1KIc1OKle)
|
||||||
|
* [Ben Howard - Noonday Dream](https://open.spotify.com/album/6astw05cTiXEc2OvyByaPs)
|
||||||
|
* [Adwaith - Melyn](https://open.spotify.com/album/2vBE40Rp60tl7rNqIZjaXM)
|
||||||
|
* [Gwidaith Hen Fran - Cedors Hen Wrach](https://open.spotify.com/album/3v2hrfNGINPLuDP0YDTOjm)
|
||||||
|
* [Metallica - Metallica](https://open.spotify.com/album/2Kh43m04B1UkVcpcRa1Zug)
|
||||||
|
* [Bloc Party - Silent Alarm](https://open.spotify.com/album/6SsIdN05HQg2GwYLfXuzLB)
|
||||||
|
* [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
|
||||||
|
* [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)
|
||||||
|
|
||||||
|
|||||||
27
app.go
@@ -2,13 +2,12 @@ package wails
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/cmd"
|
"github.com/wailsapp/wails/cmd"
|
||||||
"github.com/wailsapp/wails/cmd/frameworks"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------------------------------- Compile time Flags ------------------------------
|
// -------------------------------- Compile time Flags ------------------------------
|
||||||
|
|
||||||
// DebugMode indicates if we are in debug Mode
|
// BuildMode indicates what mode we are in
|
||||||
var DebugMode = "true"
|
var BuildMode = cmd.BuildModeProd
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -18,7 +17,6 @@ type App struct {
|
|||||||
cli *cmd.Cli // In debug mode, we have a cli
|
cli *cmd.Cli // In debug mode, we have a cli
|
||||||
renderer Renderer // The renderer is what we will render the app to
|
renderer Renderer // The renderer is what we will render the app to
|
||||||
logLevel string // The log level of the app
|
logLevel string // The log level of the app
|
||||||
headless bool // Indicates if the app should be started in headless mode
|
|
||||||
ipc *ipcManager // Handles the IPC calls
|
ipc *ipcManager // Handles the IPC calls
|
||||||
log *CustomLogger // Logger
|
log *CustomLogger // Logger
|
||||||
bindingManager *bindingManager // Handles binding of Go code to renderer
|
bindingManager *bindingManager // Handles binding of Go code to renderer
|
||||||
@@ -55,7 +53,7 @@ func CreateApp(optionalConfig ...*AppConfig) *App {
|
|||||||
result.config = appconfig
|
result.config = appconfig
|
||||||
|
|
||||||
// Set up the CLI if not in release mode
|
// Set up the CLI if not in release mode
|
||||||
if DebugMode == "true" {
|
if BuildMode != cmd.BuildModeProd {
|
||||||
result.cli = result.setupCli()
|
result.cli = result.setupCli()
|
||||||
} else {
|
} else {
|
||||||
// Disable Inspector in release mode
|
// Disable Inspector in release mode
|
||||||
@@ -67,12 +65,16 @@ func CreateApp(optionalConfig ...*AppConfig) *App {
|
|||||||
|
|
||||||
// Run the app
|
// Run the app
|
||||||
func (a *App) Run() error {
|
func (a *App) Run() error {
|
||||||
if DebugMode == "true" {
|
if BuildMode != cmd.BuildModeProd {
|
||||||
return a.cli.Run()
|
return a.cli.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
a.logLevel = "error"
|
a.logLevel = "error"
|
||||||
return a.start()
|
err := a.start()
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error(err.Error())
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) start() error {
|
func (a *App) start() error {
|
||||||
@@ -84,7 +86,7 @@ func (a *App) start() error {
|
|||||||
a.log.Info("Starting")
|
a.log.Info("Starting")
|
||||||
|
|
||||||
// Check if we are to run in headless mode
|
// Check if we are to run in headless mode
|
||||||
if a.headless {
|
if BuildMode == cmd.BuildModeBridge {
|
||||||
a.renderer = &Headless{}
|
a.renderer = &Headless{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,11 +111,6 @@ func (a *App) start() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject framework, if specified
|
|
||||||
if frameworks.FrameworkToUse != nil {
|
|
||||||
a.renderer.InjectFramework(frameworks.FrameworkToUse.JS, frameworks.FrameworkToUse.CSS)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inject CSS
|
// Inject CSS
|
||||||
a.renderer.AddCSSList(a.cssCache)
|
a.renderer.AddCSSList(a.cssCache)
|
||||||
|
|
||||||
@@ -121,9 +118,7 @@ func (a *App) start() error {
|
|||||||
a.renderer.AddJSList(a.jsCache)
|
a.renderer.AddJSList(a.jsCache)
|
||||||
|
|
||||||
// Run the renderer
|
// Run the renderer
|
||||||
a.renderer.Run()
|
return a.renderer.Run()
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind allows the user to bind the given object
|
// Bind allows the user to bind the given object
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package wails
|
package wails
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/cmd"
|
"github.com/wailsapp/wails/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,18 +9,17 @@ func (app *App) setupCli() *cmd.Cli {
|
|||||||
|
|
||||||
// Create a new cli
|
// Create a new cli
|
||||||
result := cmd.NewCli(app.config.Title, "Debug build")
|
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 and headless flags
|
||||||
result.
|
result.
|
||||||
StringFlag("loglevel", "Sets the log level [debug|info|error|panic|fatal]. Default debug", &app.logLevel).
|
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).
|
// BoolFlag("headless", "Runs the app in headless mode", &app.headless).
|
||||||
Action(app.start)
|
Action(app.start)
|
||||||
|
|
||||||
// Banner
|
// Banner
|
||||||
result.PreRun(func(cli *cmd.Cli) error {
|
result.PreRun(func(cli *cmd.Cli) error {
|
||||||
log := cmd.NewLogger()
|
log := cmd.NewLogger()
|
||||||
log.PrintBanner()
|
|
||||||
fmt.Println()
|
|
||||||
log.YellowUnderline(app.config.Title + " - Debug Build")
|
log.YellowUnderline(app.config.Title + " - Debug Build")
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,11 +4,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/dchest/htmlmin"
|
"github.com/dchest/htmlmin"
|
||||||
"github.com/gobuffalo/packr"
|
"github.com/leaanthony/mewn"
|
||||||
)
|
)
|
||||||
|
|
||||||
var assets = packr.NewBox("./assets/default")
|
|
||||||
|
|
||||||
// AppConfig is the configuration structure used when creating a Wails App object
|
// AppConfig is the configuration structure used when creating a Wails App object
|
||||||
type AppConfig struct {
|
type AppConfig struct {
|
||||||
Width, Height int
|
Width, Height int
|
||||||
@@ -43,7 +41,7 @@ func (a *AppConfig) merge(in *AppConfig) error {
|
|||||||
a.HTML = strings.TrimSpace(inlineHTML)
|
a.HTML = strings.TrimSpace(inlineHTML)
|
||||||
|
|
||||||
// Deduce whether this is a full html page or a fragment
|
// Deduce whether this is a full html page or a fragment
|
||||||
// The document is determined to be a fragment if an HMTL
|
// The document is determined to be a fragment if an HTML
|
||||||
// tag exists and is located before the first div tag
|
// tag exists and is located before the first div tag
|
||||||
HTMLTagIndex := strings.Index(a.HTML, "<html")
|
HTMLTagIndex := strings.Index(a.HTML, "<html")
|
||||||
DivTagIndex := strings.Index(a.HTML, "<div")
|
DivTagIndex := strings.Index(a.HTML, "<div")
|
||||||
@@ -85,7 +83,7 @@ func newAppConfig(userConfig *AppConfig) (*AppConfig, error) {
|
|||||||
Resizable: true,
|
Resizable: true,
|
||||||
Title: "My Wails App",
|
Title: "My Wails App",
|
||||||
Colour: "#FFF", // White by default
|
Colour: "#FFF", // White by default
|
||||||
HTML: BoxString(&defaultAssets, "default.html"),
|
HTML: mewn.String("./wailsruntimeassets/default/default.html"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if userConfig != nil {
|
if userConfig != nil {
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<div id="app"></div>
|
|
||||||
2
assets/default/jquery.3.3.1.min.js
vendored
@@ -1,229 +0,0 @@
|
|||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Wails Headless</title>
|
|
||||||
<style>
|
|
||||||
.wails-reconnect-overlay {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: rgba(0, 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: white;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* https://codepen.io/EastingAndNorthing/pen/aNWrZz - Cheers Mark! */
|
|
||||||
|
|
||||||
.wails-reconnect-overlay-loadingspinner {
|
|
||||||
pointer-events: none;
|
|
||||||
width: 2.5em;
|
|
||||||
height: 2.5em;
|
|
||||||
border: 0.4em solid transparent;
|
|
||||||
border-color: #eee;
|
|
||||||
border-top-color: #3E67EC;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: loadingspin 1s linear infinite;
|
|
||||||
margin: auto;
|
|
||||||
padding: 2.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes loadingspin {
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="wails-reconnect-overlay">
|
|
||||||
<div class="wails-reconnect-overlay-content">
|
|
||||||
<div class="wails-reconnect-overlay-title">Disconnected</div><br>
|
|
||||||
<div class="wails-reconnect-overlay-loadingspinner"></div><br>
|
|
||||||
<div class="wails-reconnect-overlay-message">Waiting for backend</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="app"></div>
|
|
||||||
|
|
||||||
<script id="wails-headless-runtime">
|
|
||||||
(function () {
|
|
||||||
|
|
||||||
var websocket = null;
|
|
||||||
var connectTimer = null;
|
|
||||||
var reconnectOverlay = document.querySelector(".wails-reconnect-overlay");
|
|
||||||
var connectionState = "disconnected";
|
|
||||||
|
|
||||||
function showReconnectOverlay() {
|
|
||||||
reconnectOverlay.style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideReconnectOverlay() {
|
|
||||||
reconnectOverlay.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
window.external = {
|
|
||||||
invoke: function (msg) {
|
|
||||||
websocket.send(msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Adds a script to the Dom.
|
|
||||||
// Removes it if second parameter is true.
|
|
||||||
function addScript(script, remove) {
|
|
||||||
var s = document.createElement("script");
|
|
||||||
s.textContent = script;
|
|
||||||
document.head.appendChild(s);
|
|
||||||
|
|
||||||
// Remove internal messages from the DOM
|
|
||||||
if (remove) {
|
|
||||||
s.parentNode.removeChild(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adapted from webview - thanks zserge!
|
|
||||||
function injectCSS(css) {
|
|
||||||
var elem = document.createElement("style");
|
|
||||||
elem.setAttribute("type", "text/css");
|
|
||||||
if (elem.styleSheet) {
|
|
||||||
elem.styleSheet.cssText = css;
|
|
||||||
} else {
|
|
||||||
elem.appendChild(document.createTextNode(css));
|
|
||||||
}
|
|
||||||
var head = document.head || document.getElementsByTagName("head")[0];
|
|
||||||
head.appendChild(elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleConnect() {
|
|
||||||
log("Connected to backend");
|
|
||||||
hideReconnectOverlay();
|
|
||||||
clearInterval(connectTimer);
|
|
||||||
websocket.onclose = handleDisconnect;
|
|
||||||
websocket.onmessage = handleMessage;
|
|
||||||
connectionState = "connected";
|
|
||||||
// websocket.onerror = function () { }
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDisconnect() {
|
|
||||||
log("Disconnected from backend");
|
|
||||||
websocket = null;
|
|
||||||
connectionState = "disconnected";
|
|
||||||
showReconnectOverlay();
|
|
||||||
connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
function connect() {
|
|
||||||
connectTimer = setInterval(function () {
|
|
||||||
if (websocket == null) {
|
|
||||||
websocket = new WebSocket("ws://localhost:34115/ws")
|
|
||||||
websocket.onopen = handleConnect;
|
|
||||||
websocket.onerror = function (e) {
|
|
||||||
e.stopImmediatePropagation();
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
websocket = null;
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
function log(message) {
|
|
||||||
console.log(
|
|
||||||
"%c wails headless %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 handleMessage(message) {
|
|
||||||
// As a bridge we ignore js and css injections
|
|
||||||
debugger;
|
|
||||||
|
|
||||||
switch (message.data[0]) {
|
|
||||||
// Wails library - inject!
|
|
||||||
case "w":
|
|
||||||
addScript(message.data.slice(1));
|
|
||||||
|
|
||||||
// Now wails runtime is loaded, wails for the ready event
|
|
||||||
// and callback to the main app
|
|
||||||
window.wails.events.on("wails:loaded", function () {
|
|
||||||
log("Wails Ready");
|
|
||||||
});
|
|
||||||
log("Loaded Wails Runtime");
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Notification
|
|
||||||
case "n":
|
|
||||||
log("Notification: " + message.data.slice(1))
|
|
||||||
addScript(message.data.slice(1), true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Binding
|
|
||||||
case "b":
|
|
||||||
var binding = message.data.slice(1)
|
|
||||||
//log("Binding: " + binding)
|
|
||||||
window.wails._.newBinding(binding);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Call back
|
|
||||||
case "c":
|
|
||||||
var callbackData = message.data.slice(1);
|
|
||||||
log("Callback = " + callbackData);
|
|
||||||
window.wails._.callback(callbackData);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// CSS
|
|
||||||
case "s":
|
|
||||||
addScript(message.data.slice(1), true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// JS
|
|
||||||
case "j":
|
|
||||||
addScript(message.data.slice(1));
|
|
||||||
break;
|
|
||||||
|
|
||||||
// HTML
|
|
||||||
case "h":
|
|
||||||
addScript(message.data.slice(1), true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
log("Ignored message: " + message.data.slice(0, 100))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connect();
|
|
||||||
|
|
||||||
}());
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -153,8 +153,11 @@ func (b *boundFunction) setInputValue(index int, typ reflect.Type, val interface
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Do the conversion
|
// Translate javascript null values
|
||||||
result = reflect.ValueOf(val).Convert(typ)
|
if val == nil {
|
||||||
|
result = reflect.Zero(typ)
|
||||||
|
} else {
|
||||||
|
result = reflect.ValueOf(val).Convert(typ)
|
||||||
|
}
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,8 +163,71 @@ func (b *bindingManager) bind(object interface{}) {
|
|||||||
b.objectsToBind = append(b.objectsToBind, object)
|
b.objectsToBind = append(b.objectsToBind, object)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *bindingManager) processFunctionCall(callData *callData) (interface{}, error) {
|
||||||
|
// Return values
|
||||||
|
var result []reflect.Value
|
||||||
|
var err error
|
||||||
|
|
||||||
|
function := b.functions[callData.BindingName]
|
||||||
|
if function == nil {
|
||||||
|
return nil, fmt.Errorf("Invalid function name '%s'", callData.BindingName)
|
||||||
|
}
|
||||||
|
result, err = function.call(callData.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have an error return type?
|
||||||
|
if function.hasErrorReturnType {
|
||||||
|
// We do - last result is an error type
|
||||||
|
// Check if the last result was nil
|
||||||
|
b.log.Debugf("# of return types: %d", len(function.returnTypes))
|
||||||
|
b.log.Debugf("# of results: %d", len(result))
|
||||||
|
errorResult := result[len(function.returnTypes)-1]
|
||||||
|
if !errorResult.IsNil() {
|
||||||
|
// It wasn't - we have an error
|
||||||
|
return nil, errorResult.Interface().(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result[0].Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bindingManager) processMethodCall(callData *callData) (interface{}, error) {
|
||||||
|
// Return values
|
||||||
|
var result []reflect.Value
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// do we have this method?
|
||||||
|
method := b.methods[callData.BindingName]
|
||||||
|
if method == nil {
|
||||||
|
return nil, fmt.Errorf("Invalid method name '%s'", callData.BindingName)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err = method.call(callData.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have an error return type?
|
||||||
|
if method.hasErrorReturnType {
|
||||||
|
// We do - last result is an error type
|
||||||
|
// Check if the last result was nil
|
||||||
|
b.log.Debugf("# of return types: %d", len(method.returnTypes))
|
||||||
|
b.log.Debugf("# of results: %d", len(result))
|
||||||
|
errorResult := result[len(method.returnTypes)-1]
|
||||||
|
if !errorResult.IsNil() {
|
||||||
|
// It wasn't - we have an error
|
||||||
|
return nil, errorResult.Interface().(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if result != nil {
|
||||||
|
return result[0].Interface(), nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// process an incoming call request
|
// process an incoming call request
|
||||||
func (b *bindingManager) processCall(callData *callData) (interface{}, error) {
|
func (b *bindingManager) processCall(callData *callData) (result interface{}, err error) {
|
||||||
b.log.Debugf("Wanting to call %s", callData.BindingName)
|
b.log.Debugf("Wanting to call %s", callData.BindingName)
|
||||||
|
|
||||||
// Determine if this is function call or method call by the number of
|
// Determine if this is function call or method call by the number of
|
||||||
@@ -176,13 +239,10 @@ func (b *bindingManager) processCall(callData *callData) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return values
|
|
||||||
var result []reflect.Value
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// We need to catch reflect related panics and return
|
// We need to catch reflect related panics and return
|
||||||
// a decent error message
|
// a decent error message
|
||||||
// TODO: DEBUG THIS!
|
// TODO: DEBUG THIS!
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = fmt.Errorf("%s", r.(string))
|
err = fmt.Errorf("%s", r.(string))
|
||||||
@@ -191,59 +251,14 @@ func (b *bindingManager) processCall(callData *callData) (interface{}, error) {
|
|||||||
|
|
||||||
switch dotCount {
|
switch dotCount {
|
||||||
case 1:
|
case 1:
|
||||||
function := b.functions[callData.BindingName]
|
result, err = b.processFunctionCall(callData)
|
||||||
if function == nil {
|
|
||||||
return nil, fmt.Errorf("Invalid function name '%s'", callData.BindingName)
|
|
||||||
}
|
|
||||||
result, err = function.call(callData.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do we have an error return type?
|
|
||||||
if function.hasErrorReturnType {
|
|
||||||
// We do - last result is an error type
|
|
||||||
// Check if the last result was nil
|
|
||||||
b.log.Debugf("# of return types: %d", len(function.returnTypes))
|
|
||||||
b.log.Debugf("# of results: %d", len(result))
|
|
||||||
errorResult := result[len(function.returnTypes)-1]
|
|
||||||
if !errorResult.IsNil() {
|
|
||||||
// It wasn't - we have an error
|
|
||||||
return nil, errorResult.Interface().(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result[0].Interface(), nil
|
|
||||||
case 2:
|
case 2:
|
||||||
// do we have this method?
|
result, err = b.processMethodCall(callData)
|
||||||
method := b.methods[callData.BindingName]
|
|
||||||
if method == nil {
|
|
||||||
return nil, fmt.Errorf("Invalid method name '%s'", callData.BindingName)
|
|
||||||
}
|
|
||||||
result, err = method.call(callData.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do we have an error return type?
|
|
||||||
if method.hasErrorReturnType {
|
|
||||||
// We do - last result is an error type
|
|
||||||
// Check if the last result was nil
|
|
||||||
b.log.Debugf("# of return types: %d", len(method.returnTypes))
|
|
||||||
b.log.Debugf("# of results: %d", len(result))
|
|
||||||
errorResult := result[len(method.returnTypes)-1]
|
|
||||||
if !errorResult.IsNil() {
|
|
||||||
// It wasn't - we have an error
|
|
||||||
return nil, errorResult.Interface().(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if result != nil {
|
|
||||||
return result[0].Interface(), nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Invalid binding name '%s'", callData.BindingName)
|
result = nil
|
||||||
|
err = fmt.Errorf("Invalid binding name '%s'", callData.BindingName)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// callWailsInitMethods calls all of the WailsInit methods that were
|
// callWailsInitMethods calls all of the WailsInit methods that were
|
||||||
|
|||||||
10
cmd/build.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
const (
|
||||||
|
// BuildModeProd indicates we are building for prod mode
|
||||||
|
BuildModeProd = "prod"
|
||||||
|
// BuildModeDebug indicates we are building for debug mode
|
||||||
|
BuildModeDebug = "debug"
|
||||||
|
// BuildModeBridge indicates we are building for bridge mode
|
||||||
|
BuildModeBridge = "bridge"
|
||||||
|
)
|
||||||
24
cmd/cli.go
@@ -96,6 +96,7 @@ type Command struct {
|
|||||||
flagCount int
|
flagCount int
|
||||||
log *Logger
|
log *Logger
|
||||||
helpFlag bool
|
helpFlag bool
|
||||||
|
hidden bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCommand creates a new Command
|
// NewCommand creates a new Command
|
||||||
@@ -106,6 +107,7 @@ func NewCommand(name string, description string, app *Cli, parentCommandPath str
|
|||||||
SubCommandsMap: make(map[string]*Command),
|
SubCommandsMap: make(map[string]*Command),
|
||||||
App: app,
|
App: app,
|
||||||
log: NewLogger(),
|
log: NewLogger(),
|
||||||
|
hidden: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up command path
|
// Set up command path
|
||||||
@@ -194,10 +196,8 @@ func (c *Command) Action(callback Action) *Command {
|
|||||||
|
|
||||||
// PrintHelp - Output the help text for this command
|
// PrintHelp - Output the help text for this command
|
||||||
func (c *Command) PrintHelp() {
|
func (c *Command) PrintHelp() {
|
||||||
versionString := c.AppVersion
|
c.log.PrintBanner()
|
||||||
if versionString != "" {
|
|
||||||
versionString = " " + versionString
|
|
||||||
}
|
|
||||||
commandTitle := c.CommandPath
|
commandTitle := c.CommandPath
|
||||||
if c.Shortdescription != "" {
|
if c.Shortdescription != "" {
|
||||||
commandTitle += " - " + c.Shortdescription
|
commandTitle += " - " + c.Shortdescription
|
||||||
@@ -211,10 +211,12 @@ func (c *Command) PrintHelp() {
|
|||||||
fmt.Println(c.Longdescription + "\n")
|
fmt.Println(c.Longdescription + "\n")
|
||||||
}
|
}
|
||||||
if len(c.SubCommands) > 0 {
|
if len(c.SubCommands) > 0 {
|
||||||
fmt.Println("")
|
|
||||||
c.log.White("Available commands:")
|
c.log.White("Available commands:")
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
for _, subcommand := range c.SubCommands {
|
for _, subcommand := range c.SubCommands {
|
||||||
|
if subcommand.isHidden() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
spacer := strings.Repeat(" ", 3+c.longestSubcommand-len(subcommand.Name))
|
spacer := strings.Repeat(" ", 3+c.longestSubcommand-len(subcommand.Name))
|
||||||
isDefault := ""
|
isDefault := ""
|
||||||
if subcommand.isDefaultCommand() {
|
if subcommand.isDefaultCommand() {
|
||||||
@@ -222,9 +224,9 @@ func (c *Command) PrintHelp() {
|
|||||||
}
|
}
|
||||||
fmt.Printf(" %s%s%s %s\n", subcommand.Name, spacer, subcommand.Shortdescription, isDefault)
|
fmt.Printf(" %s%s%s %s\n", subcommand.Name, spacer, subcommand.Shortdescription, isDefault)
|
||||||
}
|
}
|
||||||
|
fmt.Println("")
|
||||||
}
|
}
|
||||||
if c.flagCount > 0 {
|
if c.flagCount > 0 {
|
||||||
fmt.Println("")
|
|
||||||
c.log.White("Flags:")
|
c.log.White("Flags:")
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
c.Flags.SetOutput(os.Stdout)
|
c.Flags.SetOutput(os.Stdout)
|
||||||
@@ -240,6 +242,16 @@ func (c *Command) isDefaultCommand() bool {
|
|||||||
return c.App.defaultCommand == c
|
return c.App.defaultCommand == c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isHidden returns true if the command is a hidden command
|
||||||
|
func (c *Command) isHidden() bool {
|
||||||
|
return c.hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hidden hides the command from the Help system
|
||||||
|
func (c *Command) Hidden() {
|
||||||
|
c.hidden = true
|
||||||
|
}
|
||||||
|
|
||||||
// Command - Defines a subcommand
|
// Command - Defines a subcommand
|
||||||
func (c *Command) Command(name, description string) *Command {
|
func (c *Command) Command(name, description string) *Command {
|
||||||
result := NewCommand(name, description, c.App, c.CommandPath)
|
result := NewCommand(name, description, c.App, c.CommandPath)
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io/ioutil"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FrameworkMetadata contains information about a given framework
|
|
||||||
type FrameworkMetadata struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
BuildTag string `json:"buildtag"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility function for creating new FrameworkMetadata structs
|
|
||||||
func loadFrameworkMetadata(pathToMetadataJSON string) (*FrameworkMetadata, error) {
|
|
||||||
result := &FrameworkMetadata{}
|
|
||||||
configData, err := ioutil.ReadFile(pathToMetadataJSON)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Load and unmarshall!
|
|
||||||
err = json.Unmarshal(configData, result)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFrameworks returns information about all the available frameworks
|
|
||||||
func GetFrameworks() ([]*FrameworkMetadata, error) {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Calculate framework base dir
|
|
||||||
_, filename, _, _ := runtime.Caller(1)
|
|
||||||
frameworksBaseDir := filepath.Join(path.Dir(filename), "frameworks")
|
|
||||||
|
|
||||||
// Get the subdirectories
|
|
||||||
fs := NewFSHelper()
|
|
||||||
frameworkDirs, err := fs.GetSubdirs(frameworksBaseDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare result
|
|
||||||
result := []*FrameworkMetadata{}
|
|
||||||
|
|
||||||
// Iterate framework directories, looking for metadata.json files
|
|
||||||
for _, frameworkDir := range frameworkDirs {
|
|
||||||
var frameworkMetadata FrameworkMetadata
|
|
||||||
metadataFile := filepath.Join(frameworkDir, "metadata.json")
|
|
||||||
jsonData, err := ioutil.ReadFile(metadataFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(jsonData, &frameworkMetadata)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result = append(result, &frameworkMetadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read in framework metadata
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
// +build frameworkbootstrap4
|
|
||||||
|
|
||||||
package frameworks
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gobuffalo/packr"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
assets := packr.NewBox("./bootstrap4default/assets")
|
|
||||||
FrameworkToUse = &Framework{
|
|
||||||
Name: "Bootstrap 4",
|
|
||||||
JS: BoxString(&assets, "bootstrap.bundle.min.js"),
|
|
||||||
CSS: BoxString(&assets, "bootstrap.min.css"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
// +build frameworkbootstrap4
|
|
||||||
|
|
||||||
package bootstrap4
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gobuffalo/packr"
|
|
||||||
"github.com/wailsapp/wails/frameworks"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
assets := packr.NewBox("./assets")
|
|
||||||
frameworks.FrameworkToUse = &frameworks.Framework{
|
|
||||||
Name: "Bootstrap 4",
|
|
||||||
JS: BoxString(&assets, "bootstrap.bundle.min.js"),
|
|
||||||
CSS: BoxString(&assets, "bootstrap.min.css"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"Name": "Bootstrap 4",
|
|
||||||
"Description": "Standard Bootstrap 4 with default theme",
|
|
||||||
"BuildTag": "frameworkbootstrap4"
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
// +build frameworkbootstrap4lux
|
|
||||||
|
|
||||||
package frameworks
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gobuffalo/packr"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
assets := packr.NewBox("./bootstrap4lux/assets")
|
|
||||||
FrameworkToUse = &Framework{
|
|
||||||
Name: "Bootstrap 4 (Lux)",
|
|
||||||
JS: BoxString(&assets, "bootstrap.bundle.min.js"),
|
|
||||||
CSS: BoxString(&assets, "bootstrap.min.css"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2011-2018 Twitter, Inc.
|
|
||||||
Copyright (c) 2011-2018 The Bootstrap Authors
|
|
||||||
|
|
||||||
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.
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
// +build frameworkbootstrap4lux
|
|
||||||
|
|
||||||
package bootstrap4
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gobuffalo/packr"
|
|
||||||
"github.com/wailsapp/wails/frameworks"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
assets := packr.NewBox("./assets")
|
|
||||||
frameworks.FrameworkToUse = &frameworks.Framework{
|
|
||||||
Name: "Bootstrap 4 (Lux)",
|
|
||||||
JS: BoxString(&assets, "bootstrap.bundle.min.js"),
|
|
||||||
CSS: BoxString(&assets, "bootstrap.min.css"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"Name": "Bootstrap 4 (Lux)",
|
|
||||||
"Description": "Bootstrap with Lux theme",
|
|
||||||
"BuildTag": "frameworkbootstrap4lux"
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package frameworks
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/gobuffalo/packr"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Framework has details about a specific framework
|
|
||||||
type Framework struct {
|
|
||||||
Name string
|
|
||||||
JS string
|
|
||||||
CSS string
|
|
||||||
Options string
|
|
||||||
}
|
|
||||||
|
|
||||||
// FrameworkToUse is the framework we will use when building
|
|
||||||
// Set by `wails init`, used by `wails build`
|
|
||||||
var FrameworkToUse *Framework
|
|
||||||
|
|
||||||
// BoxString extracts a string from a packr box
|
|
||||||
func BoxString(box *packr.Box, filename string) string {
|
|
||||||
result, err := box.FindString(filename)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
97
cmd/fs.go
@@ -1,13 +1,19 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FSHelper - Wrapper struct for File System utility commands
|
// FSHelper - Wrapper struct for File System utility commands
|
||||||
@@ -41,6 +47,14 @@ func (fs *FSHelper) FileExists(path string) bool {
|
|||||||
return fi.Mode().IsRegular()
|
return fi.Mode().IsRegular()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateFile creates a file at the given filename location with the contents
|
||||||
|
// set to the given data. It will create intermediary directories if needed.
|
||||||
|
func (fs *FSHelper) CreateFile(filename string, data []byte) error {
|
||||||
|
// Ensure directory exists
|
||||||
|
fs.MkDirs(filepath.Dir(filename))
|
||||||
|
return ioutil.WriteFile(filename, data, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
// MkDirs creates the given nested directories.
|
// MkDirs creates the given nested directories.
|
||||||
// Returns error on failure
|
// Returns error on failure
|
||||||
func (fs *FSHelper) MkDirs(fullPath string, mode ...os.FileMode) error {
|
func (fs *FSHelper) MkDirs(fullPath string, mode ...os.FileMode) error {
|
||||||
@@ -80,11 +94,46 @@ func (fs *FSHelper) Cwd() string {
|
|||||||
return cwd
|
return cwd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveFile removes the given filename
|
||||||
|
func (fs *FSHelper) RemoveFile(filename string) error {
|
||||||
|
return os.Remove(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveFiles removes the given filenames
|
||||||
|
func (fs *FSHelper) RemoveFiles(files []string) error {
|
||||||
|
for _, filename := range files {
|
||||||
|
err := os.Remove(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dir struct {
|
||||||
|
localPath string
|
||||||
|
fullPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FSHelper) Dir(dir string) (*Dir, error) {
|
||||||
|
fullPath, err := filepath.Abs(dir)
|
||||||
|
return &Dir{fullPath: fullPath}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FSHelper) LocalDir(dir string) (*Dir, error) {
|
||||||
|
_, filename, _, _ := runtime.Caller(1)
|
||||||
|
fullPath, err := filepath.Abs(filepath.Join(path.Dir(filename), dir))
|
||||||
|
return &Dir{
|
||||||
|
localPath: dir,
|
||||||
|
fullPath: fullPath,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetSubdirs will return a list of FQPs to subdirectories in the given directory
|
// GetSubdirs will return a list of FQPs to subdirectories in the given directory
|
||||||
func (fs *FSHelper) GetSubdirs(dir string) (map[string]string, error) {
|
func (d *Dir) GetSubdirs() (map[string]string, error) {
|
||||||
|
|
||||||
// Read in the directory information
|
// Read in the directory information
|
||||||
fileInfo, err := ioutil.ReadDir(dir)
|
fileInfo, err := ioutil.ReadDir(d.fullPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -96,25 +145,65 @@ func (fs *FSHelper) GetSubdirs(dir string) (map[string]string, error) {
|
|||||||
// map["directoryName"] = "path/to/directoryName"
|
// map["directoryName"] = "path/to/directoryName"
|
||||||
for _, file := range fileInfo {
|
for _, file := range fileInfo {
|
||||||
if file.IsDir() {
|
if file.IsDir() {
|
||||||
subdirs[file.Name()] = filepath.Join(dir, file.Name())
|
subdirs[file.Name()] = filepath.Join(d.fullPath, file.Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return subdirs, nil
|
return subdirs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllFilenames returns all filename in and below this directory
|
||||||
|
func (d *Dir) GetAllFilenames() (*slicer.StringSlicer, error) {
|
||||||
|
result := slicer.String()
|
||||||
|
err := filepath.Walk(d.fullPath, func(dir string, info os.FileInfo, err error) error {
|
||||||
|
if dir == d.fullPath {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't copy template metadata
|
||||||
|
result.Add(dir)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
// MkDir creates the given directory.
|
// MkDir creates the given directory.
|
||||||
// Returns error on failure
|
// Returns error on failure
|
||||||
func (fs *FSHelper) MkDir(dir string) error {
|
func (fs *FSHelper) MkDir(dir string) error {
|
||||||
return os.Mkdir(dir, 0700)
|
return os.Mkdir(dir, 0700)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveAsJSON saves the JSON representation of the given data to the given filename
|
||||||
|
func (fs *FSHelper) SaveAsJSON(data interface{}, filename string) error {
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
e := json.NewEncoder(&buf)
|
||||||
|
e.SetEscapeHTML(false)
|
||||||
|
e.SetIndent("", " ")
|
||||||
|
e.Encode(data)
|
||||||
|
|
||||||
|
err := ioutil.WriteFile(filename, buf.Bytes(), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// LoadAsString will attempt to load the given file and return
|
// LoadAsString will attempt to load the given file and return
|
||||||
// its contents as a string
|
// its contents as a string
|
||||||
func (fs *FSHelper) LoadAsString(filename string) (string, error) {
|
func (fs *FSHelper) LoadAsString(filename string) (string, error) {
|
||||||
bytes, err := ioutil.ReadFile(filename)
|
bytes, err := fs.LoadAsBytes(filename)
|
||||||
return string(bytes), err
|
return string(bytes), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadAsBytes returns the contents of the file as a byte slice
|
||||||
|
func (fs *FSHelper) LoadAsBytes(filename string) ([]byte, error) {
|
||||||
|
return ioutil.ReadFile(filename)
|
||||||
|
}
|
||||||
|
|
||||||
// FileMD5 returns the md5sum of the given file
|
// FileMD5 returns the md5sum of the given file
|
||||||
func (fs *FSHelper) FileMD5(filename string) (string, error) {
|
func (fs *FSHelper) FileMD5(filename string) (string, error) {
|
||||||
f, err := os.Open(filename)
|
f, err := os.Open(filename)
|
||||||
|
|||||||
108
cmd/github.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GitHubHelper is a utility class for interacting with GitHub
|
||||||
|
type GitHubHelper struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGitHubHelper returns a new GitHub Helper
|
||||||
|
func NewGitHubHelper() *GitHubHelper {
|
||||||
|
return &GitHubHelper{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionTags gets the list of tags on the Wails repo
|
||||||
|
// It retuns a list of sorted tags in descending order
|
||||||
|
func (g *GitHubHelper) GetVersionTags() ([]*SemanticVersion, error) {
|
||||||
|
|
||||||
|
result := []*SemanticVersion{}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
resp, err := http.Get("https://api.github.com/repos/wailsapp/wails/tags")
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []map[string]interface{}{}
|
||||||
|
err = json.Unmarshal(body, &data)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert tag data to Version structs
|
||||||
|
for _, tag := range data {
|
||||||
|
version := tag["name"].(string)
|
||||||
|
semver, err := NewSemanticVersion(version)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
result = append(result, semver)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse Sort
|
||||||
|
sort.Sort(sort.Reverse(SemverCollection(result)))
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestStableRelease gets the latest stable release on GitHub
|
||||||
|
func (g *GitHubHelper) GetLatestStableRelease() (result *SemanticVersion, err error) {
|
||||||
|
|
||||||
|
tags, err := g.GetVersionTags()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
if tag.IsRelease() {
|
||||||
|
return tag, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no release tag found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestPreRelease gets the latest prerelease on GitHub
|
||||||
|
func (g *GitHubHelper) GetLatestPreRelease() (result *SemanticVersion, err error) {
|
||||||
|
|
||||||
|
tags, err := g.GetVersionTags()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
if tag.IsPreRelease() {
|
||||||
|
return tag, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no prerelease tag found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidTag returns true if the given string is a valid tag
|
||||||
|
func (g *GitHubHelper) IsValidTag(tagVersion string) (bool, error) {
|
||||||
|
if tagVersion[0] == 'v' {
|
||||||
|
tagVersion = tagVersion[1:]
|
||||||
|
}
|
||||||
|
tags, err := g.GetVersionTags()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
if tag.String() == tagVersion {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
306
cmd/helpers.go
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mewn "github.com/leaanthony/mewn"
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
|
"github.com/leaanthony/spinner"
|
||||||
|
)
|
||||||
|
|
||||||
|
var fs = NewFSHelper()
|
||||||
|
|
||||||
|
// ValidateFrontendConfig checks if the frontend config is valid
|
||||||
|
func ValidateFrontendConfig(projectOptions *ProjectOptions) error {
|
||||||
|
if projectOptions.FrontEnd.Dir == "" {
|
||||||
|
return fmt.Errorf("Frontend directory not set in project.json")
|
||||||
|
}
|
||||||
|
if projectOptions.FrontEnd.Build == "" {
|
||||||
|
return fmt.Errorf("Frontend build command not set in project.json")
|
||||||
|
}
|
||||||
|
if projectOptions.FrontEnd.Install == "" {
|
||||||
|
return fmt.Errorf("Frontend install command not set in project.json")
|
||||||
|
}
|
||||||
|
if projectOptions.FrontEnd.Bridge == "" {
|
||||||
|
return fmt.Errorf("Frontend bridge config not set in project.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstallGoDependencies will run go get in the current directory
|
||||||
|
func InstallGoDependencies() error {
|
||||||
|
depSpinner := spinner.New("Ensuring Dependencies are up to date...")
|
||||||
|
depSpinner.SetSpinSpeed(50)
|
||||||
|
depSpinner.Start()
|
||||||
|
err := NewProgramHelper().RunCommand("go get")
|
||||||
|
if err != nil {
|
||||||
|
depSpinner.Error()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
depSpinner.Success()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildApplication will attempt to build the project based on the given inputs
|
||||||
|
func BuildApplication(binaryName string, forceRebuild bool, buildMode string, packageApp bool, projectOptions *ProjectOptions) error {
|
||||||
|
|
||||||
|
// Generate Windows assets if needed
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
cleanUp := !packageApp
|
||||||
|
err := NewPackageHelper().PackageWindows(projectOptions, cleanUp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Mewn is installed
|
||||||
|
err := CheckMewn()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
compileMessage := "Packing + Compiling project"
|
||||||
|
|
||||||
|
if buildMode == BuildModeDebug {
|
||||||
|
compileMessage += " (Debug Mode)"
|
||||||
|
}
|
||||||
|
|
||||||
|
packSpinner := spinner.New(compileMessage + "...")
|
||||||
|
packSpinner.SetSpinSpeed(50)
|
||||||
|
packSpinner.Start()
|
||||||
|
|
||||||
|
buildCommand := slicer.String()
|
||||||
|
buildCommand.Add("mewn")
|
||||||
|
|
||||||
|
if buildMode == BuildModeBridge {
|
||||||
|
// Ignore errors
|
||||||
|
buildCommand.Add("-i")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildCommand.Add("build")
|
||||||
|
|
||||||
|
if binaryName != "" {
|
||||||
|
buildCommand.Add("-o")
|
||||||
|
buildCommand.Add(binaryName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are forcing a rebuild
|
||||||
|
if forceRebuild {
|
||||||
|
buildCommand.Add("-a")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup ld flags
|
||||||
|
ldflags := "-w -s "
|
||||||
|
if buildMode == BuildModeDebug {
|
||||||
|
ldflags = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add windows flags
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
ldflags += "-H windowsgui "
|
||||||
|
}
|
||||||
|
|
||||||
|
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode
|
||||||
|
|
||||||
|
buildCommand.AddSlice([]string{"-ldflags", ldflags})
|
||||||
|
err = NewProgramHelper().RunCommandArray(buildCommand.AsSlice())
|
||||||
|
if err != nil {
|
||||||
|
packSpinner.Error()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
packSpinner.Success()
|
||||||
|
|
||||||
|
// packageApp
|
||||||
|
if packageApp {
|
||||||
|
err = PackageApplication(projectOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageApplication will attempt to package the application in a platform dependent way
|
||||||
|
func PackageApplication(projectOptions *ProjectOptions) error {
|
||||||
|
// Package app
|
||||||
|
message := "Generating .app"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
err := CheckWindres()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
message = "Generating resource bundle"
|
||||||
|
}
|
||||||
|
packageSpinner := spinner.New(message)
|
||||||
|
packageSpinner.SetSpinSpeed(50)
|
||||||
|
packageSpinner.Start()
|
||||||
|
err := NewPackageHelper().Package(projectOptions)
|
||||||
|
if err != nil {
|
||||||
|
packageSpinner.Error()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
packageSpinner.Success()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildFrontend runs the given build command
|
||||||
|
func BuildFrontend(buildCommand string) error {
|
||||||
|
buildFESpinner := spinner.New("Building frontend...")
|
||||||
|
buildFESpinner.SetSpinSpeed(50)
|
||||||
|
buildFESpinner.Start()
|
||||||
|
err := NewProgramHelper().RunCommand(buildCommand)
|
||||||
|
if err != nil {
|
||||||
|
buildFESpinner.Error()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buildFESpinner.Success()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckMewn checks if mewn is installed and if not, attempts to fetch it
|
||||||
|
func CheckMewn() (err error) {
|
||||||
|
programHelper := NewProgramHelper()
|
||||||
|
if !programHelper.IsInstalled("mewn") {
|
||||||
|
buildSpinner := spinner.New()
|
||||||
|
buildSpinner.SetSpinSpeed(50)
|
||||||
|
buildSpinner.Start("Installing Mewn asset packer...")
|
||||||
|
err := programHelper.InstallGoPackage("github.com/leaanthony/mewn/cmd/mewn")
|
||||||
|
if err != nil {
|
||||||
|
buildSpinner.Error()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buildSpinner.Success()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckWindres checks if Windres is installed and if not, aborts
|
||||||
|
func CheckWindres() (err error) {
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
programHelper := NewProgramHelper()
|
||||||
|
if !programHelper.IsInstalled("windres") {
|
||||||
|
return fmt.Errorf("windres not installed. It comes by default with mingw. Ensure you have installed mingw correctly")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstallFrontendDeps attempts to install the frontend dependencies based on the given options
|
||||||
|
func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forceRebuild bool, caller string) error {
|
||||||
|
|
||||||
|
// Install frontend deps
|
||||||
|
err := os.Chdir(projectOptions.FrontEnd.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if frontend deps have been updated
|
||||||
|
feSpinner := spinner.New("Ensuring frontend dependencies are up to date (This may take a while)")
|
||||||
|
feSpinner.SetSpinSpeed(50)
|
||||||
|
feSpinner.Start()
|
||||||
|
|
||||||
|
requiresNPMInstall := true
|
||||||
|
|
||||||
|
// Read in package.json MD5
|
||||||
|
fs := NewFSHelper()
|
||||||
|
packageJSONMD5, err := fs.FileMD5("package.json")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const md5sumFile = "package.json.md5"
|
||||||
|
|
||||||
|
// If we aren't forcing the install and the md5sum file exists
|
||||||
|
if !forceRebuild && fs.FileExists(md5sumFile) {
|
||||||
|
// Yes - read contents
|
||||||
|
savedMD5sum, err := fs.LoadAsString(md5sumFile)
|
||||||
|
// File exists
|
||||||
|
if err == nil {
|
||||||
|
// Compare md5
|
||||||
|
if savedMD5sum == packageJSONMD5 {
|
||||||
|
// Same - no need for reinstall
|
||||||
|
requiresNPMInstall = false
|
||||||
|
feSpinner.Success("Skipped frontend dependencies (-f to force rebuild)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Md5 sum package.json
|
||||||
|
// Different? Build
|
||||||
|
if requiresNPMInstall || forceRebuild {
|
||||||
|
// Install dependencies
|
||||||
|
err = NewProgramHelper().RunCommand(projectOptions.FrontEnd.Install)
|
||||||
|
if err != nil {
|
||||||
|
feSpinner.Error()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
feSpinner.Success()
|
||||||
|
|
||||||
|
// Update md5sum file
|
||||||
|
ioutil.WriteFile(md5sumFile, []byte(packageJSONMD5), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install the bridge library
|
||||||
|
err = InstallBridge(caller, projectDir, projectOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build frontend
|
||||||
|
err = BuildFrontend(projectOptions.FrontEnd.Build)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeProject attempts to serve up the current project so that it may be connected to
|
||||||
|
// via the Wails bridge
|
||||||
|
func ServeProject(projectOptions *ProjectOptions, logger *Logger) error {
|
||||||
|
go func() {
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
logger.Green(">>>>> To connect, you will need to run '" + projectOptions.FrontEnd.Serve + "' in the '" + projectOptions.FrontEnd.Dir + "' directory <<<<<")
|
||||||
|
}()
|
||||||
|
location, err := filepath.Abs(projectOptions.BinaryName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Yellow("Serving Application: " + location)
|
||||||
|
cmd := exec.Command(location)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
53
cmd/linux.go
@@ -2,6 +2,9 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -10,9 +13,13 @@ type LinuxDistribution int
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Unknown is the catch-all distro
|
// Unknown is the catch-all distro
|
||||||
Unknown LinuxDistribution = 0
|
Unknown LinuxDistribution = iota
|
||||||
// Ubuntu distribution
|
// Ubuntu distribution
|
||||||
Ubuntu LinuxDistribution = 1
|
Ubuntu
|
||||||
|
// Arch linux distribution
|
||||||
|
Arch
|
||||||
|
// RedHat linux distribution
|
||||||
|
RedHat
|
||||||
)
|
)
|
||||||
|
|
||||||
// DistroInfo contains all the information relating to a linux distribution
|
// DistroInfo contains all the information relating to a linux distribution
|
||||||
@@ -49,6 +56,8 @@ func GetLinuxDistroInfo() *DistroInfo {
|
|||||||
switch value {
|
switch value {
|
||||||
case "Ubuntu":
|
case "Ubuntu":
|
||||||
result.Distribution = Ubuntu
|
result.Distribution = Ubuntu
|
||||||
|
case "Arch", "ManjaroLinux":
|
||||||
|
result.Distribution = Arch
|
||||||
}
|
}
|
||||||
case "Description":
|
case "Description":
|
||||||
result.Description = value
|
result.Description = value
|
||||||
@@ -60,20 +69,52 @@ func GetLinuxDistroInfo() *DistroInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// check if /etc/os-release exists
|
||||||
|
} else if _, err := os.Stat("/etc/os-release"); !os.IsNotExist(err) {
|
||||||
|
// read /etc/os-release
|
||||||
|
osRelease, _ := ioutil.ReadFile("/etc/os-release")
|
||||||
|
// compile a regex to find NAME=distro
|
||||||
|
re := regexp.MustCompile(`^NAME=(.*)\n`)
|
||||||
|
// extract the distro name
|
||||||
|
osName := string(re.FindSubmatch(osRelease)[1])
|
||||||
|
// Check distro name against list of RedHat distros
|
||||||
|
if osName == "Fedora" || osName == "CentOS" {
|
||||||
|
//if it matches set result.Distribution to RedHat
|
||||||
|
result.Distribution = RedHat
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// DpkgInstalled uses dpkg to see if a package is installed
|
// DpkgInstalled uses dpkg to see if a package is installed
|
||||||
func DpkgInstalled(packageName string) (bool, error) {
|
func DpkgInstalled(packageName string) (bool, error) {
|
||||||
result := false
|
|
||||||
program := NewProgramHelper()
|
program := NewProgramHelper()
|
||||||
dpkg := program.FindProgram("dpkg")
|
dpkg := program.FindProgram("dpkg")
|
||||||
if dpkg == nil {
|
if dpkg == nil {
|
||||||
return false, fmt.Errorf("cannot check dependencies: dpkg not found")
|
return false, fmt.Errorf("cannot check dependencies: dpkg not found")
|
||||||
}
|
}
|
||||||
_, _, exitCode, _ := dpkg.Run("-L", packageName)
|
_, _, exitCode, _ := dpkg.Run("-L", packageName)
|
||||||
result = exitCode == 0
|
return exitCode == 0, nil
|
||||||
return result, nil
|
}
|
||||||
|
|
||||||
|
// PacmanInstalled uses pacman to see if a package is installed.
|
||||||
|
func PacmanInstalled(packageName string) (bool, error) {
|
||||||
|
program := NewProgramHelper()
|
||||||
|
pacman := program.FindProgram("pacman")
|
||||||
|
if pacman == nil {
|
||||||
|
return false, fmt.Errorf("cannot check dependencies: pacman not found")
|
||||||
|
}
|
||||||
|
_, _, exitCode, _ := pacman.Run("-Qs", packageName)
|
||||||
|
return exitCode == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RpmInstalled uses rpm to see if a package is installed
|
||||||
|
func RpmInstalled(packageName string) (bool, error) {
|
||||||
|
program := NewProgramHelper()
|
||||||
|
rpm := program.FindProgram("rpm")
|
||||||
|
if rpm == nil {
|
||||||
|
return false, fmt.Errorf("cannot check dependencies: rpm not found")
|
||||||
|
}
|
||||||
|
_, _, exitCode, _ := rpm.Run("--query", packageName)
|
||||||
|
return exitCode == 0, nil
|
||||||
}
|
}
|
||||||
|
|||||||
12
cmd/log.go
@@ -17,6 +17,7 @@ func NewLogger() *Logger {
|
|||||||
return &Logger{errorOnly: false}
|
return &Logger{errorOnly: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetErrorOnly ensures that only errors are logged out
|
||||||
func (l *Logger) SetErrorOnly(errorOnly bool) {
|
func (l *Logger) SetErrorOnly(errorOnly bool) {
|
||||||
l.errorOnly = errorOnly
|
l.errorOnly = errorOnly
|
||||||
}
|
}
|
||||||
@@ -99,6 +100,17 @@ func (l *Logger) Error(format string, a ...interface{}) {
|
|||||||
color.New(color.FgHiRed).PrintfFunc()("Error: "+format+"\n", a...)
|
color.New(color.FgHiRed).PrintfFunc()("Error: "+format+"\n", a...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrintSmallBanner prints a condensed banner
|
||||||
|
func (l *Logger) PrintSmallBanner(message ...string) {
|
||||||
|
yellow := color.New(color.FgYellow).SprintFunc()
|
||||||
|
red := color.New(color.FgRed).SprintFunc()
|
||||||
|
msg := ""
|
||||||
|
if len(message) > 0 {
|
||||||
|
msg = " - " + message[0]
|
||||||
|
}
|
||||||
|
fmt.Printf("%s %s%s\n", yellow("Wails"), red(Version), msg)
|
||||||
|
}
|
||||||
|
|
||||||
// PrintBanner prints the Wails banner before running commands
|
// PrintBanner prints the Wails banner before running commands
|
||||||
func (l *Logger) PrintBanner() error {
|
func (l *Logger) PrintBanner() error {
|
||||||
banner1 := ` _ __ _ __
|
banner1 := ` _ __ _ __
|
||||||
|
|||||||
@@ -68,15 +68,15 @@ func (b *PackageHelper) getPackageFileBaseDir() string {
|
|||||||
|
|
||||||
// Package the application into a platform specific package
|
// Package the application into a platform specific package
|
||||||
func (b *PackageHelper) Package(po *ProjectOptions) error {
|
func (b *PackageHelper) Package(po *ProjectOptions) error {
|
||||||
// Check we have the exe
|
|
||||||
if !b.fs.FileExists(po.BinaryName) {
|
|
||||||
return fmt.Errorf("cannot bundle non-existant binary file '%s'. Please build with 'wails build' first", po.BinaryName)
|
|
||||||
}
|
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "darwin":
|
case "darwin":
|
||||||
|
// Check we have the exe
|
||||||
|
if !b.fs.FileExists(po.BinaryName) {
|
||||||
|
return fmt.Errorf("cannot bundle non-existent binary file '%s'. Please build with 'wails build' first", po.BinaryName)
|
||||||
|
}
|
||||||
return b.packageOSX(po)
|
return b.packageOSX(po)
|
||||||
case "windows":
|
case "windows":
|
||||||
return fmt.Errorf("windows is not supported at this time. Please see https://github.com/wailsapp/wails/issues/3")
|
return b.PackageWindows(po, false)
|
||||||
case "linux":
|
case "linux":
|
||||||
return fmt.Errorf("linux is not supported at this time. Please see https://github.com/wailsapp/wails/issues/2")
|
return fmt.Errorf("linux is not supported at this time. Please see https://github.com/wailsapp/wails/issues/2")
|
||||||
default:
|
default:
|
||||||
@@ -146,15 +146,73 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = b.packageIcon(resourceDir)
|
err = b.packageIconOSX(resourceDir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *PackageHelper) packageIcon(resourceDir string) error {
|
// PackageWindows packages the application for windows platforms
|
||||||
|
func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
|
||||||
|
basename := strings.TrimSuffix(po.BinaryName, ".exe")
|
||||||
|
|
||||||
|
// Copy icon
|
||||||
|
tgtIconFile := filepath.Join(b.fs.Cwd(), basename+".ico")
|
||||||
|
if !b.fs.FileExists(tgtIconFile) {
|
||||||
|
srcIconfile := filepath.Join(b.getPackageFileBaseDir(), "wails.ico")
|
||||||
|
err := b.fs.CopyFile(srcIconfile, tgtIconFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy manifest
|
||||||
|
tgtManifestFile := filepath.Join(b.fs.Cwd(), basename+".exe.manifest")
|
||||||
|
if !b.fs.FileExists(tgtManifestFile) {
|
||||||
|
srcManifestfile := filepath.Join(b.getPackageFileBaseDir(), "wails.exe.manifest")
|
||||||
|
err := b.fs.CopyFile(srcManifestfile, tgtManifestFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy rc file
|
||||||
|
tgtRCFile := filepath.Join(b.fs.Cwd(), basename+".rc")
|
||||||
|
if !b.fs.FileExists(tgtRCFile) {
|
||||||
|
srcRCfile := filepath.Join(b.getPackageFileBaseDir(), "wails.rc")
|
||||||
|
rcfilebytes, err := ioutil.ReadFile(srcRCfile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rcfiledata := strings.Replace(string(rcfilebytes), "$NAME$", basename, -1)
|
||||||
|
err = ioutil.WriteFile(tgtRCFile, []byte(rcfiledata), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build syso
|
||||||
|
sysofile := filepath.Join(b.fs.Cwd(), basename+"-res.syso")
|
||||||
|
windresCommand := []string{"windres", "-o", sysofile, tgtRCFile}
|
||||||
|
err := NewProgramHelper().RunCommandArray(windresCommand)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
if cleanUp {
|
||||||
|
filesToDelete := []string{tgtIconFile, tgtManifestFile, tgtRCFile, sysofile}
|
||||||
|
err := b.fs.RemoveFiles(filesToDelete)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PackageHelper) copyIcon(resourceDir string) (string, error) {
|
||||||
|
|
||||||
// TODO: Read this from project.json
|
// TODO: Read this from project.json
|
||||||
const appIconFilename = "appicon.png"
|
const appIconFilename = "appicon.png"
|
||||||
|
|
||||||
srcIcon := path.Join(b.fs.Cwd(), appIconFilename)
|
srcIcon := path.Join(b.fs.Cwd(), appIconFilename)
|
||||||
|
|
||||||
// Check if appicon.png exists
|
// Check if appicon.png exists
|
||||||
@@ -164,14 +222,22 @@ func (b *PackageHelper) packageIcon(resourceDir string) error {
|
|||||||
iconfile := filepath.Join(b.getPackageFileBaseDir(), "icon.png")
|
iconfile := filepath.Join(b.getPackageFileBaseDir(), "icon.png")
|
||||||
iconData, err := ioutil.ReadFile(iconfile)
|
iconData, err := ioutil.ReadFile(iconfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
err = ioutil.WriteFile(srcIcon, iconData, 0644)
|
err = ioutil.WriteFile(srcIcon, iconData, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return srcIcon, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PackageHelper) packageIconOSX(resourceDir string) error {
|
||||||
|
|
||||||
|
srcIcon, err := b.copyIcon(resourceDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
tgtBundle := path.Join(resourceDir, "iconfile.icns")
|
tgtBundle := path.Join(resourceDir, "iconfile.icns")
|
||||||
imageFile, err := os.Open(srcIcon)
|
imageFile, err := os.Open(srcIcon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -189,9 +255,5 @@ func (b *PackageHelper) packageIcon(resourceDir string) error {
|
|||||||
|
|
||||||
}
|
}
|
||||||
defer dest.Close()
|
defer dest.Close()
|
||||||
if err := icns.Encode(dest, srcImg); err != nil {
|
return icns.Encode(dest, srcImg)
|
||||||
return err
|
|
||||||
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
cmd/packages/windows/icon.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
12
cmd/packages/windows/wails.exe.manifest
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<assemblyIdentity type="win32" name="MyApplication" version="1.0.0.0" processorArchitecture="amd64"/>
|
||||||
|
|
||||||
|
<asmv3:application>
|
||||||
|
<asmv3:windowsSettings>
|
||||||
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <!-- fallback for Windows 7 and 8 -->
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness> <!-- falls back to per-monitor if per-monitor v2 is not supported -->
|
||||||
|
<gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling> <!-- enables GDI DPI scaling -->
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</asmv3:application>
|
||||||
|
</assembly>
|
||||||
BIN
cmd/packages/windows/wails.ico
Normal file
|
After Width: | Height: | Size: 315 KiB |
2
cmd/packages/windows/wails.rc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
100 ICON "$NAME$.ico"
|
||||||
|
100 24 "$NAME$.exe.manifest"
|
||||||
@@ -97,6 +97,12 @@ func getRequiredLibrariesLinux() (*Prerequisites, error) {
|
|||||||
case Ubuntu:
|
case Ubuntu:
|
||||||
result.Add(newPrerequisite("libgtk-3-dev", "Please install with `sudo apt install libgtk-3-dev` and try again"))
|
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"))
|
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:
|
default:
|
||||||
result.Add(newPrerequisite("libgtk-3-dev", "Please install with your system package manager and try again"))
|
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"))
|
result.Add(newPrerequisite("libwebkit2gtk-4.0-dev", "Please install with your system package manager and try again"))
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ func (p *Program) GetFullPathToBinary() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run will execute the program with the given parameters
|
// Run will execute the program with the given parameters
|
||||||
// Returns stdout + stderr as strings and an error if one occured
|
// Returns stdout + stderr as strings and an error if one occurred
|
||||||
func (p *Program) Run(vars ...string) (stdout, stderr string, exitCode int, err error) {
|
func (p *Program) Run(vars ...string) (stdout, stderr string, exitCode int, err error) {
|
||||||
command, err := p.GetFullPathToBinary()
|
command, err := p.GetFullPathToBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -92,7 +92,7 @@ func (p *Program) Run(vars ...string) (stdout, stderr string, exitCode int, err
|
|||||||
|
|
||||||
// InstallGoPackage installs the given Go package
|
// InstallGoPackage installs the given Go package
|
||||||
func (p *ProgramHelper) InstallGoPackage(packageName string) error {
|
func (p *ProgramHelper) InstallGoPackage(packageName string) error {
|
||||||
args := strings.Split("get -u "+packageName, " ")
|
args := strings.Split("get "+packageName, " ")
|
||||||
_, stderr, err := p.shell.Run("go", args...)
|
_, stderr, err := p.shell.Run("go", args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(stderr)
|
fmt.Println(stderr)
|
||||||
@@ -107,7 +107,7 @@ func (p *ProgramHelper) RunCommand(command string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RunCommandArray runs the command specified in the array
|
// RunCommandArray runs the command specified in the array
|
||||||
func (p *ProgramHelper) RunCommandArray(args []string) error {
|
func (p *ProgramHelper) RunCommandArray(args []string, dir ...string) error {
|
||||||
program := args[0]
|
program := args[0]
|
||||||
// TODO: Run FindProgram here and get the full path to the exe
|
// TODO: Run FindProgram here and get the full path to the exe
|
||||||
program, err := exec.LookPath(program)
|
program, err := exec.LookPath(program)
|
||||||
@@ -116,7 +116,13 @@ func (p *ProgramHelper) RunCommandArray(args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
_, stderr, err := p.shell.Run(program, args...)
|
var stderr string
|
||||||
|
// fmt.Printf("RunCommandArray = %s %+v\n", program, args)
|
||||||
|
if len(dir) > 0 {
|
||||||
|
_, stderr, err = p.shell.RunInDirectory(dir[0], program, args...)
|
||||||
|
} else {
|
||||||
|
_, stderr, err = p.shell.Run(program, args...)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(stderr)
|
fmt.Println(stderr)
|
||||||
}
|
}
|
||||||
|
|||||||
349
cmd/project.go
@@ -4,10 +4,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey"
|
"github.com/leaanthony/slicer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type author struct {
|
type author struct {
|
||||||
@@ -19,6 +21,8 @@ type frontend struct {
|
|||||||
Dir string `json:"dir"`
|
Dir string `json:"dir"`
|
||||||
Install string `json:"install"`
|
Install string `json:"install"`
|
||||||
Build string `json:"build"`
|
Build string `json:"build"`
|
||||||
|
Bridge string `json:"bridge"`
|
||||||
|
Serve string `json:"serve"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type framework struct {
|
type framework struct {
|
||||||
@@ -46,22 +50,14 @@ func NewProjectHelper() *ProjectHelper {
|
|||||||
// GenerateProject generates a new project using the options given
|
// GenerateProject generates a new project using the options given
|
||||||
func (ph *ProjectHelper) GenerateProject(projectOptions *ProjectOptions) error {
|
func (ph *ProjectHelper) GenerateProject(projectOptions *ProjectOptions) error {
|
||||||
|
|
||||||
fs := NewFSHelper()
|
|
||||||
exists, err := ph.templates.TemplateExists(projectOptions.Template)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
return fmt.Errorf("template '%s' is invalid", projectOptions.Template)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate project path
|
// Calculate project path
|
||||||
projectPath, err := filepath.Abs(projectOptions.OutputDirectory)
|
projectPath, err := filepath.Abs(projectOptions.OutputDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = projectPath
|
||||||
|
|
||||||
if fs.DirExists(projectPath) {
|
if fs.DirExists(projectPath) {
|
||||||
return fmt.Errorf("directory '%s' already exists", projectPath)
|
return fmt.Errorf("directory '%s' already exists", projectPath)
|
||||||
}
|
}
|
||||||
@@ -82,11 +78,27 @@ func (ph *ProjectHelper) GenerateProject(projectOptions *ProjectOptions) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // If we are on windows, dump a windows_resource.json
|
||||||
|
// if runtime.GOOS == "windows" {
|
||||||
|
// ph.GenerateWindowsResourceConfig(projectOptions)
|
||||||
|
// }
|
||||||
|
|
||||||
ph.log.Yellow("Project '%s' generated in directory '%s'!", projectOptions.Name, projectOptions.OutputDirectory)
|
ph.log.Yellow("Project '%s' generated in directory '%s'!", projectOptions.Name, projectOptions.OutputDirectory)
|
||||||
ph.log.Yellow("To compile the project, run 'wails build' in the project directory.")
|
ph.log.Yellow("To compile the project, run 'wails build' in the project directory.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // GenerateWindowsResourceConfig generates the default windows resource file
|
||||||
|
// func (ph *ProjectHelper) GenerateWindowsResourceConfig(po *ProjectOptions) {
|
||||||
|
|
||||||
|
// fmt.Println(buffer.String())
|
||||||
|
|
||||||
|
// // vi.Build()
|
||||||
|
// // vi.Walk()
|
||||||
|
// // err := vi.WriteSyso(outPath, runtime.GOARCH)
|
||||||
|
// }
|
||||||
|
|
||||||
// LoadProjectConfig loads the project config from the given directory
|
// LoadProjectConfig loads the project config from the given directory
|
||||||
func (ph *ProjectHelper) LoadProjectConfig(dir string) (*ProjectOptions, error) {
|
func (ph *ProjectHelper) LoadProjectConfig(dir string) (*ProjectOptions, error) {
|
||||||
po := ph.NewProjectOptions()
|
po := ph.NewProjectOptions()
|
||||||
@@ -97,15 +109,14 @@ func (ph *ProjectHelper) LoadProjectConfig(dir string) (*ProjectOptions, error)
|
|||||||
// NewProjectOptions creates a new default set of project options
|
// NewProjectOptions creates a new default set of project options
|
||||||
func (ph *ProjectHelper) NewProjectOptions() *ProjectOptions {
|
func (ph *ProjectHelper) NewProjectOptions() *ProjectOptions {
|
||||||
result := ProjectOptions{
|
result := ProjectOptions{
|
||||||
Name: "",
|
Name: "",
|
||||||
Description: "Enter your project description",
|
Description: "Enter your project description",
|
||||||
Version: "0.1.0",
|
Version: "0.1.0",
|
||||||
BinaryName: "",
|
BinaryName: "",
|
||||||
system: NewSystemHelper(),
|
system: NewSystemHelper(),
|
||||||
log: NewLogger(),
|
log: NewLogger(),
|
||||||
templates: NewTemplateHelper(),
|
templates: NewTemplateHelper(),
|
||||||
templateNameMap: make(map[string]string),
|
Author: &author{},
|
||||||
Author: &author{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate system config
|
// Populate system config
|
||||||
@@ -118,193 +129,94 @@ func (ph *ProjectHelper) NewProjectOptions() *ProjectOptions {
|
|||||||
return &result
|
return &result
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectQuestion creates a new select type question for Survey
|
|
||||||
func SelectQuestion(name, message string, options []string, defaultValue string, required bool) *survey.Question {
|
|
||||||
result := survey.Question{
|
|
||||||
Name: name,
|
|
||||||
Prompt: &survey.Select{
|
|
||||||
Message: message,
|
|
||||||
Options: options,
|
|
||||||
Default: defaultValue,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if required {
|
|
||||||
result.Validate = survey.Required
|
|
||||||
}
|
|
||||||
return &result
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputQuestion creates a new input type question for Survey
|
|
||||||
func InputQuestion(name, message string, defaultValue string, required bool) *survey.Question {
|
|
||||||
result := survey.Question{
|
|
||||||
Name: name,
|
|
||||||
Prompt: &survey.Input{
|
|
||||||
Message: message + ":",
|
|
||||||
Default: defaultValue,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if required {
|
|
||||||
result.Validate = survey.Required
|
|
||||||
}
|
|
||||||
return &result
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectOptions holds all the options available for a project
|
// ProjectOptions holds all the options available for a project
|
||||||
type ProjectOptions struct {
|
type ProjectOptions struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Author *author `json:"author,omitempty"`
|
Author *author `json:"author,omitempty"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
OutputDirectory string `json:"-"`
|
OutputDirectory string `json:"-"`
|
||||||
UseDefaults bool `json:"-"`
|
UseDefaults bool `json:"-"`
|
||||||
Template string `json:"-"`
|
Template string `json:"-"`
|
||||||
BinaryName string `json:"binaryname"`
|
BinaryName string `json:"binaryname"`
|
||||||
FrontEnd *frontend `json:"frontend,omitempty"`
|
FrontEnd *frontend `json:"frontend,omitempty"`
|
||||||
NPMProjectName string `json:"-"`
|
NPMProjectName string `json:"-"`
|
||||||
Framework *framework `json:"framework,omitempty"`
|
system *SystemHelper
|
||||||
system *SystemHelper
|
log *Logger
|
||||||
log *Logger
|
templates *TemplateHelper
|
||||||
templates *TemplateHelper
|
selectedTemplate *TemplateDetails
|
||||||
templateNameMap map[string]string // Converts template prompt text to template name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defaults sets the default project template
|
// Defaults sets the default project template
|
||||||
func (po *ProjectOptions) Defaults() {
|
func (po *ProjectOptions) Defaults() {
|
||||||
po.Template = "basic"
|
po.Template = "vuebasic"
|
||||||
}
|
}
|
||||||
|
|
||||||
// PromptForInputs asks the user to input project details
|
// PromptForInputs asks the user to input project details
|
||||||
func (po *ProjectOptions) PromptForInputs() error {
|
func (po *ProjectOptions) PromptForInputs() error {
|
||||||
|
|
||||||
var questions []*survey.Question
|
processProjectName(po)
|
||||||
fs := NewFSHelper()
|
|
||||||
|
|
||||||
if po.Name == "" {
|
processBinaryName(po)
|
||||||
questions = append(questions, InputQuestion("Name", "The name of the project", "My Project", true))
|
|
||||||
} else {
|
err := processOutputDirectory(po)
|
||||||
fmt.Println("Project Name: " + po.Name)
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
|
||||||
if po.BinaryName == "" {
|
|
||||||
var binaryNameComputed string
|
|
||||||
if po.Name != "" {
|
|
||||||
binaryNameComputed = strings.ToLower(po.Name)
|
|
||||||
binaryNameComputed = strings.Replace(binaryNameComputed, " ", "-", -1)
|
|
||||||
binaryNameComputed = strings.Replace(binaryNameComputed, string(filepath.Separator), "-", -1)
|
|
||||||
binaryNameComputed = strings.Replace(binaryNameComputed, ":", "-", -1)
|
|
||||||
}
|
|
||||||
questions = append(questions, InputQuestion("BinaryName", "The output binary name", binaryNameComputed, true))
|
|
||||||
} else {
|
|
||||||
fmt.Println("Output binary Name: " + po.BinaryName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if po.OutputDirectory != "" {
|
|
||||||
projectPath, err := filepath.Abs(po.OutputDirectory)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if fs.DirExists(projectPath) {
|
|
||||||
return fmt.Errorf("directory '%s' already exists", projectPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Project Directory: " + po.OutputDirectory)
|
|
||||||
} else {
|
|
||||||
questions = append(questions, InputQuestion("OutputDirectory", "Project directory name", "", true))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process Templates
|
||||||
|
templateList := slicer.Interface()
|
||||||
|
options := slicer.String()
|
||||||
templateDetails, err := po.templates.GetTemplateDetails()
|
templateDetails, err := po.templates.GetTemplateDetails()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
templates := []string{}
|
|
||||||
// Add a Custom Template
|
|
||||||
// templates = append(templates, "Custom - Choose your own CSS framework")
|
|
||||||
for templateName, templateDetails := range templateDetails {
|
|
||||||
templateText := templateName
|
|
||||||
// Check if metadata json exists
|
|
||||||
if templateDetails.Metadata != nil {
|
|
||||||
shortdescription := templateDetails.Metadata["shortdescription"]
|
|
||||||
if shortdescription != "" {
|
|
||||||
templateText += " - " + shortdescription.(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templates = append(templates, templateText)
|
|
||||||
po.templateNameMap[templateText] = templateName
|
|
||||||
}
|
|
||||||
|
|
||||||
if po.Template != "" {
|
if po.Template != "" {
|
||||||
if _, ok := templateDetails[po.Template]; !ok {
|
// Check template is valid if given
|
||||||
po.log.Error("Template '%s' invalid.", po.Template)
|
if templateDetails[po.Template] == nil {
|
||||||
questions = append(questions, SelectQuestion("Template", "Select template", templates, templates[0], true))
|
keys := make([]string, 0, len(templateDetails))
|
||||||
|
for k := range templateDetails {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("invalid template name '%s'. Valid options: %s", po.Template, strings.Join(keys, ", "))
|
||||||
}
|
}
|
||||||
|
po.selectedTemplate = templateDetails[po.Template]
|
||||||
} else {
|
} else {
|
||||||
questions = append(questions, SelectQuestion("Template", "Select template", templates, templates[0], true))
|
|
||||||
|
for _, templateDetail := range templateDetails {
|
||||||
|
templateList.Add(templateDetail)
|
||||||
|
options.Add(fmt.Sprintf("%s - %s", templateDetail.Metadata.Name, templateDetail.Metadata.ShortDescription))
|
||||||
|
}
|
||||||
|
|
||||||
|
templateIndex := 0
|
||||||
|
|
||||||
|
if len(options.AsSlice()) > 1 {
|
||||||
|
templateIndex = PromptSelection("Please select a template", options.AsSlice(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(templateList.AsSlice()) == 0 {
|
||||||
|
return fmt.Errorf("aborting: no templates found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// After selection do this....
|
||||||
|
po.selectedTemplate = templateList.AsSlice()[templateIndex].(*TemplateDetails)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = survey.Ask(questions, po)
|
fmt.Println("Template: " + po.selectedTemplate.Metadata.Name)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup NPM Project name
|
// Setup NPM Project name
|
||||||
po.NPMProjectName = strings.ToLower(strings.Replace(po.Name, " ", "_", -1))
|
po.NPMProjectName = strings.ToLower(strings.Replace(po.Name, " ", "_", -1))
|
||||||
|
|
||||||
// If we selected custom, prompt for framework
|
|
||||||
if po.Template == "custom - Choose your own CSS Framework" {
|
|
||||||
// Ask for the framework
|
|
||||||
var frameworkName string
|
|
||||||
frameworks, err := GetFrameworks()
|
|
||||||
frameworkNames := []string{}
|
|
||||||
metadataMap := make(map[string]*FrameworkMetadata)
|
|
||||||
for _, frameworkMetadata := range frameworks {
|
|
||||||
frameworkDetails := fmt.Sprintf("%s - %s", frameworkMetadata.Name, frameworkMetadata.Description)
|
|
||||||
metadataMap[frameworkDetails] = frameworkMetadata
|
|
||||||
frameworkNames = append(frameworkNames, frameworkDetails)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var frameworkQuestion []*survey.Question
|
|
||||||
frameworkQuestion = append(frameworkQuestion, SelectQuestion("Framework", "Select framework", frameworkNames, frameworkNames[0], true))
|
|
||||||
err = survey.Ask(frameworkQuestion, &frameworkName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Get metadata
|
|
||||||
metadata := metadataMap[frameworkName]
|
|
||||||
|
|
||||||
// Add to project config
|
|
||||||
po.Framework = &framework{
|
|
||||||
Name: metadata.Name,
|
|
||||||
BuildTag: metadata.BuildTag,
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix template name
|
// Fix template name
|
||||||
if po.templateNameMap[po.Template] != "" {
|
po.Template = strings.Split(po.selectedTemplate.Path, string(os.PathSeparator))[0]
|
||||||
po.Template = po.templateNameMap[po.Template]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate template details
|
// // Populate template details
|
||||||
templateMetadata := templateDetails[po.Template].Metadata
|
templateMetadata := po.selectedTemplate.Metadata
|
||||||
if templateMetadata["frontenddir"] != nil {
|
|
||||||
po.FrontEnd = &frontend{}
|
err = processTemplateMetadata(templateMetadata, po)
|
||||||
po.FrontEnd.Dir = templateMetadata["frontenddir"].(string)
|
if err != nil {
|
||||||
}
|
return err
|
||||||
if templateMetadata["install"] != nil {
|
|
||||||
if po.FrontEnd == nil {
|
|
||||||
return fmt.Errorf("install set in template metadata but not frontenddir")
|
|
||||||
}
|
|
||||||
po.FrontEnd.Install = templateMetadata["install"].(string)
|
|
||||||
}
|
|
||||||
if templateMetadata["build"] != nil {
|
|
||||||
if po.FrontEnd == nil {
|
|
||||||
return fmt.Errorf("build set in template metadata but not frontenddir")
|
|
||||||
}
|
|
||||||
po.FrontEnd.Build = templateMetadata["build"].(string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -337,3 +249,86 @@ func (po *ProjectOptions) LoadConfig(projectDir string) error {
|
|||||||
}
|
}
|
||||||
return json.Unmarshal(rawBytes, po)
|
return json.Unmarshal(rawBytes, po)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func computeBinaryName(projectName string) string {
|
||||||
|
if projectName == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var binaryNameComputed = strings.ToLower(projectName)
|
||||||
|
binaryNameComputed = strings.Replace(binaryNameComputed, " ", "-", -1)
|
||||||
|
binaryNameComputed = strings.Replace(binaryNameComputed, string(filepath.Separator), "-", -1)
|
||||||
|
binaryNameComputed = strings.Replace(binaryNameComputed, ":", "-", -1)
|
||||||
|
return binaryNameComputed
|
||||||
|
}
|
||||||
|
|
||||||
|
func processOutputDirectory(po *ProjectOptions) error {
|
||||||
|
// po.OutputDirectory
|
||||||
|
if po.OutputDirectory == "" {
|
||||||
|
po.OutputDirectory = PromptRequired("Project directory name", computeBinaryName(po.Name))
|
||||||
|
}
|
||||||
|
projectPath, err := filepath.Abs(po.OutputDirectory)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if NewFSHelper().DirExists(projectPath) {
|
||||||
|
return fmt.Errorf("directory '%s' already exists", projectPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Project Directory: " + po.OutputDirectory)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processProjectName(po *ProjectOptions) {
|
||||||
|
if po.Name == "" {
|
||||||
|
po.Name = Prompt("The name of the project", "My Project")
|
||||||
|
}
|
||||||
|
fmt.Println("Project Name: " + po.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processBinaryName(po *ProjectOptions) {
|
||||||
|
if po.BinaryName == "" {
|
||||||
|
var binaryNameComputed = computeBinaryName(po.Name)
|
||||||
|
po.BinaryName = Prompt("The output binary name", binaryNameComputed)
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
if !strings.HasSuffix(po.BinaryName, ".exe") {
|
||||||
|
po.BinaryName += ".exe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("Output binary Name: " + po.BinaryName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processTemplateMetadata(templateMetadata *TemplateMetadata, po *ProjectOptions) error {
|
||||||
|
if templateMetadata.FrontendDir != "" {
|
||||||
|
po.FrontEnd = &frontend{}
|
||||||
|
po.FrontEnd.Dir = templateMetadata.FrontendDir
|
||||||
|
}
|
||||||
|
if templateMetadata.Install != "" {
|
||||||
|
if po.FrontEnd == nil {
|
||||||
|
return fmt.Errorf("install set in template metadata but not frontenddir")
|
||||||
|
}
|
||||||
|
po.FrontEnd.Install = templateMetadata.Install
|
||||||
|
}
|
||||||
|
if templateMetadata.Build != "" {
|
||||||
|
if po.FrontEnd == nil {
|
||||||
|
return fmt.Errorf("build set in template metadata but not frontenddir")
|
||||||
|
}
|
||||||
|
po.FrontEnd.Build = templateMetadata.Build
|
||||||
|
}
|
||||||
|
|
||||||
|
if templateMetadata.Bridge != "" {
|
||||||
|
if po.FrontEnd == nil {
|
||||||
|
return fmt.Errorf("bridge set in template metadata but not frontenddir")
|
||||||
|
}
|
||||||
|
po.FrontEnd.Bridge = templateMetadata.Bridge
|
||||||
|
}
|
||||||
|
|
||||||
|
if templateMetadata.Serve != "" {
|
||||||
|
if po.FrontEnd == nil {
|
||||||
|
return fmt.Errorf("serve set in template metadata but not frontenddir")
|
||||||
|
}
|
||||||
|
po.FrontEnd.Serve = templateMetadata.Serve
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
85
cmd/prompt.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Prompt asks the user for a value
|
||||||
|
func Prompt(question string, defaultValue ...string) string {
|
||||||
|
var answer string
|
||||||
|
|
||||||
|
if len(defaultValue) > 0 {
|
||||||
|
answer = defaultValue[0]
|
||||||
|
question = fmt.Sprintf("%s (%s)", question, answer)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
|
||||||
|
if input != "" {
|
||||||
|
answer = input
|
||||||
|
}
|
||||||
|
|
||||||
|
return answer
|
||||||
|
}
|
||||||
|
|
||||||
|
// PromptRequired calls Prompt repeatedly until a value is given
|
||||||
|
func PromptRequired(question string, defaultValue ...string) string {
|
||||||
|
for {
|
||||||
|
result := Prompt(question, defaultValue...)
|
||||||
|
if result != "" {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PromptSelection asks the user to choose an option
|
||||||
|
func PromptSelection(question string, options []string, optionalDefaultValue ...int) int {
|
||||||
|
|
||||||
|
defaultValue := -1
|
||||||
|
message := "Please choose an option"
|
||||||
|
fmt.Println(question + ":")
|
||||||
|
|
||||||
|
if len(optionalDefaultValue) > 0 {
|
||||||
|
defaultValue = optionalDefaultValue[0] + 1
|
||||||
|
message = fmt.Sprintf("%s [%d]", message, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, option := range options {
|
||||||
|
fmt.Printf(" %d: %s\n", index+1, option)
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedValue := -1
|
||||||
|
|
||||||
|
for {
|
||||||
|
choice := Prompt(message)
|
||||||
|
if choice == "" && defaultValue > -1 {
|
||||||
|
selectedValue = defaultValue - 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// index
|
||||||
|
number, err := strconv.Atoi(choice)
|
||||||
|
if err == nil {
|
||||||
|
if number > 0 && number <= len(options) {
|
||||||
|
selectedValue = number - 1
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedValue
|
||||||
|
}
|
||||||
98
cmd/semver.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/masterminds/semver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SemanticVersion is a struct containing a semantic version
|
||||||
|
type SemanticVersion struct {
|
||||||
|
Version *semver.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSemanticVersion creates a new SemanticVersion object with the given version string
|
||||||
|
func NewSemanticVersion(version string) (*SemanticVersion, error) {
|
||||||
|
semverVersion, err := semver.NewVersion(version)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &SemanticVersion{
|
||||||
|
Version: semverVersion,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRelease returns true if it's a release version
|
||||||
|
func (s *SemanticVersion) IsRelease() bool {
|
||||||
|
return len(s.Version.Prerelease()) == 0 && len(s.Version.Metadata()) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPreRelease returns true if it's a prerelease version
|
||||||
|
func (s *SemanticVersion) IsPreRelease() bool {
|
||||||
|
return len(s.Version.Prerelease()) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SemanticVersion) String() string {
|
||||||
|
return s.Version.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsGreaterThan returns true if this version is greater than the given version
|
||||||
|
func (s *SemanticVersion) IsGreaterThan(version *SemanticVersion) (bool, error) {
|
||||||
|
// Set up new constraint
|
||||||
|
constraint, err := semver.NewConstraint("> " + version.Version.String())
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the desired one is greater than the requested on
|
||||||
|
success, msgs := constraint.Validate(s.Version)
|
||||||
|
if !success {
|
||||||
|
return false, msgs[0]
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsGreaterThanOrEqual returns true if this version is greater than or equal the given version
|
||||||
|
func (s *SemanticVersion) IsGreaterThanOrEqual(version *SemanticVersion) (bool, error) {
|
||||||
|
// Set up new constraint
|
||||||
|
constraint, err := semver.NewConstraint(">= " + version.Version.String())
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the desired one is greater than the requested on
|
||||||
|
success, msgs := constraint.Validate(s.Version)
|
||||||
|
if !success {
|
||||||
|
return false, msgs[0]
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MainVersion returns the main version of any version+prerelease+metadata
|
||||||
|
// EG: MainVersion("1.2.3-pre") => "1.2.3"
|
||||||
|
func (s *SemanticVersion) MainVersion() *SemanticVersion {
|
||||||
|
mainVersion := fmt.Sprintf("%d.%d.%d", s.Version.Major(), s.Version.Minor(), s.Version.Patch())
|
||||||
|
result, _ := NewSemanticVersion(mainVersion)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// SemverCollection is a collection of SemanticVersion objects
|
||||||
|
type SemverCollection []*SemanticVersion
|
||||||
|
|
||||||
|
// Len returns the length of a collection. The number of Version instances
|
||||||
|
// on the slice.
|
||||||
|
func (c SemverCollection) Len() int {
|
||||||
|
return len(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less is needed for the sort interface to compare two Version objects on the
|
||||||
|
// slice. If checks if one is less than the other.
|
||||||
|
func (c SemverCollection) Less(i, j int) bool {
|
||||||
|
return c[i].Version.LessThan(c[j].Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap is needed for the sort interface to replace the Version objects
|
||||||
|
// at two different positions in the slice.
|
||||||
|
func (c SemverCollection) Swap(i, j int) {
|
||||||
|
c[i], c[j] = c[j], c[i]
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
package cmd
|
|
||||||
16
cmd/shell.go
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,6 +18,21 @@ func NewShellHelper() *ShellHelper {
|
|||||||
// Run the given command
|
// Run the given command
|
||||||
func (sh *ShellHelper) Run(command string, vars ...string) (stdout, stderr string, err error) {
|
func (sh *ShellHelper) Run(command string, vars ...string) (stdout, stderr string, err error) {
|
||||||
cmd := exec.Command(command, vars...)
|
cmd := exec.Command(command, vars...)
|
||||||
|
cmd.Env = append(os.Environ(), "GO111MODULE=on")
|
||||||
|
var stdo, stde bytes.Buffer
|
||||||
|
cmd.Stdout = &stdo
|
||||||
|
cmd.Stderr = &stde
|
||||||
|
err = cmd.Run()
|
||||||
|
stdout = string(stdo.Bytes())
|
||||||
|
stderr = string(stde.Bytes())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunInDirectory runs the given command in the given directory
|
||||||
|
func (sh *ShellHelper) RunInDirectory(dir string, command string, vars ...string) (stdout, stderr string, err error) {
|
||||||
|
cmd := exec.Command(command, vars...)
|
||||||
|
cmd.Dir = dir
|
||||||
|
cmd.Env = append(os.Environ(), "GO111MODULE=on")
|
||||||
var stdo, stde bytes.Buffer
|
var stdo, stde bytes.Buffer
|
||||||
cmd.Stdout = &stdo
|
cmd.Stdout = &stdo
|
||||||
cmd.Stderr = &stde
|
cmd.Stderr = &stde
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey"
|
|
||||||
homedir "github.com/mitchellh/go-homedir"
|
homedir "github.com/mitchellh/go-homedir"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -69,6 +68,17 @@ func (s *SystemHelper) ConfigFileIsValid() bool {
|
|||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAuthor returns a formatted string of the user's name and email
|
||||||
|
func (s *SystemHelper) GetAuthor() (string, error) {
|
||||||
|
var config *SystemConfig
|
||||||
|
config, err := s.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s <%s>", config.Name, config.Email), nil
|
||||||
|
}
|
||||||
|
|
||||||
// BackupConfig attempts to backup the system config file
|
// BackupConfig attempts to backup the system config file
|
||||||
func (s *SystemHelper) BackupConfig() (string, error) {
|
func (s *SystemHelper) BackupConfig() (string, error) {
|
||||||
now := strconv.FormatInt(time.Now().UTC().UnixNano(), 10)
|
now := strconv.FormatInt(time.Now().UTC().UnixNano(), 10)
|
||||||
@@ -82,55 +92,43 @@ func (s *SystemHelper) BackupConfig() (string, error) {
|
|||||||
|
|
||||||
func (s *SystemHelper) setup() error {
|
func (s *SystemHelper) setup() error {
|
||||||
|
|
||||||
// Answers. We all need them.
|
systemConfig := make(map[string]string)
|
||||||
answers := &SystemConfig{}
|
|
||||||
|
|
||||||
// Try to load current values - ignore errors
|
// Try to load current values - ignore errors
|
||||||
config, err := s.LoadConfig()
|
config, _ := s.LoadConfig()
|
||||||
defaultName := ""
|
|
||||||
defaultEmail := ""
|
|
||||||
if config != nil {
|
|
||||||
defaultName = config.Name
|
|
||||||
defaultEmail = config.Email
|
|
||||||
}
|
|
||||||
// Questions
|
|
||||||
var simpleQs = []*survey.Question{
|
|
||||||
{
|
|
||||||
Name: "Name",
|
|
||||||
Prompt: &survey.Input{
|
|
||||||
Message: "What is your name:",
|
|
||||||
Default: defaultName,
|
|
||||||
},
|
|
||||||
Validate: survey.Required,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Email",
|
|
||||||
Prompt: &survey.Input{
|
|
||||||
Message: "What is your email address:",
|
|
||||||
Default: defaultEmail,
|
|
||||||
},
|
|
||||||
Validate: survey.Required,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// ask the questions
|
if config.Name != "" {
|
||||||
err = survey.Ask(simpleQs, answers)
|
systemConfig["name"] = PromptRequired("What is your name", config.Name)
|
||||||
if err != nil {
|
} else {
|
||||||
return err
|
systemConfig["name"] = PromptRequired("What is your name")
|
||||||
|
}
|
||||||
|
if config.Email != "" {
|
||||||
|
systemConfig["email"] = PromptRequired("What is your email address", config.Email)
|
||||||
|
} else {
|
||||||
|
systemConfig["email"] = PromptRequired("What is your email address")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the directory
|
// Create the directory
|
||||||
err = s.fs.MkDirs(s.wailsSystemDir)
|
err := s.fs.MkDirs(s.wailsSystemDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save
|
||||||
|
configData, err := json.Marshal(&systemConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(s.wailsSystemConfig, configData, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
s.log.White("Wails config saved to: " + s.wailsSystemConfig)
|
s.log.White("Wails config saved to: " + s.wailsSystemConfig)
|
||||||
s.log.White("Feel free to customise these settings.")
|
s.log.White("Feel free to customise these settings.")
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
return answers.Save(s.wailsSystemConfig)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const introText = `
|
const introText = `
|
||||||
@@ -258,7 +256,7 @@ func CheckDependencies(logger *Logger) (bool, error) {
|
|||||||
bin := programHelper.FindProgram(program.Name)
|
bin := programHelper.FindProgram(program.Name)
|
||||||
if bin == nil {
|
if bin == nil {
|
||||||
errors = true
|
errors = true
|
||||||
logger.Red("Program '%s' not found. %s", program.Name, program.Help)
|
logger.Error("Program '%s' not found. %s", program.Name, program.Help)
|
||||||
} else {
|
} else {
|
||||||
logger.Green("Program '%s' found: %s", program.Name, bin.Path)
|
logger.Green("Program '%s' found: %s", program.Name, bin.Path)
|
||||||
}
|
}
|
||||||
@@ -281,7 +279,30 @@ func CheckDependencies(logger *Logger) (bool, error) {
|
|||||||
}
|
}
|
||||||
if !installed {
|
if !installed {
|
||||||
errors = true
|
errors = true
|
||||||
logger.Red("Library '%s' not found. %s", library.Name, library.Help)
|
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 {
|
} else {
|
||||||
logger.Green("Library '%s' installed.", library.Name)
|
logger.Green("Library '%s' installed.", library.Name)
|
||||||
}
|
}
|
||||||
|
|||||||
342
cmd/templates.go
@@ -5,91 +5,94 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"log"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/kennygrant/sanitize"
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
)
|
)
|
||||||
|
|
||||||
const templateSuffix = ".template"
|
// TemplateMetadata holds all the metadata for a Wails template
|
||||||
|
type TemplateMetadata struct {
|
||||||
// TemplateHelper helps with creating projects
|
Name string `json:"name"`
|
||||||
type TemplateHelper struct {
|
Version string `json:"version"`
|
||||||
system *SystemHelper
|
ShortDescription string `json:"shortdescription"`
|
||||||
fs *FSHelper
|
Description string `json:"description"`
|
||||||
templateDir string
|
Install string `json:"install"`
|
||||||
// templates map[string]string
|
Build string `json:"build"`
|
||||||
templateSuffix string
|
Author string `json:"author"`
|
||||||
metadataFilename string
|
Created string `json:"created"`
|
||||||
|
FrontendDir string `json:"frontenddir"`
|
||||||
|
Serve string `json:"serve"`
|
||||||
|
Bridge string `json:"bridge"`
|
||||||
|
WailsDir string `json:"wailsdir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template defines a single template
|
// TemplateDetails holds information about a specific template
|
||||||
type Template struct {
|
type TemplateDetails struct {
|
||||||
Name string
|
Name string
|
||||||
Dir string
|
Path string
|
||||||
Metadata map[string]interface{}
|
Metadata *TemplateMetadata
|
||||||
|
fs *FSHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
// TemplateHelper is a utility object to help with processing templates
|
||||||
|
type TemplateHelper struct {
|
||||||
|
templateDir *Dir
|
||||||
|
fs *FSHelper
|
||||||
|
metadataFilename string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTemplateHelper creates a new template helper
|
// NewTemplateHelper creates a new template helper
|
||||||
func NewTemplateHelper() *TemplateHelper {
|
func NewTemplateHelper() *TemplateHelper {
|
||||||
result := TemplateHelper{
|
|
||||||
system: NewSystemHelper(),
|
templateDir, err := fs.LocalDir("./templates")
|
||||||
fs: NewFSHelper(),
|
if err != nil {
|
||||||
templateSuffix: ".template",
|
log.Fatal("Unable to find the template directory. Please reinstall Wails.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TemplateHelper{
|
||||||
|
templateDir: templateDir,
|
||||||
metadataFilename: "template.json",
|
metadataFilename: "template.json",
|
||||||
}
|
}
|
||||||
// Calculate template base dir
|
|
||||||
_, filename, _, _ := runtime.Caller(1)
|
|
||||||
result.templateDir = filepath.Join(path.Dir(filename), "templates")
|
|
||||||
// result.templateDir = filepath.Join(result.system.homeDir, "go", "src", "github.com", "wailsapp", "wails", "cmd", "templates")
|
|
||||||
return &result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTemplateNames returns a map of all available templates
|
// IsValidTemplate returns true if the given tempalte name resides on disk
|
||||||
func (t *TemplateHelper) GetTemplateNames() (map[string]string, error) {
|
func (t *TemplateHelper) IsValidTemplate(templateName string) bool {
|
||||||
templateDirs, err := t.fs.GetSubdirs(t.templateDir)
|
pathToTemplate := filepath.Join(t.templateDir.fullPath, templateName)
|
||||||
if err != nil {
|
return t.fs.DirExists(pathToTemplate)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return templateDirs, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTemplateDetails returns a map of Template structs containing details
|
// SanitizeFilename sanitizes the given string to make a valid filename
|
||||||
// of the found templates
|
func (t *TemplateHelper) SanitizeFilename(name string) string {
|
||||||
func (t *TemplateHelper) GetTemplateDetails() (map[string]*Template, error) {
|
return sanitize.Name(name)
|
||||||
templateDirs, err := t.fs.GetSubdirs(t.templateDir)
|
}
|
||||||
|
|
||||||
|
// CreateNewTemplate creates a new template based on the given directory name and string
|
||||||
|
func (t *TemplateHelper) CreateNewTemplate(dirname string, details *TemplateMetadata) (string, error) {
|
||||||
|
|
||||||
|
// Check if this template has already been created
|
||||||
|
if t.IsValidTemplate(dirname) {
|
||||||
|
return "", fmt.Errorf("cannot create template in directory '%s' - already exists", dirname)
|
||||||
|
}
|
||||||
|
|
||||||
|
targetDir := filepath.Join(t.templateDir.fullPath, dirname)
|
||||||
|
err := t.fs.MkDir(targetDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
targetMetadata := filepath.Join(targetDir, t.metadataFilename)
|
||||||
|
err = t.fs.SaveAsJSON(details, targetMetadata)
|
||||||
|
|
||||||
result := make(map[string]*Template)
|
return targetDir, err
|
||||||
|
|
||||||
for name, dir := range templateDirs {
|
|
||||||
result[name] = &Template{
|
|
||||||
Dir: dir,
|
|
||||||
}
|
|
||||||
metadata, err := t.LoadMetadata(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result[name].Metadata = metadata
|
|
||||||
if metadata["name"] != nil {
|
|
||||||
result[name].Name = metadata["name"].(string)
|
|
||||||
} else {
|
|
||||||
// Ignore bad templates?
|
|
||||||
result[name] = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadMetadata loads the template's 'metadata.json' file
|
// LoadMetadata loads the template's 'metadata.json' file
|
||||||
func (t *TemplateHelper) LoadMetadata(dir string) (map[string]interface{}, error) {
|
func (t *TemplateHelper) LoadMetadata(dir string) (*TemplateMetadata, error) {
|
||||||
templateFile := filepath.Join(dir, t.metadataFilename)
|
templateFile := filepath.Join(dir, t.metadataFilename)
|
||||||
result := make(map[string]interface{})
|
result := &TemplateMetadata{}
|
||||||
if !t.fs.FileExists(templateFile) {
|
if !t.fs.FileExists(templateFile) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -101,14 +104,48 @@ func (t *TemplateHelper) LoadMetadata(dir string) (map[string]interface{}, error
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateExists returns true if the given template name exists
|
// GetTemplateDetails returns a map of Template structs containing details
|
||||||
func (t *TemplateHelper) TemplateExists(templateName string) (bool, error) {
|
// of the found templates
|
||||||
templates, err := t.GetTemplateNames()
|
func (t *TemplateHelper) GetTemplateDetails() (map[string]*TemplateDetails, error) {
|
||||||
|
|
||||||
|
// Get the subdirectory details
|
||||||
|
templateDirs, err := t.templateDir.GetSubdirs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, exists := templates[templateName]
|
|
||||||
return exists, nil
|
result := make(map[string]*TemplateDetails)
|
||||||
|
|
||||||
|
for name, dir := range templateDirs {
|
||||||
|
result[name] = &TemplateDetails{
|
||||||
|
Path: dir,
|
||||||
|
}
|
||||||
|
_ = &TemplateMetadata{}
|
||||||
|
metadata, err := t.LoadMetadata(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result[name].Metadata = metadata
|
||||||
|
if metadata.Name != "" {
|
||||||
|
result[name].Name = metadata.Name
|
||||||
|
} else {
|
||||||
|
// Ignore bad templates?
|
||||||
|
result[name] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTemplateFilenames returns all the filenames of the given template
|
||||||
|
func (t *TemplateHelper) GetTemplateFilenames(template *TemplateDetails) (*slicer.StringSlicer, error) {
|
||||||
|
|
||||||
|
// Get the subdirectory details
|
||||||
|
templateDir, err := t.fs.Dir(template.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return templateDir.GetAllFilenames()
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstallTemplate installs the template given in the project options to the
|
// InstallTemplate installs the template given in the project options to the
|
||||||
@@ -116,161 +153,60 @@ func (t *TemplateHelper) TemplateExists(templateName string) (bool, error) {
|
|||||||
func (t *TemplateHelper) InstallTemplate(projectPath string, projectOptions *ProjectOptions) error {
|
func (t *TemplateHelper) InstallTemplate(projectPath string, projectOptions *ProjectOptions) error {
|
||||||
|
|
||||||
// Get template files
|
// Get template files
|
||||||
template, err := t.getTemplateFiles(projectOptions.Template)
|
templateFilenames, err := t.GetTemplateFilenames(projectOptions.selectedTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy files to target
|
templatePath := projectOptions.selectedTemplate.Path
|
||||||
err = template.Install(projectPath, projectOptions)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
templateJSONFilename := filepath.Join(templatePath, t.metadataFilename)
|
||||||
}
|
|
||||||
|
|
||||||
// templateFiles categorises files found in a template
|
templateFiles := templateFilenames.Filter(func(filename string) bool {
|
||||||
type templateFiles struct {
|
filename = filepath.FromSlash(filename)
|
||||||
BaseDir string
|
return strings.HasPrefix(filename, templatePath) && filename != templateJSONFilename
|
||||||
StandardFiles []string
|
|
||||||
Templates []string
|
|
||||||
Dirs []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// newTemplateFiles returns a new TemplateFiles struct
|
|
||||||
func (t *TemplateHelper) newTemplateFiles(dir string) *templateFiles {
|
|
||||||
pathsep := string(os.PathSeparator)
|
|
||||||
// Ensure base directory has trailing slash
|
|
||||||
if !strings.HasSuffix(dir, pathsep) {
|
|
||||||
dir = dir + pathsep
|
|
||||||
}
|
|
||||||
return &templateFiles{
|
|
||||||
BaseDir: dir,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddStandardFile adds the given file to the list of standard files
|
|
||||||
func (t *templateFiles) AddStandardFile(filename string) {
|
|
||||||
localPath := strings.TrimPrefix(filename, t.BaseDir)
|
|
||||||
t.StandardFiles = append(t.StandardFiles, localPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTemplate adds the given file to the list of template files
|
|
||||||
func (t *templateFiles) AddTemplate(filename string) {
|
|
||||||
localPath := strings.TrimPrefix(filename, t.BaseDir)
|
|
||||||
t.Templates = append(t.Templates, localPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddDir adds the given directory to the list of template dirs
|
|
||||||
func (t *templateFiles) AddDir(dir string) {
|
|
||||||
localPath := strings.TrimPrefix(dir, t.BaseDir)
|
|
||||||
t.Dirs = append(t.Dirs, localPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTemplateFiles returns a struct categorising files in
|
|
||||||
// the template directory
|
|
||||||
func (t *TemplateHelper) getTemplateFiles(templateName string) (*templateFiles, error) {
|
|
||||||
|
|
||||||
templates, err := t.GetTemplateNames()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
templateDir := templates[templateName]
|
|
||||||
result := t.newTemplateFiles(templateDir)
|
|
||||||
var localPath string
|
|
||||||
err = filepath.Walk(templateDir, func(dir string, info os.FileInfo, err error) error {
|
|
||||||
if dir == templateDir {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't copy template metadata
|
|
||||||
localPath = strings.TrimPrefix(dir, templateDir+string(filepath.Separator))
|
|
||||||
if localPath == t.metadataFilename {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Categorise the file
|
|
||||||
switch {
|
|
||||||
case info.IsDir():
|
|
||||||
result.AddDir(dir)
|
|
||||||
case strings.HasSuffix(info.Name(), templateSuffix):
|
|
||||||
result.AddTemplate(dir)
|
|
||||||
default:
|
|
||||||
result.AddStandardFile(dir)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
templateFiles.Each(func(templateFile string) {
|
||||||
return nil, fmt.Errorf("error processing template '%s' in path '%q': %v", templateName, templateDir, err)
|
|
||||||
}
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Install the template files into the given project path
|
// Setup filenames
|
||||||
func (t *templateFiles) Install(projectPath string, projectOptions *ProjectOptions) error {
|
relativeFilename := strings.TrimPrefix(templateFile, templatePath)[1:]
|
||||||
|
targetFilename, err := filepath.Abs(filepath.Join(projectOptions.OutputDirectory, relativeFilename))
|
||||||
fs := NewFSHelper()
|
|
||||||
|
|
||||||
// Create directories
|
|
||||||
var targetDir string
|
|
||||||
for _, dirname := range t.Dirs {
|
|
||||||
targetDir = filepath.Join(projectPath, dirname)
|
|
||||||
fs.MkDir(targetDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy standard files
|
|
||||||
var targetFile, sourceFile string
|
|
||||||
var err error
|
|
||||||
for _, filename := range t.StandardFiles {
|
|
||||||
sourceFile = filepath.Join(t.BaseDir, filename)
|
|
||||||
targetFile = filepath.Join(projectPath, filename)
|
|
||||||
|
|
||||||
err = fs.CopyFile(sourceFile, targetFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return
|
||||||
|
}
|
||||||
|
filedata, err := t.fs.LoadAsBytes(templateFile)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Do we have template files?
|
// If file is a template, process it
|
||||||
if len(t.Templates) > 0 {
|
if strings.HasSuffix(templateFile, ".template") {
|
||||||
|
templateData := string(filedata)
|
||||||
// Iterate over the templates
|
tmpl := template.New(templateFile)
|
||||||
var templateFile string
|
tmpl.Parse(templateData)
|
||||||
var tmpl *template.Template
|
|
||||||
for _, filename := range t.Templates {
|
|
||||||
|
|
||||||
// Load template text
|
|
||||||
templateFile = filepath.Join(t.BaseDir, filename)
|
|
||||||
templateText, err := fs.LoadAsString(templateFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply template
|
|
||||||
tmpl = template.New(templateFile)
|
|
||||||
tmpl.Parse(templateText)
|
|
||||||
|
|
||||||
// Write the template to a buffer
|
|
||||||
var tpl bytes.Buffer
|
var tpl bytes.Buffer
|
||||||
err = tmpl.Execute(&tpl, projectOptions)
|
err = tmpl.Execute(&tpl, projectOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("ERROR!!! " + err.Error())
|
return
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save buffer to disk
|
// Remove template suffix
|
||||||
targetFilename := strings.TrimSuffix(filename, templateSuffix)
|
targetFilename = strings.TrimSuffix(targetFilename, ".template")
|
||||||
targetFile = filepath.Join(projectPath, targetFilename)
|
|
||||||
err = ioutil.WriteFile(targetFile, tpl.Bytes(), 0644)
|
// Set the filedata to the template result
|
||||||
if err != nil {
|
filedata = tpl.Bytes()
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normal file, just copy it
|
||||||
|
err = fs.CreateFile(targetFilename, filedata)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
<div style="text-align:center">
|
|
||||||
<div
|
|
||||||
class="wails-logo"
|
|
||||||
style="width: 25rem;margin: auto;height: 25rem;"
|
|
||||||
></div>
|
|
||||||
<h1>Basic Template</h1>
|
|
||||||
Welcome to your basic Wails app!
|
|
||||||
</div>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gobuffalo/packr"
|
|
||||||
wails "github.com/wailsapp/wails"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
// Assets
|
|
||||||
assets := packr.NewBox("./frontend")
|
|
||||||
|
|
||||||
// Initialise the app
|
|
||||||
app := wails.CreateApp(&wails.AppConfig{
|
|
||||||
Width: 1024,
|
|
||||||
Height: 768,
|
|
||||||
Title: "{{.Name}}",
|
|
||||||
HTML: wails.BoxString(&assets, "main.html"),
|
|
||||||
})
|
|
||||||
app.Run()
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Basic",
|
|
||||||
"shortdescription": "A basic template",
|
|
||||||
"description": "A basic template using vanilla JS",
|
|
||||||
"author": "Lea Anthony<lea.anthony@gmail.com>",
|
|
||||||
"created": "2018-10-18"
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<div style="text-align:center">
|
|
||||||
<div
|
|
||||||
class="wails-logo"
|
|
||||||
style="width: 25rem;margin: auto;height: 25rem;"
|
|
||||||
></div>
|
|
||||||
<h1>Custom CSS Template</h1>
|
|
||||||
Welcome to your basic Wails app with custom CSS!
|
|
||||||
</div>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
module {{.BinaryName}}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gobuffalo/packr"
|
|
||||||
wails "github.com/wailsapp/wails"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
// Assets
|
|
||||||
assets := packr.NewBox("./frontend")
|
|
||||||
|
|
||||||
// Initialise the app
|
|
||||||
app := wails.CreateApp(&wails.AppConfig{
|
|
||||||
Width: 1024,
|
|
||||||
Height: 768,
|
|
||||||
Title: "{{.Name}}",
|
|
||||||
HTML: wails.BoxString(&assets, "main.html"),
|
|
||||||
})
|
|
||||||
app.Run()
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Custom",
|
|
||||||
"shortdescription": "Choose your own CSS Framework",
|
|
||||||
"description": "A basic template allowing use of CSS Frameworks",
|
|
||||||
"author": "Lea Anthony<lea.anthony@gmail.com>",
|
|
||||||
"created": "2018-10-22"
|
|
||||||
}
|
|
||||||
@@ -1,29 +1,35 @@
|
|||||||
# frontend
|
# vue basic
|
||||||
|
|
||||||
## Project setup
|
## Project setup
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Compiles and hot-reloads for development
|
### Compiles and hot-reloads for development
|
||||||
|
|
||||||
```
|
```
|
||||||
npm run serve
|
npm run serve
|
||||||
```
|
```
|
||||||
|
|
||||||
### Compiles and minifies for production
|
### Compiles and minifies for production
|
||||||
|
|
||||||
```
|
```
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run your tests
|
### Run your tests
|
||||||
|
|
||||||
```
|
```
|
||||||
npm run test
|
npm run test
|
||||||
```
|
```
|
||||||
|
|
||||||
### Lints and fixes files
|
### Lints and fixes files
|
||||||
|
|
||||||
```
|
```
|
||||||
npm run lint
|
npm run lint
|
||||||
```
|
```
|
||||||
|
|
||||||
### Customize configuration
|
### Customize configuration
|
||||||
|
|
||||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||||
11109
cmd/templates/vuebasic/frontend/package-lock.json
generated
Normal file
49
cmd/templates/vuebasic/frontend/package.json.template
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "{{.NPMProjectName}}",
|
||||||
|
"author": "{{.Author.Name}}<{{.Author.Email}}>",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"core-js": "^2.6.4",
|
||||||
|
"vue": "^2.5.22"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/cli-plugin-babel": "^3.4.0",
|
||||||
|
"@vue/cli-plugin-eslint": "^3.4.0",
|
||||||
|
"@vue/cli-service": "^3.4.0",
|
||||||
|
"babel-eslint": "^10.0.1",
|
||||||
|
"eslint": "^5.8.0",
|
||||||
|
"eslint-plugin-vue": "^5.0.0",
|
||||||
|
"eventsource-polyfill": "^0.9.6",
|
||||||
|
"vue-template-compiler": "^2.5.21",
|
||||||
|
"webpack-hot-middleware": "^2.24.3"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/essential",
|
||||||
|
"eslint:recommended"
|
||||||
|
],
|
||||||
|
"rules": {},
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "babel-eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postcss": {
|
||||||
|
"plugins": {
|
||||||
|
"autoprefixer": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not ie <= 8"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,22 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<img alt="Wails logo" src="./assets/images/logo.png" class="logo zoomIn">
|
<img alt="Wails logo" src="./assets/images/logo.png" class="logo zoomIn">
|
||||||
<HelloWorld msg="Welcome to Your Wails App!"/>
|
<HelloWorld/>
|
||||||
<Quote/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import HelloWorld from "./components/HelloWorld.vue";
|
import HelloWorld from "./components/HelloWorld.vue";
|
||||||
import Quote from "./components/Quote.vue";
|
|
||||||
import "./assets/css/main.css";
|
import "./assets/css/main.css";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "app",
|
name: "app",
|
||||||
components: {
|
components: {
|
||||||
HelloWorld,
|
HelloWorld
|
||||||
Quote
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -10,21 +10,6 @@
|
|||||||
html {
|
html {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
/* https://leaverou.github.io/css3patterns/#carbon */
|
|
||||||
background: linear-gradient(27deg, #151515 5px, transparent 5px) 0 5px,
|
|
||||||
linear-gradient(207deg, #151515 5px, transparent 5px) 10px 0px,
|
|
||||||
linear-gradient(27deg, #222 5px, transparent 5px) 0px 10px,
|
|
||||||
linear-gradient(207deg, #222 5px, transparent 5px) 10px 5px,
|
|
||||||
linear-gradient(90deg, #1b1b1b 10px, transparent 10px),
|
|
||||||
linear-gradient(
|
|
||||||
#1d1d1d 25%,
|
|
||||||
#1a1a1a 25%,
|
|
||||||
#1a1a1a 50%,
|
|
||||||
transparent 50%,
|
|
||||||
transparent 75%,
|
|
||||||
#242424 75%,
|
|
||||||
#242424
|
|
||||||
);
|
|
||||||
background-color: #131313;
|
background-color: #131313;
|
||||||
background-size: 20px 20px;
|
background-size: 20px 20px;
|
||||||
}
|
}
|
||||||
@@ -46,10 +31,8 @@ html {
|
|||||||
format("woff2"),
|
format("woff2"),
|
||||||
/* Super Modern Browsers */
|
/* Super Modern Browsers */
|
||||||
url("../fonts/roboto/roboto-v18-latin-regular.woff") format("woff"),
|
url("../fonts/roboto/roboto-v18-latin-regular.woff") format("woff"),
|
||||||
/* Modern Browsers */
|
/* Modern Browsers */ url("../fonts/roboto/roboto-v18-latin-regular.ttf")
|
||||||
url("../fonts/roboto/roboto-v18-latin-regular.ttf")
|
|
||||||
format("truetype"),
|
format("truetype"),
|
||||||
/* Safari, Android, iOS */
|
/* Safari, Android, iOS */
|
||||||
url("../fonts/roboto/roboto-v18-latin-regular.svg#Roboto")
|
url("../fonts/roboto/roboto-v18-latin-regular.svg#Roboto") format("svg"); /* Legacy iOS */
|
||||||
format("svg"); /* Legacy iOS */
|
}
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 301 KiB After Width: | Height: | Size: 301 KiB |
@@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<h1>{{message}}</h1>
|
||||||
|
<a @click="getMessage">Press Me!</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
message: " "
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getMessage: function() {
|
||||||
|
var self = this;
|
||||||
|
window.backend.basic().then(result => {
|
||||||
|
self.message = result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped>
|
||||||
|
h1 {
|
||||||
|
margin-top: 2em;
|
||||||
|
position: relative;
|
||||||
|
min-height: 5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
font-size: 1.7em;
|
||||||
|
border-color: blue;
|
||||||
|
background-color: blue;
|
||||||
|
color: white;
|
||||||
|
border: 3px solid white;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 9px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 500ms;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
font-size: 1.7em;
|
||||||
|
border-color: white;
|
||||||
|
background-color: #121212;
|
||||||
|
color: white;
|
||||||
|
border: 3px solid white;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 9px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
13
cmd/templates/vuebasic/frontend/src/main.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import Vue from "vue";
|
||||||
|
import App from "./App.vue";
|
||||||
|
|
||||||
|
Vue.config.productionTip = false;
|
||||||
|
Vue.config.devtools = true;
|
||||||
|
|
||||||
|
import Bridge from "./wailsbridge";
|
||||||
|
|
||||||
|
Bridge.Start(() => {
|
||||||
|
new Vue({
|
||||||
|
render: h => h(App)
|
||||||
|
}).$mount("#app");
|
||||||
|
});
|
||||||
@@ -9,7 +9,9 @@
|
|||||||
export default {
|
export default {
|
||||||
// The main function
|
// The main function
|
||||||
// Passes the main Wails object to the callback if given.
|
// Passes the main Wails object to the callback if given.
|
||||||
Start: function (callback) {
|
Start: function(callback) {
|
||||||
return callback();
|
if (callback) {
|
||||||
|
window.wails.events.on("wails:ready", callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
43
cmd/templates/vuebasic/frontend/vue.config.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
let cssConfig = {};
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV == "production") {
|
||||||
|
cssConfig = {
|
||||||
|
extract: {
|
||||||
|
filename: "[name].css",
|
||||||
|
chunkFilename: "[name].css"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
chainWebpack: config => {
|
||||||
|
let limit = 9999999999999999;
|
||||||
|
config.module
|
||||||
|
.rule("images")
|
||||||
|
.test(/\.(png|gif|jpg)(\?.*)?$/i)
|
||||||
|
.use("url-loader")
|
||||||
|
.loader("url-loader")
|
||||||
|
.tap(options => Object.assign(options, { limit: limit }));
|
||||||
|
config.module
|
||||||
|
.rule("fonts")
|
||||||
|
.test(/\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/i)
|
||||||
|
.use("url-loader")
|
||||||
|
.loader("url-loader")
|
||||||
|
.options({
|
||||||
|
limit: limit
|
||||||
|
});
|
||||||
|
},
|
||||||
|
css: cssConfig,
|
||||||
|
configureWebpack: {
|
||||||
|
output: {
|
||||||
|
filename: "[name].js"
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
splitChunks: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
disableHostCheck: true,
|
||||||
|
host: "localhost"
|
||||||
|
}
|
||||||
|
};
|
||||||
27
cmd/templates/vuebasic/main.go.template
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/leaanthony/mewn"
|
||||||
|
"github.com/wailsapp/wails"
|
||||||
|
)
|
||||||
|
|
||||||
|
func basic() string {
|
||||||
|
return "Hello World!"
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
js := mewn.String("./frontend/dist/app.js")
|
||||||
|
css := mewn.String("./frontend/dist/app.css")
|
||||||
|
|
||||||
|
app := wails.CreateApp(&wails.AppConfig{
|
||||||
|
Width: 1024,
|
||||||
|
Height: 768,
|
||||||
|
Title: "{{.Name}}",
|
||||||
|
JS: js,
|
||||||
|
CSS: css,
|
||||||
|
Colour: "#131313",
|
||||||
|
})
|
||||||
|
app.Bind(basic)
|
||||||
|
app.Run()
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "Vue2/Webpack",
|
"name": "Vue2/Webpack Basic",
|
||||||
"shortdescription": "A basic Vue2/WebPack4 template",
|
"shortdescription": "A basic Vue2/WebPack4 template",
|
||||||
"description": "A basic template using Vue 2 and bundled using Webpack 4",
|
"description": "A basic template using Vue 2 and bundled using Webpack 4",
|
||||||
"author": "Lea Anthony<lea.anthony@gmail.com>",
|
"author": "Lea Anthony<lea.anthony@gmail.com>",
|
||||||
"created": "2018-12-01",
|
"created": "2018-12-01",
|
||||||
"frontenddir": "frontend",
|
"frontenddir": "frontend",
|
||||||
"install": "npm install",
|
"install": "npm install",
|
||||||
"build": "npm run build"
|
"build": "npm run build",
|
||||||
}
|
"serve": "npm run serve",
|
||||||
|
"bridge": "src"
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
> 1%
|
|
||||||
last 2 versions
|
|
||||||
not ie <= 8
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "{{.NPMProjectName}}",
|
|
||||||
"author": "{{.Author.Name}}<{{.Author.Email}}>",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"serve": "vue-cli-service serve",
|
|
||||||
"build": "vue-cli-service build"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"vue": "^2.5.17"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@vue/cli-plugin-babel": "^3.1.1",
|
|
||||||
"@vue/cli-service": "^3.1.4",
|
|
||||||
"vue-template-compiler": "^2.5.17"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
autoprefixer: {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,17 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
|
||||||
<title>frontend</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>
|
|
||||||
<strong>We're sorry but frontend doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
|
||||||
</noscript>
|
|
||||||
<div id="app"></div>
|
|
||||||
<!-- built files will be auto injected -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
/**
|
|
||||||
Credit: https://codepen.io/harmputman/pen/IpAnb
|
|
||||||
**/
|
|
||||||
|
|
||||||
body {
|
|
||||||
font: normal 300 1em/1.5em sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
background: #fff;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 480px;
|
|
||||||
min-width: 320px;
|
|
||||||
margin: 2em auto 0;
|
|
||||||
padding: 1.5em;
|
|
||||||
opacity: 0.8;
|
|
||||||
border-radius: 1em;
|
|
||||||
border-color: #117;
|
|
||||||
}
|
|
||||||
|
|
||||||
p { margin-bottom: 1.5em; }
|
|
||||||
p:last-child { margin-bottom: 0; }
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
display: block;
|
|
||||||
border-width: 2px 0;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: #eee;
|
|
||||||
padding: 1.5em 0 0.5em;
|
|
||||||
margin: 1.5em 0;
|
|
||||||
position: relative;
|
|
||||||
color: #117;
|
|
||||||
}
|
|
||||||
blockquote:before {
|
|
||||||
content: '\201C';
|
|
||||||
position: absolute;
|
|
||||||
top: 0em;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
background: #fff;
|
|
||||||
width: 3rem;
|
|
||||||
height: 2rem;
|
|
||||||
font: 6em/1.08em sans-serif;
|
|
||||||
color: #666;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
blockquote:after {
|
|
||||||
content: "\2013 \2003" attr(cite);
|
|
||||||
display: block;
|
|
||||||
text-align: right;
|
|
||||||
font-size: 0.875em;
|
|
||||||
color: #e70000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* https://fdossena.com/?p=html5cool/buttons/i.frag */
|
|
||||||
button {
|
|
||||||
display:inline-block;
|
|
||||||
padding:0.35em 1.2em;
|
|
||||||
border:0.1em solid #000;
|
|
||||||
margin:0 0.3em 0.3em 0;
|
|
||||||
border-radius:0.12em;
|
|
||||||
box-sizing: border-box;
|
|
||||||
text-decoration:none;
|
|
||||||
font-family:'Roboto',sans-serif;
|
|
||||||
font-weight:300;
|
|
||||||
font-size: 1em;
|
|
||||||
color:#000;
|
|
||||||
text-align:center;
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
button:hover{
|
|
||||||
color:#FFF;
|
|
||||||
background-color:#000;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="hello">
|
|
||||||
<h1>{{ msg }}</h1>
|
|
||||||
<p></p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "HelloWorld",
|
|
||||||
props: {
|
|
||||||
msg: String
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped>
|
|
||||||
.hello {
|
|
||||||
margin-top: 2em;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
margin: 40px 0 0;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: gold;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="container">
|
|
||||||
<blockquote v-if="quote != null" :cite="quote.person">{{ quote.text }}</blockquote>
|
|
||||||
<p></p>
|
|
||||||
<button @click="getNewQuote()">Get new Quote</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import "../assets/css/quote.css";
|
|
||||||
import { eventBus } from "../main";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
quote: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getNewQuote: function() {
|
|
||||||
var self = this;
|
|
||||||
backend.QuotesCollection.GetQuote().then(result => {
|
|
||||||
self.quote = result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
eventBus.$on("ready", this.getNewQuote);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped>
|
|
||||||
.hello {
|
|
||||||
margin-top: 2em;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
margin: 40px 0 0;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: gold;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import Vue from "vue";
|
|
||||||
export const eventBus = new Vue();
|
|
||||||
|
|
||||||
import App from "./App.vue";
|
|
||||||
new Vue({
|
|
||||||
render: h => h(App)
|
|
||||||
}).$mount("#app");
|
|
||||||
|
|
||||||
import Bridge from "./wailsbridge";
|
|
||||||
Bridge.Start(startApp);
|
|
||||||
|
|
||||||
function startApp() {
|
|
||||||
eventBus.$emit("ready");
|
|
||||||
}
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
/*
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
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) {
|
|
||||||
var elem = document.createElement("style");
|
|
||||||
elem.setAttribute("type", "text/css");
|
|
||||||
if (elem.styleSheet) {
|
|
||||||
elem.styleSheet.cssText = css;
|
|
||||||
} else {
|
|
||||||
elem.appendChild(document.createTextNode(css));
|
|
||||||
}
|
|
||||||
var head = document.head || document.getElementsByTagName("head")[0];
|
|
||||||
head.appendChild(elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a node in the Dom
|
|
||||||
function createNode(parent, elementType, id, className, content) {
|
|
||||||
var d = document.createElement(elementType);
|
|
||||||
if (id) {
|
|
||||||
d.id = id;
|
|
||||||
}
|
|
||||||
if (className) {
|
|
||||||
d.className = className;
|
|
||||||
}
|
|
||||||
if (content) {
|
|
||||||
d.innerHTML = content;
|
|
||||||
}
|
|
||||||
parent.appendChild(d);
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets up the overlay
|
|
||||||
function setupOverlay() {
|
|
||||||
var body = document.body;
|
|
||||||
var wailsBridgeNode = createNode(body, "div", "wails-bridge");
|
|
||||||
wailsBridgeNode.innerHTML = window.wailsbridge.overlayHTML;
|
|
||||||
|
|
||||||
// Inject the overlay CSS
|
|
||||||
injectCSS(window.wailsbridge.overlayCSS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the Wails Bridge
|
|
||||||
function startBridge() {
|
|
||||||
// Setup the overlay
|
|
||||||
setupOverlay();
|
|
||||||
|
|
||||||
window.wailsbridge.websocket = null;
|
|
||||||
window.wailsbridge.connectTimer = null;
|
|
||||||
window.wailsbridge.reconnectOverlay = document.querySelector(
|
|
||||||
".wails-reconnect-overlay"
|
|
||||||
);
|
|
||||||
window.wailsbridge.connectionState = "disconnected";
|
|
||||||
|
|
||||||
// Shows the overlay
|
|
||||||
function showReconnectOverlay() {
|
|
||||||
window.wailsbridge.reconnectOverlay.style.display = "block";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hides the overlay
|
|
||||||
function hideReconnectOverlay() {
|
|
||||||
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) {
|
|
||||||
var s = document.createElement("script");
|
|
||||||
s.textContent = script;
|
|
||||||
document.head.appendChild(s);
|
|
||||||
|
|
||||||
// Remove internal messages from the DOM
|
|
||||||
if (remove) {
|
|
||||||
s.parentNode.removeChild(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles incoming websocket connections
|
|
||||||
function handleConnect() {
|
|
||||||
window.wailsbridge.log("Connected to backend");
|
|
||||||
hideReconnectOverlay();
|
|
||||||
clearInterval(window.wailsbridge.connectTimer);
|
|
||||||
window.wailsbridge.websocket.onclose = handleDisconnect;
|
|
||||||
window.wailsbridge.websocket.onmessage = handleMessage;
|
|
||||||
window.wailsbridge.connectionState = "connected";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles websocket disconnects
|
|
||||||
function handleDisconnect() {
|
|
||||||
window.wailsbridge.log("Disconnected from backend");
|
|
||||||
window.wailsbridge.websocket = null;
|
|
||||||
window.wailsbridge.connectionState = "disconnected";
|
|
||||||
showReconnectOverlay();
|
|
||||||
connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to connect to the backend every 300ms (default value).
|
|
||||||
// Change this value in the main wailsbridge object.
|
|
||||||
function connect() {
|
|
||||||
window.wailsbridge.connectTimer = setInterval(function() {
|
|
||||||
if (window.wailsbridge.websocket == null) {
|
|
||||||
window.wailsbridge.websocket = new WebSocket(window.wailsbridge.wsURL);
|
|
||||||
window.wailsbridge.websocket.onopen = handleConnect;
|
|
||||||
window.wailsbridge.websocket.onerror = function(e) {
|
|
||||||
e.stopImmediatePropagation();
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
window.wailsbridge.websocket = null;
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}, window.wailsbridge.reconnectTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleMessage(message) {
|
|
||||||
// As a bridge we ignore js and css injections
|
|
||||||
|
|
||||||
switch (message.data[0]) {
|
|
||||||
// Wails library - inject!
|
|
||||||
case "w":
|
|
||||||
addScript(message.data.slice(1));
|
|
||||||
|
|
||||||
// Now wails runtime is loaded, wails for the ready event
|
|
||||||
// and callback to the main app
|
|
||||||
window.wails.events.on("wails:loaded", function() {
|
|
||||||
window.wailsbridge.log("Wails Ready");
|
|
||||||
if (window.wailsbridge.callback) {
|
|
||||||
window.wailsbridge.log("Notifying application");
|
|
||||||
window.wailsbridge.callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.wailsbridge.log("Loaded Wails Runtime");
|
|
||||||
break;
|
|
||||||
// Notifications
|
|
||||||
case "n":
|
|
||||||
addScript(message.data.slice(1), true);
|
|
||||||
break;
|
|
||||||
// Binding
|
|
||||||
case "b":
|
|
||||||
var binding = message.data.slice(1);
|
|
||||||
//window.wailsbridge.log("Binding: " + binding)
|
|
||||||
window.wails._.newBinding(binding);
|
|
||||||
break;
|
|
||||||
// Call back
|
|
||||||
case "c":
|
|
||||||
var callbackData = message.data.slice(1);
|
|
||||||
// window.wailsbridge.log("Callback = " + callbackData);
|
|
||||||
window.wails._.callback(callbackData);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start by showing the overlay...
|
|
||||||
showReconnectOverlay();
|
|
||||||
|
|
||||||
// ...and attempt to connect
|
|
||||||
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;
|
|
||||||
|
|
||||||
// Start Bridge
|
|
||||||
startBridge();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
|
|
||||||
module.exports = {
|
|
||||||
chainWebpack: (config) => {
|
|
||||||
let limit = 9999999999999999;
|
|
||||||
config.module
|
|
||||||
.rule('images')
|
|
||||||
.test(/\.(png|gif|jpg)(\?.*)?$/i)
|
|
||||||
.use('url-loader')
|
|
||||||
.loader('url-loader')
|
|
||||||
.tap(options => Object.assign(options, { limit: limit }));
|
|
||||||
config.module
|
|
||||||
.rule('fonts')
|
|
||||||
.test(/\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/i)
|
|
||||||
.use('url-loader')
|
|
||||||
.loader('url-loader')
|
|
||||||
.options({
|
|
||||||
limit: limit,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
css: {
|
|
||||||
extract: {
|
|
||||||
filename: '[name].css',
|
|
||||||
chunkFilename: '[name].css',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
configureWebpack: {
|
|
||||||
output: {
|
|
||||||
filename: '[name].js',
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
splitChunks: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
module {{.BinaryName}}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gobuffalo/packr"
|
|
||||||
"github.com/wailsapp/wails"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
assets := packr.NewBox("./frontend/dist")
|
|
||||||
|
|
||||||
app := wails.CreateApp(&wails.AppConfig{
|
|
||||||
Width: 1024,
|
|
||||||
Height: 768,
|
|
||||||
Title: "{{.Name}}",
|
|
||||||
JS: wails.BoxString(&assets, "app.js"),
|
|
||||||
CSS: wails.BoxString(&assets, "app.css"),
|
|
||||||
})
|
|
||||||
app.Bind(newQuotesCollection())
|
|
||||||
app.Run()
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Quote holds a single quote and the person who said it
|
|
||||||
type Quote struct {
|
|
||||||
Text string `json:"text"`
|
|
||||||
Person string `json:"person"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// QuotesCollection holds a collection of quotes!
|
|
||||||
type QuotesCollection struct {
|
|
||||||
quotes []*Quote
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddQuote creates a Quote object with the given inputs and
|
|
||||||
// adds it to the Quotes collection
|
|
||||||
func (Q *QuotesCollection) AddQuote(text, person string) {
|
|
||||||
Q.quotes = append(Q.quotes, &Quote{Text: text, Person: person})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetQuote returns a random Quote object from the Quotes collection
|
|
||||||
func (Q *QuotesCollection) GetQuote() *Quote {
|
|
||||||
return Q.quotes[rand.Intn(len(Q.quotes))]
|
|
||||||
}
|
|
||||||
|
|
||||||
// newQuotesCollection creates a new QuotesCollection
|
|
||||||
func newQuotesCollection() *QuotesCollection {
|
|
||||||
result := &QuotesCollection{}
|
|
||||||
rand.Seed(time.Now().Unix())
|
|
||||||
result.AddQuote("Age is an issue of mind over matter. If you don't mind, it doesn't matter", "Mark Twain")
|
|
||||||
result.AddQuote("Anyone who stops learning is old, whether at twenty or eighty. Anyone who keeps learning stays young. The greatest thing in life is to keep your mind young", "Henry Ford")
|
|
||||||
result.AddQuote("Wrinkles should merely indicate where smiles have been", "Mark Twain")
|
|
||||||
result.AddQuote("True terror is to wake up one morning and discover that your high school class is running the country", "Kurt Vonnegut")
|
|
||||||
result.AddQuote("A diplomat is a man who always remembers a woman's birthday but never remembers her age", "Robert Frost")
|
|
||||||
result.AddQuote("As I grow older, I pay less attention to what men say. I just watch what they do", "Andrew Carnegie")
|
|
||||||
result.AddQuote("How incessant and great are the ills with which a prolonged old age is replete", "C. S. Lewis")
|
|
||||||
result.AddQuote("Old age, believe me, is a good and pleasant thing. It is true you are gently shouldered off the stage, but then you are given such a comfortable front stall as spectator", "Confucius")
|
|
||||||
result.AddQuote("Old age has deformities enough of its own. It should never add to them the deformity of vice", "Eleanor Roosevelt")
|
|
||||||
result.AddQuote("Nobody grows old merely by living a number of years. We grow old by deserting our ideals. Years may wrinkle the skin, but to give up enthusiasm wrinkles the soul", "Samuel Ullman")
|
|
||||||
result.AddQuote("An archaeologist is the best husband a woman can have. The older she gets the more interested he is in her", "Agatha Christie")
|
|
||||||
result.AddQuote("All diseases run into one, old age", "Ralph Waldo Emerson")
|
|
||||||
result.AddQuote("Bashfulness is an ornament to youth, but a reproach to old age", "Aristotle")
|
|
||||||
result.AddQuote("Like everyone else who makes the mistake of getting older, I begin each day with coffee and obituaries", "Bill Cosby")
|
|
||||||
result.AddQuote("Age appears to be best in four things old wood best to burn, old wine to drink, old friends to trust, and old authors to read", "Francis Bacon")
|
|
||||||
result.AddQuote("None are so old as those who have outlived enthusiasm", "Henry David Thoreau")
|
|
||||||
result.AddQuote("Every man over forty is a scoundrel", "George Bernard Shaw")
|
|
||||||
result.AddQuote("Forty is the old age of youth fifty the youth of old age", "Victor Hugo")
|
|
||||||
result.AddQuote("You can't help getting older, but you don't have to get old", "George Burns")
|
|
||||||
result.AddQuote("Alas, after a certain age every man is responsible for his face", "Albert Camus")
|
|
||||||
result.AddQuote("Youth is when you're allowed to stay up late on New Year's Eve. Middle age is when you're forced to", "Bill Vaughan")
|
|
||||||
result.AddQuote("Old age is like everything else. To make a success of it, you've got to start young", "Theodore Roosevelt")
|
|
||||||
result.AddQuote("A comfortable old age is the reward of a well-spent youth. Instead of its bringing sad and melancholy prospects of decay, it would give us hopes of eternal youth in a better world", "Maurice Chevalier")
|
|
||||||
result.AddQuote("A man growing old becomes a child again", "Sophocles")
|
|
||||||
result.AddQuote("I will never be an old man. To me, old age is always 15 years older than I am", "Francis Bacon")
|
|
||||||
result.AddQuote("Age considers youth ventures", "Rabindranath Tagore")
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
// Version - Wails version
|
// Version - Wails version
|
||||||
// ...oO(There must be a better way)
|
const Version = "v0.14.4-pre"
|
||||||
const Version = "v0.5.0"
|
|
||||||
|
|||||||