5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-11 22:49:29 +08:00

Merge pull request #1 from wailsapp/develop

Develop
This commit is contained in:
admin_3.exe 2019-07-27 22:12:15 +03:00 committed by GitHub
commit 9370030ff3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
142 changed files with 10748 additions and 1375 deletions

View File

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

View File

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

1
.eslintignore Normal file
View File

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

30
.eslintrc Normal file
View File

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

2
.gitignore vendored
View File

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

6
.hound.yml Normal file
View File

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

View File

@ -1,3 +1,4 @@
{
"go.formatTool": "goimports"
"go.formatTool": "goimports",
"eslint.alwaysShowStatus": true
}

View File

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

View File

@ -12,3 +12,6 @@ Wails is what it is because of the time and effort given by these great people.
* [intelwalk](https://github.com/intelwalk)
* [Mark Stenglein](https://github.com/ocelotsloth)
* [admin_3.exe](https://github.com/bh90210)
* [iceleo-com](https://github.com/iceleo-com)
* [fallendusk](https://github.com/fallendusk)
* [Florian Didran](https://github.com/fdidron)

View File

@ -46,7 +46,7 @@ Make sure you have the xcode command line tools installed. This can be done by r
### Linux
#### Ubuntu 18.04, Debian 9
#### Ubuntu 18.04, Debian 9, Zorin 15
`sudo apt install pkg-config build-essential libgtk-3-dev libwebkit2gtk-4.0-dev`

52
app.go
View File

@ -2,6 +2,12 @@ package wails
import (
"github.com/wailsapp/wails/cmd"
"github.com/wailsapp/wails/lib/binding"
"github.com/wailsapp/wails/lib/event"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/ipc"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/renderer"
)
// -------------------------------- Compile time Flags ------------------------------
@ -13,15 +19,15 @@ var BuildMode = cmd.BuildModeProd
// App defines the main application struct
type App struct {
config *AppConfig // The Application configuration object
cli *cmd.Cli // In debug mode, we have a cli
renderer Renderer // The renderer is what we will render the app to
logLevel string // The log level of the app
ipc *ipcManager // Handles the IPC calls
log *CustomLogger // Logger
bindingManager *bindingManager // Handles binding of Go code to renderer
eventManager *eventManager // Handles all the events
runtime *Runtime // The runtime object for registered structs
config *AppConfig // The Application configuration object
cli *cmd.Cli // In debug mode, we have a cli
renderer interfaces.Renderer // The renderer is what we will render the app to
logLevel string // The log level of the app
ipc interfaces.IPCManager // Handles the IPC calls
log *logger.CustomLogger // Logger
bindingManager interfaces.BindingManager // Handles binding of Go code to renderer
eventManager interfaces.EventManager // Handles all the events
runtime interfaces.Runtime // The runtime object for registered structs
}
// CreateApp creates the application window with the given configuration
@ -34,14 +40,14 @@ func CreateApp(optionalConfig ...*AppConfig) *App {
result := &App{
logLevel: "info",
renderer: &webViewRenderer{},
ipc: newIPCManager(),
bindingManager: newBindingManager(),
eventManager: newEventManager(),
log: newCustomLogger("App"),
renderer: renderer.NewWebView(),
ipc: ipc.NewManager(),
bindingManager: binding.NewManager(),
eventManager: event.NewManager(),
log: logger.NewCustomLogger("App"),
}
appconfig, err := newAppConfig(userConfig)
appconfig, err := newConfig(userConfig)
if err != nil {
result.log.Fatalf("Cannot use custom HTML: %s", err.Error())
}
@ -75,14 +81,14 @@ func (a *App) Run() error {
func (a *App) start() error {
// Set the log level
setLogLevel(a.logLevel)
logger.SetLogLevel(a.logLevel)
// Log starup
a.log.Info("Starting")
// Check if we are to run in headless mode
// Check if we are to run in bridge mode
if BuildMode == cmd.BuildModeBridge {
a.renderer = &Headless{}
a.renderer = &renderer.Bridge{}
}
// Initialise the renderer
@ -92,16 +98,16 @@ func (a *App) start() error {
}
// Start event manager and give it our renderer
a.eventManager.start(a.renderer)
a.eventManager.Start(a.renderer)
// Start the IPC Manager and give it the event manager and binding manager
a.ipc.start(a.eventManager, a.bindingManager)
a.ipc.Start(a.eventManager, a.bindingManager)
// Create the runtime
a.runtime = newRuntime(a.eventManager, a.renderer)
a.runtime = NewRuntime(a.eventManager, a.renderer)
// Start binding manager and give it our renderer
err = a.bindingManager.start(a.renderer, a.runtime)
err = a.bindingManager.Start(a.renderer, a.runtime)
if err != nil {
return err
}
@ -113,5 +119,5 @@ func (a *App) start() error {
// Bind allows the user to bind the given object
// with the application
func (a *App) Bind(object interface{}) {
a.bindingManager.bind(object)
a.bindingManager.Bind(object)
}

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ import (
"runtime"
"time"
mewn "github.com/leaanthony/mewn"
"github.com/leaanthony/mewn"
"github.com/leaanthony/slicer"
"github.com/leaanthony/spinner"
)
@ -249,8 +249,8 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
ioutil.WriteFile(md5sumFile, []byte(packageJSONMD5), 0644)
}
// Install the bridge library
err = InstallBridge(caller, projectDir, projectOptions)
// Install the runtime
err = InstallRuntime(caller, projectDir, projectOptions)
if err != nil {
return err
}
@ -263,22 +263,29 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
return nil
}
// InstallBridge installs the relevant bridge javascript library
func InstallBridge(caller string, projectDir string, projectOptions *ProjectOptions) error {
bridgeFile := "wailsbridge.prod.js"
if caller == "serve" {
bridgeFile = "wailsbridge.js"
// InstallRuntime installs the correct runtime for the type of build
func InstallRuntime(caller string, projectDir string, projectOptions *ProjectOptions) error {
if caller == "build" {
return InstallProdRuntime(projectDir, projectOptions)
}
// Copy bridge to project
bridgeAssets := mewn.Group("../wailsruntimeassets/bridge/")
bridgeFileData := bridgeAssets.Bytes(bridgeFile)
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, projectOptions.FrontEnd.Bridge, "wailsbridge.js")
err := fs.CreateFile(bridgeFileTarget, bridgeFileData)
if err != nil {
return err
}
return nil
return InstallBridge(projectDir, projectOptions)
}
// InstallBridge installs the relevant bridge javascript library
func InstallBridge(projectDir string, projectOptions *ProjectOptions) error {
bridgeFileData := mewn.String("../runtime/assets/bridge.js")
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, "node_modules", "@wailsapp", "runtime", "main.js")
err := fs.CreateFile(bridgeFileTarget, []byte(bridgeFileData))
return err
}
// InstallProdRuntime installs the production runtime
func InstallProdRuntime(projectDir string, projectOptions *ProjectOptions) error {
prodInit := mewn.String("../runtime/js/runtime/init.js")
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, "node_modules", "@wailsapp", "runtime", "main.js")
err := fs.CreateFile(bridgeFileTarget, []byte(prodInit))
return err
}
// ServeProject attempts to serve up the current project so that it may be connected to

View File

@ -25,6 +25,10 @@ const (
RedHat
// Debian distribution
Debian
// Gentoo distribution
Gentoo
// Zorin distribution
Zorin
)
// DistroInfo contains all the information relating to a linux distribution
@ -66,6 +70,12 @@ func GetLinuxDistroInfo() *DistroInfo {
result.Distribution = Arch
case "Debian":
result.Distribution = Debian
case "Gentoo":
result.Distribution = Gentoo
case "Zorin":
result.Distribution = Zorin
case "Fedora":
result.Distribution = RedHat
}
case "Description":
result.Description = value
@ -113,6 +123,8 @@ func GetLinuxDistroInfo() *DistroInfo {
result.Distribution = Arch
case "Debian GNU/Linux":
result.Distribution = Debian
case "Gentoo/Linux":
result.Distribution = Gentoo
default:
result.Distribution = Unknown
result.DistributorID = osName
@ -121,6 +133,17 @@ func GetLinuxDistroInfo() *DistroInfo {
return result
}
// EqueryInstalled uses equery to see if a package is installed
func EqueryInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
equery := program.FindProgram("equery")
if equery == nil {
return false, fmt.Errorf("cannont check dependencies: equery not found")
}
_, _, exitCode, _ := equery.Run("l", packageName)
return exitCode == 0, nil
}
// DpkgInstalled uses dpkg to see if a package is installed
func DpkgInstalled(packageName string) (bool, error) {
program := NewProgramHelper()

View File

@ -49,7 +49,7 @@ func getRequiredProgramsLinux() *Prerequisites {
result := &Prerequisites{}
distroInfo := GetLinuxDistroInfo()
switch distroInfo.Distribution {
case Ubuntu, Debian:
case Ubuntu, Debian, Zorin:
result.Add(newPrerequisite("gcc", "Please install with `sudo apt install build-essentials` and try again"))
result.Add(newPrerequisite("pkg-config", "Please install with `sudo apt install pkg-config` and try again"))
result.Add(newPrerequisite("npm", "Please install with `sudo snap install node --channel=12/stable --classic` and try again"))
@ -93,9 +93,12 @@ func getRequiredLibrariesLinux() (*Prerequisites, error) {
result := &Prerequisites{}
distroInfo := GetLinuxDistroInfo()
switch distroInfo.Distribution {
case Ubuntu:
case Ubuntu, Debian, Zorin:
result.Add(newPrerequisite("libgtk-3-dev", "Please install with `sudo apt install libgtk-3-dev` and try again"))
result.Add(newPrerequisite("libwebkit2gtk-4.0-dev", "Please install with `sudo apt install libwebkit2gtk-4.0-dev` and try again"))
case Gentoo:
result.Add(newPrerequisite("gtk+:3", "Please install with `sudo emerge gtk+:3` and try again"))
result.Add(newPrerequisite("webkit-gtk", "Please install with `sudo emerge webkit-gtk` 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"))

View File

@ -117,14 +117,15 @@ func (p *ProgramHelper) RunCommandArray(args []string, dir ...string) error {
}
args = args[1:]
var stderr string
// fmt.Printf("RunCommandArray = %s %+v\n", program, args)
var stdout string
if len(dir) > 0 {
_, stderr, err = p.shell.RunInDirectory(dir[0], program, args...)
stdout, stderr, err = p.shell.RunInDirectory(dir[0], program, args...)
} else {
_, stderr, err = p.shell.Run(program, args...)
stdout, stderr, err = p.shell.Run(program, args...)
}
if err != nil {
fmt.Println(stderr)
fmt.Println(stdout)
}
return err
}

View File

@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"github.com/leaanthony/slicer"
@ -184,7 +185,13 @@ func (po *ProjectOptions) PromptForInputs() error {
po.selectedTemplate = templateDetails[po.Template]
} else {
for _, templateDetail := range templateDetails {
keys := make([]string, 0)
for k := range templateDetails {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
templateDetail := templateDetails[k]
templateList.Add(templateDetail)
options.Add(fmt.Sprintf("%s - %s", templateDetail.Metadata.Name, templateDetail.Metadata.ShortDescription))
}

View File

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

View File

@ -272,7 +272,7 @@ func CheckDependencies(logger *Logger) (bool, error) {
distroInfo := GetLinuxDistroInfo()
for _, library := range *requiredLibraries {
switch distroInfo.Distribution {
case Ubuntu, Debian:
case Ubuntu, Zorin, Debian:
installed, err := DpkgInstalled(library.Name)
if err != nil {
return false, err
@ -305,6 +305,17 @@ func CheckDependencies(logger *Logger) (bool, error) {
} else {
logger.Green("Library '%s' installed.", library.Name)
}
case Gentoo:
installed, err := EqueryInstalled(library.Name)
if err != nil {
return false, err
}
if !installed {
errors = true
logger.Error("Library '%s' not found. %s", library.Name, library.Help)
} else {
logger.Green("Library '%s' installed.", library.Name)
}
default:
return false, RequestSupportForDistribution(distroInfo, library.Name)
}

View File

@ -0,0 +1,13 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@ -0,0 +1,47 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events.json
speed-measure-plugin.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
.editorcinfig

View File

@ -0,0 +1,27 @@
# MyApp
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.0.3.
## Development server
Run `npx ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `npx ng generate component component-name` to generate a new component. You can also use `npx ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `npx ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `npx ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `npx ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `npx ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@ -0,0 +1,121 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"my-app": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "ngx-build-plus:browser",
"options": {
"outputPath": "dist/my-app",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
}
}
},
"serve": {
"builder": "ngx-build-plus:dev-server",
"options": {
"browserTarget": "my-app:build"
},
"configurations": {
"production": {
"browserTarget": "my-app:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "my-app:build"
}
},
"test": {
"builder": "ngx-build-plus:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "my-app:serve"
},
"configurations": {
"production": {
"devServerTarget": "my-app:serve:production"
}
}
}
}
}
},
"defaultProject": "my-app"
}

View File

@ -0,0 +1,12 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.

View File

@ -0,0 +1,32 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

@ -0,0 +1,23 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to my-app!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

View File

@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root h1')).getText() as Promise<string>;
}
}

View File

@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

View File

@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/my-app'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

View File

@ -0,0 +1,51 @@
{
"name": "my-app",
"version": "0.0.0",
"scripts": {
"ng": "npx ng",
"start": "npx ng serve --poll=2000",
"build": "npx ng build --single-bundle true --output-hashing none --prod --bundle-styles false",
"test": "npx ng test",
"lint": "npx ng lint",
"e2e": "npx ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^8.0.2",
"@angular/cdk": "^8.0.1",
"@angular/common": "~8.0.1",
"@angular/compiler": "~8.0.1",
"@angular/core": "~8.0.1",
"@angular/forms": "~8.0.1",
"@angular/material": "^8.0.1",
"@angular/platform-browser": "~8.0.1",
"@angular/platform-browser-dynamic": "~8.0.1",
"@angular/router": "~8.0.1",
"@wailsapp/runtime": "^1.0.0",
"ngx-build-plus": "^8.0.3",
"rxjs": "~6.4.0",
"tslib": "^1.9.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.800.0",
"@angular/cli": "~8.0.3",
"@angular/compiler-cli": "~8.0.1",
"@angular/language-service": "~8.0.1",
"@types/node": "~8.9.4",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "^5.0.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.4.3"
}
}

View File

@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [
RouterModule.forRoot(routes)
],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -0,0 +1,14 @@
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<img width="300" alt="Angular Logo"
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
<br />
<button (click)="onClickMe()">Hello</button>
<p>{{clickMessage}}</p>
</div>
<router-outlet></router-outlet>

View File

@ -0,0 +1,35 @@
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'my-app'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('my-app');
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to my-app!');
});
});

View File

@ -0,0 +1,19 @@
import { Component } from '@angular/core';
@Component({
selector: '[id="app"]',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-app';
clickMessage = '';
onClickMe() {
// @ts-ignore
window.backend.basic().then(result =>
this.clickMessage = result
);
}
}

View File

@ -0,0 +1,20 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_BASE_HREF } from '@angular/common';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [{provide: APP_BASE_HREF, useValue : '/' }],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -0,0 +1,3 @@
export const environment = {
production: true
};

View File

@ -0,0 +1,16 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>my-app</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<div id="app"></div>
</body>
</html>

View File

@ -0,0 +1,18 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import 'zone.js'
import Wails from '@wailsapp/runtime';
if (environment.production) {
enableProdMode();
}
Wails.Init(() => {
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
});

View File

@ -0,0 +1,63 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
//import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View File

@ -0,0 +1,24 @@
/* You can add global styles to this file, and also import other style files */
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #282c34;
}
p {
color: white
}
h1 {
color: white
}
button {
background-color: white;
color: black;
}

View File

@ -0,0 +1,20 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@ -9,9 +9,9 @@
export default {
// The main function
// Passes the main Wails object to the callback if given.
Start: function (callback) {
Start: function(callback) {
if (callback) {
window.wails.Events.On("wails:ready", callback);
window.wails.events.on("wails:ready", callback);
}
}
};

View File

@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"include": [
"src/**/*.ts"
],
"exclude": [
"src/test.ts",
"src/**/*.spec.ts"
]
}

View File

@ -0,0 +1,23 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2018",
"dom"
]
}
}

View File

@ -0,0 +1,18 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@ -0,0 +1,92 @@
{
"extends": "tslint:recommended",
"rules": {
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warn"
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"import-blacklist": [
true,
"rxjs/Rx"
],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-use-before-declare": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [
true,
"single"
],
"trailing-comma": false,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": [
"codelyzer"
]
}

View File

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

View File

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

View File

@ -0,0 +1,20 @@
{
"name": "Angular",
"version": "1.0.0",
"shortdescription": "Angular 8 template (Requires node 10.8+)",
"description": "Angular projects w/ @angular/cli - Note: in order to reach the cli use npx like this: npx ng",
"dependencies": [
{
"bin": "npx",
"help": "This template requires 'npx'. Please install with 'npm install -g npx'"
}
],
"install": "npm install",
"build": "npx ng build --single-bundle true --output-hashing none --prod --bundle-styles false",
"author": "bh90210 <ktc@pm.me>",
"created": "2019-06-15 18:23:48.666414555 +0300 EEST m=+223.934866008",
"frontenddir": "frontend",
"serve": "npx ng serve --poll=2000",
"bridge": "src",
"wailsdir": ""
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package cmd
// Version - Wails version
const Version = "v0.16.4-pre"
const Version = "v0.17.6-pre"

View File

@ -96,7 +96,7 @@ func checkLibraries() (errors bool, err error) {
distroInfo := cmd.GetLinuxDistroInfo()
for _, library := range *requiredLibraries {
switch distroInfo.Distribution {
case cmd.Ubuntu, cmd.Debian:
case cmd.Ubuntu, cmd.Zorin, cmd.Debian:
installed, err := cmd.DpkgInstalled(library.Name)
if err != nil {
return false, err

View File

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

View File

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

110
config.go Normal file
View File

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

5
go.mod
View File

@ -1,7 +1,7 @@
module github.com/wailsapp/wails
require (
github.com/Masterminds/semver v1.4.2 // indirect
github.com/Masterminds/semver v1.4.2
github.com/dchest/cssmin v0.0.0-20151210170030-fb8d9b44afdc // indirect
github.com/dchest/htmlmin v0.0.0-20150526090704-e254725e81ac
github.com/dchest/jsmin v0.0.0-20160823214000-faeced883947 // indirect
@ -12,9 +12,8 @@ require (
github.com/kennygrant/sanitize v1.2.4
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/leaanthony/mewn v0.10.7
github.com/leaanthony/slicer v1.3.1
github.com/leaanthony/slicer v1.3.2
github.com/leaanthony/spinner v0.5.3
github.com/masterminds/semver v1.4.2
github.com/mattn/go-colorable v0.1.1 // indirect
github.com/mattn/go-isatty v0.0.7 // indirect
github.com/mitchellh/go-homedir v1.1.0

14
go.sum
View File

@ -25,7 +25,6 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -33,22 +32,18 @@ github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/leaanthony/mewn v0.10.7 h1:jCcNJyIUOpwj+I5SuATvCugDjHkoo+j6ubEOxxrxmPA=
github.com/leaanthony/mewn v0.10.7/go.mod h1:CRkTx8unLiSSilu/Sd7i1LwrdaAL+3eQ3ses99qGMEQ=
github.com/leaanthony/slicer v1.3.1 h1:n2iIV2sxvL/3bpnmVY0vBjXf3yYFWcB6CYLVMrzJxRw=
github.com/leaanthony/slicer v1.3.1/go.mod h1:VMB/HGvr3uR3MRpFWHWAm0w+DHQLzPHYe2pKfpFlQIQ=
github.com/leaanthony/slicer v1.3.2 h1:kGWWFoyaY5WzwGrUsHXMmGbssuYthP4qYBNlkNpNAB8=
github.com/leaanthony/slicer v1.3.2/go.mod h1:VMB/HGvr3uR3MRpFWHWAm0w+DHQLzPHYe2pKfpFlQIQ=
github.com/leaanthony/spinner v0.5.3 h1:IMTvgdQCec5QA4qRy0wil4XsRP+QcG1OwLWVK/LPZ5Y=
github.com/leaanthony/spinner v0.5.3/go.mod h1:oHlrvWicr++CVV7ALWYi+qHk/XNA91D9IJ48IqmpVUo=
github.com/leaanthony/synx v0.1.0 h1:R0lmg2w6VMb8XcotOwAe5DLyzwjLrskNkwU7LLWsyL8=
github.com/leaanthony/synx v0.1.0/go.mod h1:Iz7eybeeG8bdq640iR+CwYb8p+9EOsgMWghkSRyZcqs=
github.com/leaanthony/wincursor v0.1.0 h1:Dsyp68QcF5cCs65AMBmxoYNEm0n8K7mMchG6a8fYxf8=
github.com/leaanthony/wincursor v0.1.0/go.mod h1:7TVwwrzSH/2Y9gLOGH+VhA+bZhoWXBRgbGNTMk+yimE=
github.com/masterminds/semver v1.4.2 h1:BgrAYDjlAebjtOwS7C/1QZoh5WgyXx4b59ydc+Ph8xI=
github.com/masterminds/semver v1.4.2/go.mod h1:s7KNT9fnd7edGzwwP7RBX4H0v/CYd5qdOLfkL1V75yg=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
@ -73,11 +68,8 @@ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/wailsapp/webview v0.2.7 h1:fN5L5H9Oivg9IJPk7uaXQnjqB68Fny11ZWkIaTIZHmk=
github.com/wailsapp/webview v0.2.7/go.mod h1:XO9HJbKWokDxUYTWQEBCYg95n/To1v7PxvanDNVf8hY=
github.com/zserge/webview v0.0.0-20190123072648-16c93bcaeaeb/go.mod h1:a1CV8KR4Dd1eP2g+mEijGOp+HKczwdKHWyx0aPHKvo4=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -86,10 +78,8 @@ golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR17
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb h1:pf3XwC90UUdNPYWZdFjhGBE7DUFuK3Ct1zWmZ65QN30=
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 h1:rM0ROo5vb9AdYJi1110yjWGMej9ITfKddS89P3Fkhug=

View File

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

View File

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

View File

@ -1,47 +1,46 @@
package wails
package binding
import (
"fmt"
"reflect"
"unicode"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
"github.com/wailsapp/wails/lib/interfaces"
)
/**
binding:
Name() // Full name (package+name)
Call(params)
**/
type bindingManager struct {
// Manager handles method binding
type Manager struct {
methods map[string]*boundMethod
functions map[string]*boundFunction
internalMethods *internalMethods
initMethods []*boundMethod
log *CustomLogger
renderer Renderer
runtime *Runtime // The runtime object to pass to bound structs
log *logger.CustomLogger
renderer interfaces.Renderer
runtime interfaces.Runtime // The runtime object to pass to bound structs
objectsToBind []interface{}
bindPackageNames bool // Package name should be considered when binding
}
func newBindingManager() *bindingManager {
result := &bindingManager{
// NewManager creates a new Manager struct
func NewManager() interfaces.BindingManager {
result := &Manager{
methods: make(map[string]*boundMethod),
functions: make(map[string]*boundFunction),
log: newCustomLogger("Bind"),
log: logger.NewCustomLogger("Bind"),
internalMethods: newInternalMethods(),
}
return result
}
// Sets flag to indicate package names should be considered when binding
func (b *bindingManager) BindPackageNames() {
// BindPackageNames sets a flag to indicate package names should be considered when binding
func (b *Manager) BindPackageNames() {
b.bindPackageNames = true
}
func (b *bindingManager) start(renderer Renderer, runtime *Runtime) error {
// Start the binding manager
func (b *Manager) Start(renderer interfaces.Renderer, runtime interfaces.Runtime) error {
b.log.Info("Starting")
b.renderer = renderer
b.runtime = runtime
@ -54,7 +53,7 @@ func (b *bindingManager) start(renderer Renderer, runtime *Runtime) error {
return err
}
func (b *bindingManager) initialise() error {
func (b *Manager) initialise() error {
var err error
// var binding *boundMethod
@ -92,7 +91,7 @@ func (b *bindingManager) initialise() error {
}
// bind the given struct method
func (b *bindingManager) bindMethod(object interface{}) error {
func (b *Manager) bindMethod(object interface{}) error {
objectType := reflect.TypeOf(object)
baseName := objectType.String()
@ -142,7 +141,7 @@ func (b *bindingManager) bindMethod(object interface{}) error {
}
// bind the given function object
func (b *bindingManager) bindFunction(object interface{}) error {
func (b *Manager) bindFunction(object interface{}) error {
newFunction, err := newBoundFunction(object)
if err != nil {
@ -159,18 +158,18 @@ func (b *bindingManager) bindFunction(object interface{}) error {
return nil
}
// Save the given object to be bound at start time
func (b *bindingManager) bind(object interface{}) {
// Bind saves the given object to be bound at start time
func (b *Manager) Bind(object interface{}) {
// Store binding
b.objectsToBind = append(b.objectsToBind, object)
}
func (b *bindingManager) processInternalCall(callData *callData) (interface{}, error) {
func (b *Manager) processInternalCall(callData *messages.CallData) (interface{}, error) {
// Strip prefix
return b.internalMethods.processCall(callData)
}
func (b *bindingManager) processFunctionCall(callData *callData) (interface{}, error) {
func (b *Manager) processFunctionCall(callData *messages.CallData) (interface{}, error) {
// Return values
var result []reflect.Value
var err error
@ -199,7 +198,7 @@ func (b *bindingManager) processFunctionCall(callData *callData) (interface{}, e
return result[0].Interface(), nil
}
func (b *bindingManager) processMethodCall(callData *callData) (interface{}, error) {
func (b *Manager) processMethodCall(callData *messages.CallData) (interface{}, error) {
// Return values
var result []reflect.Value
var err error
@ -233,8 +232,8 @@ func (b *bindingManager) processMethodCall(callData *callData) (interface{}, err
return nil, nil
}
// process an incoming call request
func (b *bindingManager) processCall(callData *callData) (result interface{}, err error) {
// ProcessCall processes the given call request
func (b *Manager) ProcessCall(callData *messages.CallData) (result interface{}, err error) {
b.log.Debugf("Wanting to call %s", callData.BindingName)
// Determine if this is function call or method call by the number of
@ -272,7 +271,7 @@ func (b *bindingManager) processCall(callData *callData) (result interface{}, er
// callWailsInitMethods calls all of the WailsInit methods that were
// registered with the runtime object
func (b *bindingManager) callWailsInitMethods() error {
func (b *Manager) callWailsInitMethods() error {
// Create reflect value for runtime object
runtimeValue := reflect.ValueOf(b.runtime)
params := []reflect.Value{runtimeValue}

View File

@ -1,10 +1,12 @@
package wails
package binding
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"github.com/wailsapp/wails/lib/logger"
)
type boundMethod struct {
@ -13,7 +15,7 @@ type boundMethod struct {
method reflect.Value
inputs []reflect.Type
returnTypes []reflect.Type
log *CustomLogger
log *logger.CustomLogger
hasErrorReturnType bool // Indicates if there is an error return type
isWailsInit bool
}
@ -27,7 +29,7 @@ func newBoundMethod(name string, fullName string, method reflect.Value, objectTy
}
// Setup logger
result.log = newCustomLogger(result.fullName)
result.log = logger.NewCustomLogger(result.fullName)
// Check if Parameters are valid
err := result.processParameters()
@ -57,7 +59,7 @@ func (b *boundMethod) processParameters() error {
b.inputs[index] = param
typ := param
index := index
b.log.DebugFields("Input param", Fields{
b.log.DebugFields("Input param", logger.Fields{
"index": index,
"name": name,
"kind": kind,
@ -166,10 +168,10 @@ func (b *boundMethod) setInputValue(index int, typ reflect.Type, val interface{}
reflect.Map,
reflect.Ptr,
reflect.Slice:
logger.Debug("Converting nil to type")
b.log.Debug("Converting nil to type")
result = reflect.ValueOf(val).Convert(typ)
default:
logger.Debug("Cannot convert nil to type, returning error")
b.log.Debug("Cannot convert nil to type, returning error")
return reflect.Zero(typ), fmt.Errorf("Unable to use null value for parameter %d of method %s", index+1, b.fullName)
}
} else {

View File

@ -1,31 +1,34 @@
package wails
package event
import (
"fmt"
"sync"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
"github.com/wailsapp/wails/lib/interfaces"
)
// eventManager handles and processes events
type eventManager struct {
incomingEvents chan *eventData
// Manager handles and processes events
type Manager struct {
incomingEvents chan *messages.EventData
listeners map[string][]*eventListener
exit bool
log *CustomLogger
renderer Renderer // Messages will be dispatched to the frontend
log *logger.CustomLogger
renderer interfaces.Renderer // Messages will be dispatched to the frontend
}
// newEventManager creates a new event manager with a 100 event buffer
func newEventManager() *eventManager {
return &eventManager{
incomingEvents: make(chan *eventData, 100),
// NewManager creates a new event manager with a 100 event buffer
func NewManager() interfaces.EventManager {
return &Manager{
incomingEvents: make(chan *messages.EventData, 100),
listeners: make(map[string][]*eventListener),
exit: false,
log: newCustomLogger("Events"),
log: logger.NewCustomLogger("Events"),
}
}
// PushEvent places the given event on to the event queue
func (e *eventManager) PushEvent(eventData *eventData) {
func (e *Manager) PushEvent(eventData *messages.EventData) {
e.incomingEvents <- eventData
}
@ -40,7 +43,7 @@ type eventListener struct {
}
// Creates a new event listener from the given callback function
func (e *eventManager) addEventListener(eventName string, callback func(...interface{}), counter int) error {
func (e *Manager) addEventListener(eventName string, callback func(...interface{}), counter int) error {
// Sanity check inputs
if callback == nil {
@ -65,18 +68,19 @@ func (e *eventManager) addEventListener(eventName string, callback func(...inter
return nil
}
func (e *eventManager) On(eventName string, callback func(...interface{})) {
// On adds a listener for the given event
func (e *Manager) On(eventName string, callback func(...interface{})) {
// Add a persistent eventListener (counter = 0)
e.addEventListener(eventName, callback, 0)
}
// Emit broadcasts the given event to the subscribed listeners
func (e *eventManager) Emit(eventName string, optionalData ...interface{}) {
e.incomingEvents <- &eventData{Name: eventName, Data: optionalData}
func (e *Manager) Emit(eventName string, optionalData ...interface{}) {
e.incomingEvents <- &messages.EventData{Name: eventName, Data: optionalData}
}
// Starts the event manager's queue processing
func (e *eventManager) start(renderer Renderer) {
// Start the event manager's queue processing
func (e *Manager) Start(renderer interfaces.Renderer) {
e.log.Info("Starting")
@ -95,7 +99,7 @@ func (e *eventManager) start(renderer Renderer) {
// TODO: Listen for application exit
select {
case event := <-e.incomingEvents:
e.log.DebugFields("Got Event", Fields{
e.log.DebugFields("Got Event", logger.Fields{
"data": event.Data,
"name": event.Name,
})
@ -143,6 +147,6 @@ func (e *eventManager) start(renderer Renderer) {
wg.Wait()
}
func (e *eventManager) stop() {
func (e *Manager) stop() {
e.exit = true
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,35 +1,42 @@
package wails
package ipc
import (
"fmt"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
)
type ipcManager struct {
renderer Renderer // The renderer
// Manager manages the IPC subsystem
type Manager struct {
renderer interfaces.Renderer // The renderer
messageQueue chan *ipcMessage
// quitChannel chan struct{}
// signals chan os.Signal
log *CustomLogger
eventManager *eventManager
bindingManager *bindingManager
log *logger.CustomLogger
eventManager interfaces.EventManager
bindingManager interfaces.BindingManager
}
func newIPCManager() *ipcManager {
result := &ipcManager{
// NewManager creates a new IPC Manager
func NewManager() interfaces.IPCManager {
result := &Manager{
messageQueue: make(chan *ipcMessage, 100),
// quitChannel: make(chan struct{}),
// signals: make(chan os.Signal, 1),
log: newCustomLogger("IPC"),
log: logger.NewCustomLogger("IPC"),
}
return result
}
// Sets the renderer, returns the dispatch function
func (i *ipcManager) bindRenderer(renderer Renderer) {
// BindRenderer sets the renderer, returns the dispatch function
func (i *Manager) BindRenderer(renderer interfaces.Renderer) {
i.renderer = renderer
}
func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingManager) {
// Start the IPC Manager
func (i *Manager) Start(eventManager interfaces.EventManager, bindingManager interfaces.BindingManager) {
// Store manager references
i.eventManager = eventManager
@ -42,36 +49,36 @@ func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingMa
for running {
select {
case incomingMessage := <-i.messageQueue:
i.log.DebugFields("Processing message", Fields{
i.log.DebugFields("Processing message", logger.Fields{
"1D": &incomingMessage,
})
switch incomingMessage.Type {
case "call":
callData := incomingMessage.Payload.(*callData)
i.log.DebugFields("Processing call", Fields{
callData := incomingMessage.Payload.(*messages.CallData)
i.log.DebugFields("Processing call", logger.Fields{
"1D": &incomingMessage,
"bindingName": callData.BindingName,
"data": callData.Data,
})
go func() {
result, err := bindingManager.processCall(callData)
i.log.DebugFields("processed call", Fields{"result": result, "err": err})
result, err := bindingManager.ProcessCall(callData)
i.log.DebugFields("processed call", logger.Fields{"result": result, "err": err})
if err != nil {
incomingMessage.ReturnError(err.Error())
} else {
incomingMessage.ReturnSuccess(result)
}
i.log.DebugFields("Finished processing call", Fields{
i.log.DebugFields("Finished processing call", logger.Fields{
"1D": &incomingMessage,
})
}()
case "event":
// Extract event data
eventData := incomingMessage.Payload.(*eventData)
eventData := incomingMessage.Payload.(*messages.EventData)
// Log
i.log.DebugFields("Processing event", Fields{
i.log.DebugFields("Processing event", logger.Fields{
"name": eventData.Name,
"data": eventData.Data,
})
@ -80,24 +87,24 @@ func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingMa
i.eventManager.PushEvent(eventData)
// Log
i.log.DebugFields("Finished processing event", Fields{
i.log.DebugFields("Finished processing event", logger.Fields{
"name": eventData.Name,
})
case "log":
logdata := incomingMessage.Payload.(*logData)
logdata := incomingMessage.Payload.(*messages.LogData)
switch logdata.Level {
case "info":
logger.Info(logdata.Message)
logger.GlobalLogger.Info(logdata.Message)
case "debug":
logger.Debug(logdata.Message)
logger.GlobalLogger.Debug(logdata.Message)
case "warning":
logger.Warning(logdata.Message)
logger.GlobalLogger.Warn(logdata.Message)
case "error":
logger.Error(logdata.Message)
logger.GlobalLogger.Error(logdata.Message)
case "fatal":
logger.Fatal(logdata.Message)
logger.GlobalLogger.Fatal(logdata.Message)
default:
i.log.ErrorFields("Invalid log level sent", Fields{
logger.ErrorFields("Invalid log level sent", logger.Fields{
"level": logdata.Level,
"message": logdata.Message,
})
@ -107,7 +114,7 @@ func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingMa
}
// Log
i.log.DebugFields("Finished processing message", Fields{
i.log.DebugFields("Finished processing message", logger.Fields{
"1D": &incomingMessage,
})
// case <-manager.quitChannel:
@ -125,7 +132,7 @@ func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingMa
// Dispatch receives JSON encoded messages from the renderer.
// It processes the message to ensure that it is valid and places
// the processed message on the message queue
func (i *ipcManager) Dispatch(message string) {
func (i *Manager) Dispatch(message string) {
// Create a new IPC Message
incomingMessage, err := newIPCMessage(message, i.SendResponse)
@ -148,7 +155,7 @@ func (i *ipcManager) Dispatch(message string) {
}
// SendResponse sends the given response back to the frontend
func (i *ipcManager) SendResponse(response *ipcResponse) error {
func (i *Manager) SendResponse(response *ipcResponse) error {
// Serialise the Message
data, err := response.Serialise()

View File

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

View File

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

View File

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

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

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

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

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

View File

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

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

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

42
log.go
View File

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

View File

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

217
runtime/assets/bridge.js Normal file
View File

@ -0,0 +1,217 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The lightweight framework for web-like apps
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 6 */
function init() {
// Bridge object
window.wailsbridge = {
reconnectOverlay: null,
reconnectTimer: 300,
wsURL: 'ws://localhost:34115/bridge',
connectionState: null,
config: {},
websocket: null,
callback: null,
overlayHTML:
'<div class="wails-reconnect-overlay"><div class="wails-reconnect-overlay-content"><div class="wails-reconnect-overlay-title">Wails Bridge</div><br><div class="wails-reconnect-overlay-loadingspinner"></div><br><div id="wails-reconnect-overlay-message">Waiting for backend</div></div></div>',
overlayCSS:
'.wails-reconnect-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.6);font-family:sans-serif;display:none;z-index:999999}.wails-reconnect-overlay-content{padding:20px 30px;text-align:center;width:20em;position:relative;height:14em;border-radius:1em;margin:5% auto 0;background-color:#fff;box-shadow:1px 1px 20px 3px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAAuCAMAAACPpbA7AAAAqFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAAAAAAAAEBAQAAAAAAAAAAAAEBAQEBAQDAwMBAQEAAAABAQEAAAAAAAAAAAABAQEAAAAAAAACAgICAgIBAQEAAAAAAAAAAAAAAAAAAAAAAAABAQEAAAACAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQWKCj6oAAAAN3RSTlMALiIqDhkGBAswJjP0GxP6NR4W9/ztjRDMhWU50G9g5eHXvbZ9XEI9xZTcqZl2aldKo55QwoCvZUgzhAAAAs9JREFUSMeNleeWqjAUhU0BCaH3Itiw9zKT93+zG02QK1hm/5HF+jzZJ6fQe6cyXE+jg9X7o9wxuylIIf4Tv2V3+bOrEXnf8dwQ/KQIGDN2/S+4OmVCVXL/ScBnfibxURqIByP/hONE8r8T+bDMlQ98KSl7Y8hzjpS8v1qtDh8u5f8KQpGpfnPPhqG8JeogN37Hq9eaN2xRhIwAaGnvws8F1ShxqK5ob2twYi1FAMD4rXsYtnC/JEiRbl4cUrCWhnMCLRFemXezXbb59QK4WASOsm6n2W1+4CBT2JmtzQ6fsrbGubR/NFbd2g5Y179+5w/GEHaKsHjYCet7CgrXU3txarNC7YxOVJtIj4/ERzMdZfzc31hp+8cD6eGILgarZY9uZ12hAs03vfBD9C171gS5Omz7OcvxALQIn4u8RRBBBcsi9WW2woO9ipLgfzpYlggg3ZRdROUC8KT7QLqq3W9KB5BbdFVg4929kdwp6+qaZnMCCNBdj+NyN1W885Ry/AL3D4AQbsVV4noCiM/C83kyYq80XlDAYQtralOiDzoRAHlotWl8q2tjvYlOgcg1A8jEApZa+C06TBdAz2Qv0wu11I/zZOyJQ6EwGez2P2b8PIQr1hwwnAZsAxwA4UAYOyXUxM/xp6tHAn4GUmPGM9R28oVxgC0e/zQJJI6DyhyZ1r7uzRQhpcW7x7vTaWSzKSG6aep77kroTEl3U81uSVaUTtgEINfC8epx+Q4F9SpplHG84Ek6m4RAq9/TLkOBrxyeuddZhHvGIp1XXfFy3Z3vtwNblKGiDn+J+92vwwABHghj7HnzlS1H5kB49AZvdGCFgiBPq69qfXPr3y++yilF0ON4R8eR7spAsLpZ95NqAW5tab1c4vkZm6aleajchMwYTdILQQTwE2OV411ZM9WztDjPql12caBi6gDpUKmDd4U1XNdQxZ4LIXQ5/Tr4P7I9tYcFrDK3AAAAAElFTkSuQmCC);background-repeat:no-repeat;background-position:center}.wails-reconnect-overlay-title{font-size:2em}.wails-reconnect-overlay-message{font-size:1.3em}.wails-reconnect-overlay-loadingspinner{pointer-events:none;width:2.5em;height:2.5em;border:.4em solid transparent;border-color:#3E67EC #eee #eee;border-radius:50%;animation:loadingspin 1s linear infinite;margin:auto;padding:2.5em}@keyframes loadingspin{100%{transform:rotate(360deg)}}',
log: function (message) {
// eslint-disable-next-line
console.log(
'%c wails bridge %c ' + message + ' ',
'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem',
'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem'
);
}
};
}
// Adapted from webview - thanks zserge!
function injectCSS(css) {
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.setAttribute('type', 'text/javascript');
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.wails);
}
});
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);
//log("Binding: " + binding)
window.wails._.NewBinding(binding);
break;
// Call back
case 'c':
var callbackData = message.data.slice(1);
window.wails._.Callback(callbackData);
break;
default:
window.wails.Log.Error('Unknown message type received: ' + message.data[0]);
}
}
// Start by showing the overlay...
showReconnectOverlay();
// ...and attempt to connect
connect();
}
function start(callback) {
// Set up the bridge
init();
// Save the callback
window.wailsbridge.callback = callback;
// Start Bridge
startBridge();
}
function Init(callback) {
start(callback);
}
module.exports = { Init };

View File

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

1
runtime/assets/wails.js Normal file
View File

@ -0,0 +1 @@
!function(n){var t={};function e(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return n[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}e.m=n,e.c=t,e.d=function(n,t,r){e.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:r})},e.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},e.t=function(n,t){if(1&t&&(n=e(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(e.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var o in n)e.d(r,o,function(t){return n[t]}.bind(null,o));return r},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},e.p="",e(e.s=0)}([function(n,t,e){"use strict";e.r(t);var r={};e.r(r),e.d(r,"Debug",function(){return c}),e.d(r,"Info",function(){return u}),e.d(r,"Warning",function(){return l}),e.d(r,"Error",function(){return f}),e.d(r,"Fatal",function(){return d});var o={};function a(n,t,e){var r={type:n,callbackID:e,payload:t};!function(n){window.external.invoke(n)}(JSON.stringify(r))}function i(n,t){a("log",{level:n,message:t})}function c(n){i("debug",n)}function u(n){i("info",n)}function l(n){i("warning",n)}function f(n){i("error",n)}function d(n){i("fatal",n)}e.r(o),e.d(o,"OpenURL",function(){return y}),e.d(o,"OpenFile",function(){return g});var s,p={};function v(n,t,e){return null!=e&&null!=e||(e=0),new Promise(function(r,o){var i;do{i=n+"-"+s()}while(p[i]);if(e>0)var c=setTimeout(function(){o(Error("Call to "+n+" timed out. Request ID: "+i))},e);p[i]={timeoutHandle:c,reject:o,resolve:r};try{a("call",{bindingName:n,data:JSON.stringify(t)},i)}catch(n){console.error(n)}})}function w(n,t){return v(".wails."+n,t)}function y(n){return w("Browser.OpenURL",n)}function g(n){return w("Browser.OpenFile",n)}s=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()};var m=function n(t,e){!function(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),e=e||-1,this.Callback=function(n){return t.apply(null,n),-1!==e&&0===(e-=1)}},b={};function h(n,t,e){b[n]=b[n]||[];var r=new m(t,e);b[n].push(r)}function O(n){a("event",{name:n,data:JSON.stringify([].slice.apply(arguments).slice(1))})}var S={};function j(n){try{return new Function("var "+n),!0}catch(n){return!1}}function k(){return(k=Object.assign||function(n){for(var t=1;t<arguments.length;t++){var e=arguments[t];for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r])}return n}).apply(this,arguments)}window.backend={},e.d(t,"Init",function(){return N}),window.wails=window.wails||{},window.backend={};var E={NewBinding:function(n){var t=[].concat(n.split(".").splice(1)),e=window.backend;if(t.length>1)for(var r=0;r<t.length-1;r+=1){var o=t[r];if(!j(o))return new Error("".concat(o," is not a valid javascript identifier."));e[o]={},e=e[o]}var a=t.pop();if(!j(a))return new Error("".concat(a," is not a valid javascript identifier."));e[a]=function(){var t=0;function e(){var e=[].slice.call(arguments);return v(n,e,t)}return e.setTimeout=function(n){t=n},e.getTimeout=function(){return t},e}()},Callback:function(n){var t;n=decodeURIComponent(n.replace(/\s+/g,"").replace(/[0-9a-f]{2}/g,"%$&"));try{t=JSON.parse(n)}catch(t){var e="Invalid JSON passed to callback: ".concat(t.message,". Message: ").concat(n);throw c(e),new Error(e)}var r=t.callbackid,o=p[r];if(!o){var a="Callback '".concat(r,"' not registed!!!");throw console.error(a),new Error(a)}clearTimeout(o.timeoutHandle),delete p[r],t.error?o.reject(t.error):o.resolve(t.data)},Notify:function(n,t){if(b[n]){for(var e=b[n].slice(),r=0;r<b[n].length;r+=1){var o=b[n][r],a=[];if(t)try{a=JSON.parse(t)}catch(t){f("Invalid JSON data sent to notify. Event name = "+n)}o.Callback(a)&&e.splice(r,1)}b[n]=e}},AddScript:function(n,t){var e=document.createElement("script");e.text=n,document.body.appendChild(e),t&&O(t)},InjectCSS:function(n){var t=document.createElement("style");t.setAttribute("type","text/css"),t.styleSheet?t.styleSheet.cssText=n:t.appendChild(document.createTextNode(n)),(document.head||document.getElementsByTagName("head")[0]).appendChild(t)},Init:N},C={Log:r,Browser:o,Events:{On:function(n,t){h(n,t)},Emit:O,Heartbeat:function(n,t,e){var r=null;S[n]=function(){clearInterval(r),e()},r=setInterval(function(){O(n)},t)},Acknowledge:function(n){if(!S[n])throw new f("Cannot acknowledge unknown heartbeat '".concat(n,"'"));S[n]()}},_:E};function N(n){n()}k(window.wails,C),O("wails:loaded")}]);

21
runtime/browser.go Normal file
View File

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

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