+
+
+## Tooling
+
+The Wails cli has developer tooling built in, but needs activating. To create a developer version, do the following:
+
+```
+cd cmd/wails
+go install --tags=dev
+```
+
+This unlocks a `wails dev` command that has subcommands for development.
+
+### Creating new project templates
+
+With a developer enabled cli, you can run `wails dev newtemplate` to create a new project template. You will be asked a number of questions regarding your template and as a result, a new directory will be created in `/cmd/templates`.
+
+Here is an example run:
+
+```
+Wails v0.14.4-pre - Generating new project template
+
+? Please enter the name of your template (eg: React/Webpack Basic): Mithril Basic
+? Please enter a short description for the template (eg: React with Webpack 4): Mithril with Webpack 3
+? Please enter a long description: Mithril v2.0.0-rc.4 with Webpack 4
+? Please enter the name of the directory the frontend code resides (eg: frontend): frontend
+? Please enter the install command (eg: npm install): npm install
+? Please enter the build command (eg: npm run build): npm run build
+? Please enter the serve command (eg: npm run serve): npm run serve
+? Please enter the name of the directory to copy the wails bridge runtime (eg: src): src
+? Please enter a directory name for the template: mithril-basic
+Created new template 'Mithril Basic' in directory '/Users/lea/Projects/wails/cmd/templates/mithril-basic'
+```
+This generates the following `template.json`:
+
+```json
+{
+ "name": "Mithril Basic",
+ "version": "1.0.0",
+ "shortdescription": "Mithril with Webpack 3",
+ "description": "Mithril v2.0.0-rc.4 with Webpack 4",
+ "install": "npm install",
+ "build": "npm run build",
+ "author": "Duncan Disorderly ",
+ "created": "2019-05-20 20:16:30.394489 +1000 AEST m=+159.490635188",
+ "frontenddir": "frontend",
+ "serve": "npm run serve",
+ "bridge": "src",
+ "wailsdir": ""
+}
+```
+
+*Note: The `wailsdir` key is currently unused but will be used in place of bridge in the [near future](https://github.com/wailsapp/wails/issues/88)*
\ No newline at end of file
diff --git a/zh/home.md b/zh/home.md
new file mode 100644
index 0000000..7b0ae90
--- /dev/null
+++ b/zh/home.md
@@ -0,0 +1,235 @@
+---
+sidebar: on
+sidebarDepth: 1
+---
+
+# Wails
+
+> 使用Go&Web Technologies构建桌面应用程序的框架。
+
+## 关于
+
+向 `Go` 程序提供 `Web` 界面的传统方法是通过内置的 `Web` 服务器。 `Wails` 提供了一种不同的方法:它提供了将 `Go` 代码和 `Web` 前端包装到单个二进制文件中的能力。通过处理项目创建,编译和捆绑, Wails cli可以使您轻松实现这一目标。您要做的就是发挥创意!
+
+## 功能
+
+* Use standard Go libraries/frameworks for the backend
+* Use any frontend technology to build your UI
+* Expose Go methods/functions to the frontend via a single bind command
+* Uses native rendering engines - no embedded browser
+* Shared events system
+* Native file dialogs
+* Powerful cli tool
+* Multiplatform
+
+## 总览
+
+`Wails` 是一个框架,可帮助使用 `Go` and `Web Technologies` 编写桌面应用程序。对于前端,它使用[Webview] [1]库。反过来,它使用平台的本机渲染引擎(当前用于 `Linux` 和 `Mac` 的 `Webkit` ,用于 `Windows` 的 `MSHTML` )。前端使用HTML /Javascript /CSS进行编码,后端是纯 `Go` 。通过绑定机制,可以将Go代码作为返回 `Promise` 的功能公开给前端。该项目编译为单个可执行文件,将所有资产捆绑到其中。在 `Windows` 和 `MacOS` 上,可以将二进制文件捆绑到特定于平台的程序包中进行分发。
+
+首先,让我们完成设置过程。
+
+## 概念
+
+`Wails` 旨在使 `Web` 技术和 `Go` 之间的差距尽可能小。前端是[Webview] [1]组件,您可以使用喜欢的任何常见 `Javascript` 框架开发前端代码,并与其中的 `Go` 代码进行无缝交互。这是通过共享 `IPC` 机制完成的。
+
+
+
+ * App.vue - Vue 项目根组件
+ * assets - 前端资源目录
+ * components - 前端组件目录
+ * main.js - 前端项目的入口
+ * wailsbridge.js - 前端和Go程序运行的桥梁
+
+该项目分为两部分-后端由根目录中的Go文件组成,而前端则位于 `frontend` 目录中。前端项目是一个(大多数)标准 `Vue` 项目,在 `vue.config.js` 中具有一些自定义 `Webpack` 设置
+
+由于 `Vue/Webpack` 模板中包含完整的前端项目,因此项目元数据包含有关如何安装和构建它的详细信息。这是 `Vue/Webpack` 项目的示例 `project.json` 文件:
+
+``` json
+{
+ "name": "My Project",
+ "description": "Enter your project description",
+ "author": {
+ "name": "John Doe",
+ "email": "jd@jd.com"
+ },
+ "version": "0.1.0",
+ "binaryname": "my-project",
+ "frontend": {
+ "dir": "frontend",
+ "install": "npm install",
+ "build": "npm run build",
+ "bridge": "src",
+ "serve": "npm run serve"
+ }
+}
+```
+
+如您所见,还有一个附加部分描述了前端项目。这些键具有以下含义:
+
+| Key | Meaning |
+| -------- | ----------------------------------------------------------- |
+| dir | 前端项目所在目录 |
+| install | 执行以安装前端依赖 |
+| build | 执行以生成前端项目的命令。 在此示例中,这将仅运行前端的package.json文件中定义的构建脚本。 |
+| bridge | Wails桥接脚本所在的目录 |
+| serve | 以“桥接”模式提供服务的命令(稍后会详细介绍)|
+
+通常情况下,您无需编辑这些值。 提供它们是为了使模板设计人员具有灵活性。
+
+### 以服务形式启动项目
+
+`Wails` 可以使用您的标准前端工具,但仍然可以调用 `Go` 代码。 这是通过以桥接模式“服务”您的后端来完成的。 当您的前端启动时,它将连接到后端,并在后台进行所有绑定。
+
+您可以通过在项目目录中运行 `wails serve` 命令来为后端服务。
+
+您现在可以在前端目录中运行`npm run serve',并使用浏览器和·Vue devtools·插件开发应用程序。
+
+### 构建项目
+
+一旦可以将项目构建到单个应用程序中,请在项目目录中运行 `wails build` 。 默认情况下,项目以 `production mode` 构建。 如果您需要在调试模式下运行应用,请使用 `-d` 标志进行构建。 以调试模式运行意味着:
+
+ + 调试消息被打印到终端
+ + 您可以右键单击以在 `Web` 视图中检查您的应用程序(MacOS和Linux)
+ + 二进制文件具有许多标志,以帮助开发应用程序。 传递--help标志以查看选项。
+
+## 执行流程
+
+Wails应用程序的执行工作流程为:
+
+ + The application window is created
+ + The Wails Javascript runtime is injected into the frontend
+ + All functions that were bound in Go are setup in the frontend
+ + All WailsInit methods are called with the Go Runtime
+ + The application CSS is injected into the frontend
+ + Finally, the application Javascript is injected into the frontend
+ + On shutdown ( Ctrl-C, Kill Window or runtime. Window. Close() ), all WailsShutdown methods are called
+ + The applications exits cleanly
+
+ + 创建应用程序窗口
+ + `Wails Javascript` 运行时已注入前端
+ + `Go` 中绑定的所有功能都在前端设置
+ + 所有 `WailsInit` 方法都通过 `Go Runtime` 调用
+ + 将应用程序 `CSS` 注入前端
+ + 最后,将应用程序 `Javascript` 注入到前端
+ + 在关闭时(Ctrl-C,Kill Window或 `runtime.Window.Close()` ),将调用所有 `WailsShutdown` 方法
+ + 应用程序干净退出
diff --git a/zh/project_status.md b/zh/project_status.md
new file mode 100644
index 0000000..3ff7736
--- /dev/null
+++ b/zh/project_status.md
@@ -0,0 +1,83 @@
+---
+sidebar: on
+sidebarDepth: 1
+---
+
+# Project Status
+
+**This project is currently in BETA.**
+
+This means that in general it works, and there are a few things that are keeping it from becoming a v1 release. Any help with these issues would be greatly appreciated:
+
+ - The project has been mostly developed on Mac, with a lesser amount of testing done on Window and Linux. Feedback from users of these platforms will be invaluable.
+ - The docs have taken a long time to write. They need reviewing by fresh eyes. Whilst I've tried to keep the docs cross-platform, there may be areas that are lacking (screenshots!).
+ - Wails uses an older version of Webview, which uses a deprecated API for windowing on Mac. Attempts to use the newer releases have [not been successful](https://github.com/zserge/webview/issues/236). Any help on this would be appreciated.
+
+In building this project, I have followed the principle of:
+
+ * Make it work
+ * Make it right
+ * Make it fast
+
+It is currently at stage 1. The project has been through a fair number of rewrites and refactors and as such, there will be areas that need attention. I'm hoping with the help of the community we can get to at least stage 2.
+
+## Platform Support
+
+These are the current tested platforms. If you have Wails working on a platform not specified here, please consider making a PR.
+
+* MacOS 10.14
+* Windows 10
+* Linux
+ * RedHat
+ * Centos
+ * Ubuntu 18.04, 19.04
+ * Pop!_OS 19.04
+ * Debian 9, 10
+ * Gentoo
+ * Arch / Manjaro
+ * Manjaro
+ * Zorin
+
+## Template Support
+
+The current status of template/Platform support is as follows:
+
+| Template | Mac | Linux | Windows |
+| ---------- | ------- | --------- | --------------- |
+| Vue Basic | Working | Working | Working |
+| Vuetify | Working | Working | Working |
+| React | Working | Working | Working |
+| Angular | Working | Working | [Not Working](https://github.com/wailsapp/wails/issues/146) |
+
+
+## Roadmap
+
+The focus at the moment is very much on ironing out bugs and getting to v1.0. After that, there's a few avenues I'd like to explore:
+
+ * Support other UI Frameworks
+
+ Currently, Wails supports Vue, Vuetify, React and Angular projects, however there is no real reason it couldn't support other frameworks. It would be good (and relatively easy) to create Project Templates to support Preact, Svelte, etc.
+
+ * Create more examples
+
+ Even though Wails is only Beta, it's still possible to create some really cool things. As part of the journey, I created an MP3 player to see what was involved, using Go to decode artwork and sending that with the music to the frontend to play. I really look forward to seeing what can be built with this and hope to provide some sort of showcase, along with tutorials.
+
+ * Expanding the rendering targets
+
+ Wails has been designed to handle different targets for rendering your app. Currently it supports Webview and Browser (via a bridge). It would be good to look at other targets, EG: [Ultralight](https://ultralig.ht/).
+
+ * Support for Desktop Elements
+
+ There is currently no support for Menus, Tray, Notifications or any other native desktop elements. Initially, I thought that might be best served by the rendering target but perhaps it would be possible to support at the application layer.
+
+ * Cross-Compilation
+
+ The holy grail! I believe this might be possible via a docker container containing all the tooling. It's definitely something I think should be explored.
+
+ * Wails Project UI
+
+ Vue has an awesome UI for managing projects. It would be great to build something like this for managing Wails projects.
+
+ * VSCode Extension
+
+ It would be great to be able to have auto-complete of your bound methods whilst writing your frontend code. This would be a good solution as well as offering the usual helpers such as project creation, building, etc.
diff --git a/zh/quick_start.md b/zh/quick_start.md
new file mode 100644
index 0000000..3b8558d
--- /dev/null
+++ b/zh/quick_start.md
@@ -0,0 +1,192 @@
+---
+sidebar: on
+sidebarDepth: 1
+---
+
+# 快速开始
+
+## 概述
+
+`Wails` 应用包含两个部分:
+
+ + 用 `Go` 编写的后端
+ + 使用标准HTML/JS/CSS编写的前端
+
+使用 `wails build` 命令将它们编译并绑定在一起。这将首先将 `frontend` 项目构建成 `Javascript` 和 `CSS` 包。然后,它将构建主 `Go` 应用程序,该应用程序将两个前端资产文件作为应用程序的一部分。这将生成一个应用程序。
+
+### 前端
+
+`frontend` 是一个标准的前端项目(Vue, React等),它被编译成 `Javascript` 和 `CSS` 包。它位于项目的 `frontend` 目录中。除了将整个项目配置为两个包: `app.js` 和 `app.css` 之外,这个项目没有什么特别之处。
+
+### 后端
+
+后端最初由单个 `main.go` 组成。文件:
+
+``` go
+package main
+
+import (
+ "github.com/leaanthony/mewn"
+ "github.com/wailsapp/wails"
+)
+
+func basic() string {
+ return "Hello World!"
+}
+
+func main() {
+
+ js := mewn.String("./frontend/dist/app.js")
+ css := mewn.String("./frontend/dist/app.css")
+
+ app := wails.CreateApp(&wails.AppConfig{
+ Width: 1024,
+ Height: 768,
+ Title: "Quotes",
+ JS: js,
+ CSS: css,
+ Colour: "#131313",
+ })
+ app.Bind(basic)
+ app.Run()
+}
+```
+
+以下是有关其工作原理的简要说明:
+
+| 行 | 说明 |
+| -------- | -------------------------------------------------------------- |
+| 1 | 标准包指令|
+| 3-6 | 导入我们将用于处理资产的Wails框架和Mewn实用程序包|
+| 8-10 | 返回字符串 ` Hello World!` 的标准Go函数。 |
+| 12 | 主函数声明|
+| 14 | 以字符串形式读取前端 `javascript` 捆绑包|
+| 15 | 以字符串形式读取前端 `CSS` 捆绑包|
+| 17-24 | 创建一个新的 `Wails` 应用程序,为应用程序窗口指定宽度,高度,标题和颜色。我们还指定了希望应用程序呈现的 `Javascript` 和 `CSS` -先前在第14和15行中阅读的 `JS/CSS` |
+| 25 | 将我们的基本功能绑定到应用程序。然后我们可以使用以下代码从 `Javascript` 调用此方法: `window.backend.basic()` . |
+| 26 | 运行程序|
+
+如果您对前端或后端进行更改,则只需运行 `wails build` 即可重新生成您的应用程序。
+
+## 先决条件
+
+Wails使用 `cgo` 绑定到本机渲染引擎,因此需要大量依赖于平台的库以及 `Go` 的安装。基本要求是:
+
+* Go 1.12 以上
+* npm
+
+运行 `go version && npm --version` 进行验证。
+
+### MacOS
+
+确保已安装 `xcode` 命令行工具。可以通过运行以下命令来完成:
+
+ `xcode-select --install`
+
+### Linux
+
+#### Debian/Ubuntu
+
+ `sudo apt install libgtk-3-dev libwebkit2gtk-4.0-dev`
+_Debian: 8, 9, 10_
+
+_Ubuntu: 16.04, 18.04, 19.04_
+
+_也可以通过以下测试: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_
+
+#### Arch Linux
+
+ `sudo pacman -S webkit2gtk gtk3`
+_也成功测试了: ArcoLinuxB, Manjaro_
+
+#### Centos
+
+ `sudo yum install webkitgtk3-devel gtk3-devel`
+_CentOS 6, 7_
+
+#### Fedora
+
+ `sudo yum install webkit2gtk3-devel gtk3-devel`
+_Fedora 29, 30_
+
+#### VoidLinux & VoidLinux-musl
+
+ `xbps-install gtk+3-devel webkit2gtk-devel`
+
+#### Gentoo
+
+ `sudo emerge gtk+:3 webkit-gtk`
+::: tip
+如果您已成功在不同版本的 `Linux` 上安装了这些依赖项,请考虑单击底部的“帮助我们改善此页面”链接并提交PR。
+:::
+
+### Windows
+
+`Windows` 需要 `gcc` 和相关工具。推荐的下载是从[http://tdm-gcc.tdragon.net/download](http://tdm-gcc.tdragon.net/download)下载的。一旦安装完成,您就可以开始了。
+
+## 安装
+
+### 准备工作
+
+确保启用Go mod:
+
+ `export GO111MODULE=on`
+并且 go/bin 存在你的环境变量:
+
+ `echo $PATH | grep go/bin`
+
+### 安装
+
+安装就像运行以下命令一样简单:
+
+
+
+### 服务
+
+#### `wails serve`
+
+当使用 `wails` 开发应用程序时,[serve 命令](./reference/#serve) `wails serve` 是首选项 .
+
+::: tip
+这样可以在 _debug_ 模式下生成**更快**的轻量级构建,不包括 `npm` 构建脚本,节省了开发后端的时间,还允许使用 `npm run serve` 来进行前端的部分浏览器开发!
+:::
+
+#### `npm run serve`
+
+运行 `cd my-project/frontend` 切换到前端项目目录,并使用 `npm run serve` 预览你的前端界面 .
+
+## 下一步
+
+如果你想立即开始只做应用, 我们建议你通过 _awesome_ [tutorials](./tutorials/)探索 Wails.
+如果您希望在构建任何东西之前对框架有所了解,我们建议您仔细阅读一下[概念](./home.html#concepts).
+最后,如果您是高阶用户,并且想直接使用它,请转到[API参考](./reference/#api) 和[Cli参考](./reference/#cli)部分。
+::: tip
+加入我们 [Slack](https://gophers.slack.com/messages/CJ4P9F7MZ) 的通道.
+[_Invite_](https://invite.slack.golangbridge.org)
+为了支持还是打个招呼!
+:::
diff --git a/zh/reference/README.md b/zh/reference/README.md
new file mode 100644
index 0000000..c57b354
--- /dev/null
+++ b/zh/reference/README.md
@@ -0,0 +1,665 @@
+---
+sidebar: on
+sidebarDepth: 1
+---
+
+# Reference
+
+## API
+
+### Binding
+
+Having just a web frontend means nothing unless you can interact with the system. Wails enables this through 'binding' - making Go code callable from the frontend. There are 2 types of code you can bind to the frontend: Functions and Struct Methods. When they are bound, they may be used in the frontend.
+
+### Functions
+
+Binding a function is as easy as this:
+
+```go{18}
+package main
+
+import (
+ "github.com/wailsapp/wails"
+ "fmt"
+)
+
+func Greet(name string) string {
+ return fmt.Printf("Hello %s!", name)
+}
+
+func main() {
+
+ app := wails.CreateApp(&wails.AppConfig{
+ Width: 1024,
+ Height: 768,
+ })
+ app.Bind(Greet)
+ app.Run()
+}
+```
+
+When this is run, a Javascript function called 'Greet' is made available under the global 'backend' object. The function may be invoked by calling `backend.Greet`, EG: `backend.Greet("World")`. The dynamically generated functions return a standard promise. For this simple example, you could therefore print the result as so: `backend.Greet("World").then(console.log)`.
+
+### Type conversion
+
+Scalar types are automatically converted into the relevant Go types. Objects are converted to `map[string]interface{}`. If you wish to make those concrete types in Go, we recommend you use Hashicorp's [mapstructure](https://github.com/mitchellh/mapstructure).
+
+Example:
+
+Using a default Vue template project, we update `main.go` to include our struct and callback function:
+
+```go
+ type MyData struct {
+ A string
+ B float64
+ C int64
+ }
+
+ // We are expecting a javascript object of the form:
+ // { A: "", B: 0.0, C: 0 }
+ func basic(data map[string]interface{}) string {
+ var result MyData
+ fmt.Printf("data: %#v\n", data)
+
+ err := mapstructure.Decode(data, &result)
+ if err != nil {
+ // Do something with the error
+ }
+ fmt.Printf("result: %#v\n", result)
+ return "Hello World!"
+ }
+```
+
+In the frontend, we update the `getMessage` method in the `HelloWorld.vue` component to send our object:
+
+```go
+ getMessage: function() {
+ var self = this;
+ var mytestStruct = {
+ A: "hello",
+ B: 1.1,
+ C: 99
+ }
+ window.backend.basic(mytestStruct).then(result => {
+ self.message = result;
+ });
+ }
+```
+
+When you run this, you will get the following output:
+
+```
+data: map[string]interface {}{"A":"hello", "B":1.1, "C":99}
+Result: main.MyData{A:"hello", B:1.1, C:99}
+```
+
+::: danger
+It is recommended that business logic and data structure predominantly preside in the Go portion of your application and updates are sent to the front end using events. Managing state in 2 places leads to a very unhappy life.
+:::
+
+### Structs
+
+It is possible to bind structs to the frontend in a similar way but we must be clear on what this means: Binding a struct simply means exposing the public methods of the struct to the frontend. Wails does not attempt, or even believe, that binding data to the frontend is a good thing. Wails views the frontend as primarily a view layer with state and business logic normally handled by Go. As such, the structs that you bind to the front end should be viewed as a "wrapper" or an "interface".
+
+Binding a struct is as easy as:
+
+robot.go:
+```go
+package main
+
+import "fmt"
+
+type Robot struct {
+ Name string
+}
+
+func NewRobot() *Robot {
+ result := &Robot{
+ Name: "Robbie",
+ }
+ return result
+}
+
+func (t *Robot) Hello(name string) string {
+ return fmt.Sprintf("Hello %s! My name is %s", name, t.Name)
+}
+
+func (t *Robot) Rename(name string) string {
+ t.Name = name
+ return fmt.Sprintf("My name is now '%s'", t.Name)
+}
+
+func (t *Robot) privateMethod(name string) string {
+ t.Name = name
+ return fmt.Sprintf("My name is now '%s'", t.Name)
+}
+
+```
+main.go:
+```go
+package main
+
+import "github.com/wailsapp/wails"
+
+func main() {
+ app := wails.CreateApp(&wails.AppConfig{
+ Width: 1024,
+ Height: 768,
+ Title: "Binding Structs",
+ })
+
+ app.Bind(NewRobot())
+ app.Run()
+}
+```
+
+When the Robot struct is bound, it is made available at `backend.Robot` in the frontend. As the robot struct has a public method called `Hello`, then this is available to call at `backend.Robot.Hello`. The same is true for the `Rename` method. The robot struct also has another method called `privateMethod`, but as that is not public, it is not bound.
+
+Here is a demonstration of how this works by running the app in debug mode and using the inspector:
+
+
+
+
+
+#### Struct Initialisation
+
+If your struct has a special initialisation method, Wails will call it at startup. The signature for this method is:
+```go
+ WailsInit(runtime *wails.Runtime) error
+```
+This allows you to do some initialisation before the main application is launched.
+
+```go
+ type MyStruct struct {
+ runtime *wails.Runtime
+ }
+
+ func (s *MyStruct) WailsInit(runtime *wails.Runtime) error {
+ // Save runtime
+ s.runtime = runtime
+
+ // Do some other initialisation
+
+ return nil
+ }
+
+```
+
+If an error is returned, then the application will log the error and shutdown.
+
+The Runtime Object that is passed to it is the primary means for interacting with the application at runtime. It consists of a number of subsystems which provide access to different parts of the system. This is detailed in the [Wails Runtime](#wails-runtime) section.
+
+#### Struct Shutdown
+
+If your struct has a special shutdown method, Wails will call it during application shutdown. The signature for this method is:
+```go
+ WailsShutdown()
+```
+This allows you to do clean up any resources when the main application is terminated.
+
+```go
+ type MyStruct struct {
+ runtime *wails.Runtime
+ }
+
+ func (s *MyStruct) WailsInit(runtime *wails.Runtime) error {
+ // Save runtime
+ s.runtime = runtime
+
+ // Allocate some resources...
+
+ return nil
+ }
+
+
+ func (s *MyStruct) WailsShutdown() {
+
+ // De-Allocate some resources...
+
+ }
+
+```
+
+
+### Binding Rules
+
+Any Go function (or method) may be bound, so long as it follows the following rules:
+
+ - The function must return 0 - 2 results.
+ - If there are 2 return parameters, the last one must be an error type.
+ - If you return a struct, or struct pointer, the fields you wish to access in the frontend must have Go's standard json struct tags defined.
+
+If only one value is returned then it will either be available in the resolve or reject part of the promise depending on if it was an error type or not.
+
+Example 1:
+
+```go
+ func (m *MyStruct) MyBoundMethod(name string) string {
+ return fmt.Sprintf("Hello %s!", name)
+ }
+```
+In Javascript, the call to `MyStruct.MyBoundMethod` will return a promise that will resolve with a string.
+
+Example 2:
+
+```go
+ ...
+ func (m *MyStruct) AddUser(name string) error {
+ if m.userExists(name) {
+ return fmt.Errorf("user '%s' already exists");
+ }
+ m.saveUser(name)
+ return nil
+ }
+ ...
+```
+In Javascript, the call to `MyStruct.MyBoundMethod` with a new user name will return a promise that will resolve with no value. A call to `MyStruct.MyBoundMethod` with an existing user name will return a promise that will reject with the error set to "user '$name' already exists".
+
+It's good practice to return 2 values, a result and an error, as this maps directly to Javascript promises. If you are not returning anything, then perhaps events may be a better fit.
+
+### Important Detail!
+
+A very important detail to consider is that all calls to bound Go code are run in their own goroutine. Any bound functions should be authored with this in mind. The reason for this is to ensure that bound code does not block the main event loop in the application, which leads to a frozen UI.
+
+## Wails Runtime
+
+Wails comes with a runtime library that may be accessed from Javascript or Go. It has the following subsystems:
+
+ * Events
+ * Logging
+ * Window
+ * Dialog
+ * Browser
+
+**NOTE: At this time, the Javascript runtime does not include the Window and Dialog subsystems**
+
+When binding a struct with the `WailsInit` method, the Go runtime object is presented by the Application.
+
+For the frontend, the runtime is accessed through the `@wailsapp/runtime` module:
+
+```javascript
+import runtime from '@wailsapp/runtime';
+```
+
+
+### Events
+
+The Events subsystem provides a means of listening and emitting events across the application as a whole. This means that you can listen for events emitted in both Javascript and Go, and events that you emit will be received by listeners in both Go and Javascript.
+
+In the Go runtime, it is accessible via `runtime.Events` and provides 2 methods: `Emit` and `On`.
+
+#### Emit
+
+> Emit(eventName string, optionalData ...interface{})
+
+The `Emit` method is used to emit named events across the application.
+
+The first parameter is the name of the event to emit. The second parameter is an optional list of interface{} types, meaning you can pass arbitrary data along with the event.
+
+Example 1:
+
+```go
+func (m *MyStruct) WailsInit(runtime *wails.Runtime) error {
+ runtime.Events.Emit("initialised")
+}
+```
+
+Example 2:
+
+```go
+func (m *MyStruct) WailsInit(runtime *wails.Runtime) error {
+ t := time.Now()
+ message := fmt.Sprintf("I was initialised at %s", t.String())
+ runtime.Events.Emit("initialised", message)
+}
+```
+
+#### On
+
+> On(eventName string, callback func(optionalData ...interface{}))
+
+The `On` method is used to listen for events emitted across the application.
+
+The first parameter is the name of the event to listen for. The second parameter is a function to call when the event is emitted. This function has an optional parameter which will contain any data that was sent with the event. To listen to the 2 events emitted in the [emit](####emit) examples:
+
+Example with no data:
+
+```go
+func (m *MyStruct) WailsInit(runtime *wails.Runtime) error {
+ runtime.Events.On("initialised", func(...interface{}) {
+ fmt.Println("I received the 'initialised' event!")
+ })
+ return nil
+}
+```
+
+Example with data:
+
+```go
+func (m *MyStruct) WailsInit(runtime *wails.Runtime) error {
+ runtime.Events.On("hello", func(data ...interface{}) {
+ // You should probably do better error checking
+ fmt.Printf("I received the 'initialised' event with the message '%s'!\n", data[0])
+ })
+ return nil
+}
+```
+
+### Log
+
+The Log subsystem allows you to log messages at various log levels to the application log.
+
+#### New
+
+> New(prefix string)
+
+Creates a new custom Logger with the given prefix.
+
+
+```go
+type MyStruct struct {
+ log *wails.CustomLogger
+}
+
+func (m *MyStruct) WailsInit(runtime *wails.Runtime) error {
+ m.log = runtime.Log.New("MyStruct")
+ return nil
+}
+```
+
+Once created, you may use any of the logger's methods:
+
+##### Standard logging
+
+Each of these methods take a string (like fmt.Println):
+
+ - Debug
+ - Info
+ - Warn
+ - Error
+ - Fatal
+
+```go
+ m.Log.Info("This is fine")
+```
+
+##### Formatted logging
+
+Each of these methods take a string and optional data (like fmt.Printf):
+
+ - Debugf
+ - Infof
+ - Warnf
+ - Errorf
+ - Fatalf
+
+```go
+ feeling := "okay"
+ m.Log.Info("I'm %s with the events that are currently unfolding", feeling)
+```
+
+##### Field logging
+
+Each of these methods take a string and a set of fields:
+
+ - DebugFields
+ - InfoFields
+ - WarnFields
+ - ErrorFields
+ - FatalFields
+
+
+```go
+ m.Log.InfoFields("That's okay", wails.Fields{
+ "things are going to be": "okay",
+ })
+```
+
+### Dialog
+
+The Dialog subsystem allows you to activate the Webview's native dialogs. It is accessible via `runtime.Dialog` and has the following methods:
+
+**NOTE: Opening a Dialog will halt Javascript execution, just like a browser**
+
+#### SelectFile
+
+> SelectFile()
+
+Prompts the user to select a file for opening. Returns the path to the file.
+
+```go
+ selectedFile := runtime.Dialog.SelectFile()
+```
+
+#### SelectDirectory
+
+> SelectDirectory()
+
+Prompts the user to select a directory. Returns the path to the directory.
+
+```go
+ selectedDirectory := runtime.Dialog.SelectDirectory()
+```
+
+#### SelectSaveFile
+
+> SelectSaveFile()
+
+Prompts the user to select a file for saving. Returns the path to the file.
+
+```go
+ selectedFile := runtime.Dialog.SelectSaveFile()
+```
+
+### Window
+
+The Window subsystem provides methods to interact with the application's main window.
+
+#### SetColour
+
+> SetColour(colour string) error
+
+Sets the background colour of the window to the colour given to it (string). The colour may be specified in the following formats:
+
+ - RGB "rgb(0, 0, 0)"
+ - RGBA "rgba(0, 0, 0, 0.8)"
+ - HEX "#fff"
+
+```go
+ runtime.Window.SetColour("#eee")
+```
+
+#### Fullscreen
+
+> Fullscreen()
+
+Attempts to make the application window fullscreen. Will fail if the application was started with the option "Resize: false".
+
+```go
+ runtime.Window.Fullscreen()
+```
+
+#### UnFullscreen
+
+> UnFullscreen()
+
+Attempts to revert the window back to its size prior to a Fullscreen call. Will fail if the application was started with the option "Resize: false"
+
+```go
+ UnFullscreen()
+```
+
+#### SetTitle
+
+> SetTitle(title string)
+
+Sets the title in the application title bar.
+
+```go
+ runtime.Window.SetTitle("We'll need a bigger boat")
+```
+
+#### Close
+
+Closes the main window and thus terminates the application. Use with care!
+
+```go
+ runtime.Window.Close()
+```
+
+### Browser
+
+The browser subsystem provides methods to interact with the system browser.
+
+#### OpenURL
+
+> OpenURL(url string)
+
+Opens the given URL in the system browser.
+
+```go
+runtime.Browser.OpenURL("https://wails.app")
+```
+### A Common Pattern
+
+A common pattern for the Runtime is to simply save it as part of the struct and use it when needed:
+
+```go
+type MyStruct struct {
+ Runtime *wails.Runtime
+}
+
+func (m *MyStruct) WailsInit(r *wails.Runtime) error {
+ m.Runtime = r
+}
+```
+
+---
+
+[1]: https://github.com/zserge/webview
+
+## Cli
+
+Wails comes with a CLI tool that allows you to generate, build and bundle your projects. It deals with the complexity of juggling Go and Javascript environments.
+
+It has a number of commands:
+
+### Help
+
+> wails --help
+
+This will output the cli help message with all the available commands and flags.
+
+### Setup
+
+> wails setup
+
+The setup command does a number of things - it asks you for your name and email so that it can fill in project templates with your details. It also checks to see if your environment has the dependencies it needs and if not, try to suggest ways on how to install those dependencies.
+
+Setup is also the default command so it can be invoked by simply running `wails`.
+
+
+
+
+
+### Init
+
+> wails init
+
+The init command builds out a new project based on a template of your choice. We curently support a basic Vue, Vuetify and React templates. The project will be built automatically after initialisation.
+
+#### Basic Vue
+
+This template consists of a frontend composed of Vue components, bundled together using Webpack. It makes a simple call to the backend.
+
+#### Vuetify
+
+This template consists of a frontend composed of Vuetify components, bundled together using Webpack. It makes a simple call to the backend.
+
+#### React
+
+This template consists of a frontend composed of React components, bundled together using Webpack. It makes a simple call to the backend.
+
+
+
+
+
+### Serve
+
+> wails serve
+
+When you run `wails serve`, it will compile up the backend and run it in headless mode. This allows you to develop the frontend using your standard tooling. When you run your app, it will connect to the backend at startup and make all your backend functions available to you.
+
+We will cover this more in the tutorial.
+
+
+
+
+
+### Build
+
+> wails build
+
+The build command is the Wails equivalent of `go build`, however it does a number of things:
+
+- Installs frontend dependencies if needed
+- Performs a build of the frontend
+- Packs the frontend using Webpack
+- It downloads any Go dependencies that are required
+- It finally compiles and bundles everything into a single binary
+
+
+
+
+
+### Build Flags
+
+Here is a list of all available flags:
+
+| Flag | Description |
+| ---- | -------------------------------------------- |
+| -f | Force rebuild of frontend dependencies |
+| -d | Build application in Debug mode |
+| -p | Package application after a successful build |
+
+The `-p` flag is currently supports OSX and Windows. On OSX, it bundles your binary into a .app file with the default icon. On Windows, it will generate the application resource files and compile it all into a '.exe'. When the `-p` flag is used, the packaging files are left available for editing. Any changes will be picked up by the next build (eg icon).
+
+### Update
+
+> wails update
+
+This command does a check to see if the current version is the latest. If not, it will download and install the latest version. It is possible to also use it to install 'prerelease' versions by using the `-pre` flag. If a specific version is required, then it supports a `-version` flag.
+
+Example: `wails update -pre` will update the latest prerelease version
+
+### Issue
+
+> wails issue
+
+This command speeds up the process for submitting an issue to the Wails project. When you run the command, you will be asked to answer a couple of questions:
+
+
+
+
+
+Wails then determines some environmental details such as it's own version, opens a browser and fills in the default issue template.
+
+
+
+
+
+Please note: you can edit the template as you feel fit before submitting.
+
+You now have a good basis for your template. Running `wails init` will now give you your template as an option to install. When the project is generated using the template, it will create directories, copy non-template files then copy template files. Template files end in .template and will be treated as standard Go templates in which embedded codes are substituted with values in the [Project Options](https://github.com/wailsapp/wails/blob/master/cmd/project.go#L139-L154).
diff --git a/zh/releases/README.md b/zh/releases/README.md
new file mode 100644
index 0000000..a7c0e51
--- /dev/null
+++ b/zh/releases/README.md
@@ -0,0 +1,14 @@
+---
+sidebar: on
+sidebarDepth: 1
+---
+
+# Releases
+
+| Version | Release Date | Summary | Release Notes |
+| -------- | -------------- | --------------------------------------- | ------------- |
+| v0.17.0 | 1st July 2019 | New Angular template, Debian support, tooling updates | [here](./v0.17.0.md) |
+| v0.18.2 | 8th Oct 2019 | Refactored runtime, New Event functionality, Windows fixes, more! | [here](./v0.18.2.md) |
+| v0.18.5 | 23rd Oct 2019 | Better shutdown behaviour | [here](./v0.18.5.md) |
+| v0.19.0 | 5th Nov 2019 | Better Windows support, Migrate command & misc bugfixes | [here](./v0.19.0.md) |
+| v0.20.0 | 5th Dec 2019 | Better Windows support: Angular, Console & HiDPI bugfixes | [here](./v0.20.0.mds) |
diff --git a/zh/releases/v0.17.0.md b/zh/releases/v0.17.0.md
new file mode 100644
index 0000000..6f6c738
--- /dev/null
+++ b/zh/releases/v0.17.0.md
@@ -0,0 +1,40 @@
+---
+sidebar: on
+sidebarDepth: 1
+---
+
+# Wails v0.17.0
+
+This version brings with it a new template, a new supported Linux distribution and a helpful utility. Unfortunately, Windows support is again proving problematic. If there is anyone out there who would like to help with Windows support, please get in touch! We are looking to fix the Windows issues and do a point release.
+
+A *huge* thank you to everyone that was involved in this release!
+
+## Angular 8 Template (Mac & Linux)
+
+
+
+
+
+After his awesome React template last release, [admin_3.exe](https://github.com/bh90210) has come up with the goods again and delivered an Angular 8 template for the Wails community! Thanks admin_3.exe! 👏🎉
+
+**REQUIRES NODE 10.8+**
+
+## Debian Support
+
+
+
+
+
+We continue to expand our supported platform list with the introduction of Debian support. Versions 9 and 10 tested so far. Big shout out to [iceleo-com](https://github.com/iceleo-com) for the original request and testing!
+
+## Distribution Support Request Wizard!
+
+
+
+
+
+We have added a feature whereby when you try and run `wails init` on an unsupported Linux distribution, it will now prompt you to submit a support request. This will automatically generate a github request with the distribution name, version and discovery method. There is literally nothing more you have to do than click the 'submit' button in the browser window that opens for you. This really helps us to get your distribution supported.
+
+## Documentation Site Revamp
+
+The tireless [admin_3.exe](https://github.com/bh90210) has revamped the documentation site. We hope the improved structure and layout will help those who are new to the project get a better head start.
\ No newline at end of file
diff --git a/zh/releases/v0.18.2.md b/zh/releases/v0.18.2.md
new file mode 100644
index 0000000..451ff2a
--- /dev/null
+++ b/zh/releases/v0.18.2.md
@@ -0,0 +1,89 @@
+---
+sidebar: on
+sidebarDepth: 1
+---
+
+# Wails v0.18.2
+
+This version brings new functionality to the Event subsystem.
+
+A *huge* thank you to everyone that was involved in this release!
+
+## Even more Linux!
+
+We've been busy on the Linux front and now have support for:
+ * Linux Mint
+ * KDE Neon
+ * Elementary
+ * Kali Linux
+ * Parrot
+ * Zorin
+ * Void Linux
+ * Manjaro Linux (re-enabled)
+
+Plus better support for existing distributions:
+ * Fedora, CentOS, Debian, Arch and Gentoo
+
+## Refactored Runtime
+
+The Javascript part of the runtime has been refactored to bring in some improvements. It has been assembled as a standard node module that may now be imported by frontend code to get access to the runtime methods.
+
+```javascript
+
+import Wails from '@wailsapp/runtime';
+
+Wails.Events.On('myevent', () => {
+ // etc
+})
+
+```
+
+This is going to allow us to improve the runtime with minimal impact on application code.
+
+### Method deprecation (renaming)
+The original, lower cased methods for the JS runtime have been deprecated. The driver for this has been to unify the Go and JS runtime signatures. The new methods are the same as the old ones, just capitalised.
+
+## Event Updates
+
+This release sees a huge refactor of the runtime to improve maintainability and testability. With it come a number of new methods which are designed to simplify coordination between the frontend and backend. For the time being, these are only available in the JS runtime.
+
+### OnMultiple
+
+> Events.OnMultiple(eventName, callback, maxCallbacks)
+
+Registers a listener that will call `callback` a maximum of `maxCallbacks` times. After this time, the listener will be removed.
+
+### Once
+
+> Events.Once(eventName, callback)
+
+Registers a listener that will be destroyed after being notified once. Shorthand for `Events.OnMultiple(eventName, callback, 1)`.
+
+### Heartbeat
+
+> Events.Heartbeat(eventName, timeInMilliseconds, callback)
+
+Heartbeat will emit `eventName` every `timeInMilliseconds` until it is acknowledged by `Event.Acknowledged`. When that happens, `callback` is invoked.
+
+### Acknowledge
+
+> Events.Acknowledge(eventName)
+
+Acknowledge will acknowledge the heartbeat event `eventName`.
+
+## Binding updates
+
+It's now possible to bind Go methods with zero return types correctly. This will return an `undefined` in the promise callback.
+
+## Misc Updates
+
+ * `wails issue` now includes npm, gcc and node versions. Thanks [Byron](https://github.com/bh90210)!
+ * `wails issue` now also relies on `/etc/os-release` in Linux for system information, replacing the requirement for `lsb`. Thanks [Nikolai Zimmermann](https://github.com/Chronophylos)!
+ * React template now working on Windows
+ * Angular template still **NOT** working for Windows
+
+---
+
+We will be spending the next month improving documentation and bug fixing. Please let us know what bugs you find in v0.18.2 and we will look at them with the highest priority!
+
+We hope you enjoy the final v0.* release! See you at v1.0.0!
\ No newline at end of file
diff --git a/zh/releases/v0.18.5.md b/zh/releases/v0.18.5.md
new file mode 100644
index 0000000..a897678
--- /dev/null
+++ b/zh/releases/v0.18.5.md
@@ -0,0 +1,46 @@
+---
+sidebar: on
+sidebarDepth: 1
+---
+
+# Wails v0.18.5
+
+This version brings better handling of shutdown behaviour.
+
+A *huge* thank you to everyone that was involved in this release!
+
+## Wails Shutdown
+
+It is now possible to create methods on structs that will get called during application shutdown. Any struct methods with the signature `WailsShutdown()` will be called during shutdown.
+
+Example:
+```go
+ type MyStruct struct {
+ runtime *wails.Runtime
+ }
+
+ // This method will be called during application startup
+ func (s *MyStruct) WailsInit(runtime *wails.Runtime) error {
+ // Save runtime
+ s.runtime = runtime
+
+ // Allocate some resources...
+
+ return nil
+ }
+
+ // This method will be called during application startup
+ func (s *MyStruct) WailsShutdown() {
+
+ // De-Allocate some resources...
+
+ return nil
+ }
+
+```
+
+---
+
+We will be spending the next month improving documentation and bug fixing. Please let us know what bugs you find and we will look at them with the highest priority!
+
+See you at v1.0.0!
\ No newline at end of file
diff --git a/zh/releases/v0.19.0.md b/zh/releases/v0.19.0.md
new file mode 100644
index 0000000..7bb48b9
--- /dev/null
+++ b/zh/releases/v0.19.0.md
@@ -0,0 +1,40 @@
+---
+sidebar: on
+sidebarDepth: 1
+---
+
+# Wails v0.19.0
+
+This version brings better Windows support as well as a number of bugfixes.
+
+A *huge* thank you to everyone that was involved in this release!
+
+## Features
+
+### Migrate command
+
+There is a new `wails migrate` command that will attempt to auto-migrate your project to be API compatible with the latest version. This is an *experimental* feature, but has worked well for a number of projects.
+
+### Binary Name
+
+The generated binary name is now adjusted on build to be more consistent with the host platform. Projects created on linux but compiled on windows will output `.exe` instead of ``.
+
+## Bug Fixes
+
+### Spaces in gcc path
+
+Users who use a gcc distribution that lives on a path with spaces, used to get errors during the build process. This is now fixed.
+
+### Allow IE11 for serve
+
+IE11 was not previously supported for the `wails serve` command.
+
+### NPM/Node Versions
+
+The `wails issue` command would report the wrong Node/NPM version.
+
+---
+
+We will be spending the next month improving documentation and bug fixing. Please let us know what bugs you find and we will look at them with the highest priority!
+
+See you at v1.0.0!
\ No newline at end of file
diff --git a/zh/releases/v0.20.0.md b/zh/releases/v0.20.0.md
new file mode 100644
index 0000000..8d82b13
--- /dev/null
+++ b/zh/releases/v0.20.0.md
@@ -0,0 +1,32 @@
+---
+sidebar: on
+sidebarDepth: 1
+---
+
+# Wails v0.20.0
+
+This version brings a number of bugfixes for better Windows support.
+
+A *huge* thank you to everyone that was involved in this release!
+
+## Bug Fixes
+
+### Angular support for Windows
+
+The errors with building the Angular template on Windows have been fixed. This is now fully supported.
+
+### Alpha release of Console for Windows
+
+When compiling with `wails build -d` on Windows, a rudimentary console is available by pressing `SHIFT-ESC`. It allows you to see console logs/errors and has basic input capabilities.
+
+If anyone knows how to get firebug lite working in this scenario, please get in touch!
+
+### HiDPI Fix
+
+Windows builds previously did not work correctly with HiDPI screens. This is now fixed.
+
+---
+
+Please let us know what bugs you find and we will look at them with the highest priority!
+
+See you at v1.0.0!
\ No newline at end of file
diff --git a/zh/tutorials/README.md b/zh/tutorials/README.md
new file mode 100644
index 0000000..70c2567
--- /dev/null
+++ b/zh/tutorials/README.md
@@ -0,0 +1,48 @@
+---
+sidebar: on
+sidebarDepth: 1
+---
+# Tutorials
+
+## Template Overview
+
+In this tutorial we go through the default Vue template to gain an understanding of how Wails works. It is a simple introduction to some of the core concepts of the framework.
+
+[Start the tutorial](./template.md)
+
+## Quote Generator
+
+Building on the default template, we create a Quotes Generator. This introduces the concepts of binding a struct to your application as well as interacting with the Wails runtime in Go.
+
+This is recommended for those who are proficient in Javascript and Go and are looking to quickly create an app. It builds on the Template Tutorial so it is recommended to complete that first.
+
+[Start the tutorial](./quotes.md)
+
+## Todo
+
+What's a framework without a Todo app? We revisit the classic app, basing it on the [Todo MVC](http://todomvc.com/examples/vue/) version.
+
+This is a comprehensive and advanced tutorial that only requires a basic knowledge of Javascript and Go. It covers all aspects of Wails and is recommended for people of all skill levels.
+
+[Start the tutorial](./todo.md)
+
+## CPU Usage App
+
+In Package Main Episode 16, [Alex Pliutau](https://twitter.com/pliutau) builds a CPU Usage app with Wails. He covers struct binding, the events system and how to use 3rd party Javascript packages in your app. It's a really great tutorial!
+
+
+
+
+
+
+
+
+The tutorial is also available in text format at Package Main's [episode 16 github repo](https://github.com/plutov/packagemain/tree/master/16-wails-desktop-app) or as a [Medium article](https://medium.com/js-dojo/building-a-desktop-app-in-go-using-wails-b7f5825f986a).
+
+::: tip
+This tutorial uses an older version of the Runtime which has had some slight changes since v0.18.0. Please read the [Reference Guide](../reference/#wails-runtime) for more information.
+:::
diff --git a/zh/tutorials/quotes.md b/zh/tutorials/quotes.md
new file mode 100644
index 0000000..e72a1c5
--- /dev/null
+++ b/zh/tutorials/quotes.md
@@ -0,0 +1,481 @@
+# Quotes Generator
+
+In this tutorial, we will be building on the default Vue/Webpack template to create a quotes generator. We shall cover how we can group methods into a struct in Go and how to use the Go runtime object.
+
+It is assumed you have completed and understood the [template tutorial](./template.md). We will continue from where we left off.
+
+## Creating the Quotes Struct
+
+Wails allows you to bind structs to your application. Methods that are exposed, ie those with their first letters capitalised, will be bound to the application.
+
+We will start by creating a new file: quotes.go.
+
+```go
+package main
+
+// Quotes is our bound Quote Struct
+type Quotes struct {
+}
+
+// NewQuotes creates a new Quotes Struct
+func NewQuotes() *Quotes {
+ return &Quotes{}
+}
+
+// GetQuote returns a quote
+func (q *Quotes) GetQuote() string {
+ return "s/be/in - Mat Ryer"
+}
+```
+
+Our Quotes struct has one method `GetQuote`. When this is bound to the application, Wails will bind it to the frontend as `backend.Quotes`. Because `GetQuote` is an exposed method, it will be bound in the frontend as `backend.Quotes.GetQuote`.
+
+The NewQuotes method is simply a convention for creating a new instance of a struct. Wails requires that structs are instantiated before binding, so we will use this function in the main app.
+
+## Binding it to the application
+
+Now that we have our initial struct, let's bind it to the app:
+
+```go
+package main
+
+import (
+ "github.com/leaanthony/mewn"
+ "github.com/wailsapp/wails"
+)
+
+func basic() string {
+ return "Hello World!"
+}
+
+func main() {
+
+ js := mewn.String("./frontend/dist/app.js")
+ css := mewn.String("./frontend/dist/app.css")
+
+ app := wails.CreateApp(&wails.AppConfig{
+ Width: 1024,
+ Height: 768,
+ Title: "Quotes",
+ JS: js,
+ CSS: css,
+ Colour: "#131313",
+ })
+ app.Bind(basic)
+ app.Bind(NewQuotes()) // Add this
+ app.Run()
+}
+```
+
+Binding our quotes struct is a single line change to our existing main function. We can test this in the front end by serving the project again:
+
+ * Run `wails serve` in the project directory
+ * When it is ready, run `npm run serve` in the `frontend` directory
+
+ When it is ready, open the browser to the project url and you should see the same familiar screen as in the template tutorial:
+
+
+
+
+
+If you open up the browser's inspector window and select console, you should be able to access the bound Quotes struct:
+
+
+
+
+
+Note that the `GetQuote()` method is available to us. This, just like bound functions, returns a Javascript promise. We can run the method and print the output like so:
+
+
+
+
+
+
+Now that we have access to our Quotes struct, let's add more quotes.
+
+### Better Quotes
+
+A quote traditionally has a text and an author. Currently we are returning a single string from our `GetQuote` method, but it would be better to return a struct. Let's define it:
+
+```go
+// Quote holds a single quote and the author who said it
+type Quote struct {
+ Text string `json:"text"`
+ Author string `json:"author"`
+}
+```
+
+We can now update our `GetQuote` method to return a Quote:
+
+```go
+// GetQuote returns a quote
+func (q *Quotes) GetQuote() *Quote {
+ return &Quote{Text: "s/be/in", Author: "Mat Ryer"}
+}
+```
+
+If the previous `wails serve` is still running, press `ctrl-c` to stop it and re-run the serve command. The frontend does not need recompiling and will automatically reconnect to the backend when it becomes available. During that connection time, you will see a screen like this letting you know it is trying to reconnect to the backend:
+
+
+
+
+
+Once reconnected, open the console again and re-issue the `GetQuote` command. You should see something like the following:
+
+
+
+
+
+Now that we have the data in a format we can manipulate, we can update our frontend code to use it.
+
+### Rendering the Quotes
+
+Let's create a new component for rendering the quotes. It will be based on our HelloWorld component, so make a copy of this file and name it `Quote.vue`.
+
+The first thing we will do is register our new Quote component in `App.vue`:
+
+```javascript
+
+
+
+
+
+
+
+
+```
+
+Then we will update the component to get and store our quote:
+
+```javascript
+
+```
+
+We are simply changing the name of the data element from `message` to `quote`, and changing the name of the function we are calling to retrieve the quote. We store it in the quote variable defined in `data()`.
+
+Recap: The quote struct looks like this:
+```json
+{
+ text: 'the quote text',
+ author: 'the quote author'
+}
+```
+
+Next, we'll update the template to use the quote object:
+
+```javascript
+
+
+
+```
+
+Let's look at Line 3. We are using a blockquote element to encapsulate the quote. Within this element, we are using the `v-if` Vue directive. This will conditionally render the element based on the condition given to it. In our case, this is `quote != nil`. We will define `quote` in the component. This will be what stores our quote struct from the backend. The next directive we use is `:cite`. This simply sets an attribute on the blockquote element. In our case, we are setting it to `quote.author`. This references the author field of the quote struct we are getting from the backend. Within the element tags, we are using a template directive. This will output text based on the data within the double braces. In our case this will be `quote.text`.
+
+In Line 4, we simply update the name of the component's method to call when the button is clicked.
+
+If we serve the project now, we can see something like this:
+
+
+
+
+
+If we press the button, we can see the quote!
+
+
+
+
+
+The styling is terrible. Let's fix that!
+
+### Styling the component
+
+We already have some styling in the `
+```
+
+When we reload the app now, we should see something like this:
+
+
+
+
+
+Pressing the button should yield the following:
+
+
+
+
+
+### Adding more quotes
+
+Whilst Mat's quote is iconic (and potentially career ending), we are going to add in a few more quotes. What we want is to retrieve a random quote, every time the button is pressed.
+
+To do this, we are going to add a Quote slice to our Quotes struct in our quotes.go file:
+
+```go
+// Quotes is our bound Quote Struct
+type Quotes struct {
+ quotes []*Quote
+}
+```
+
+We will also create a small function to create and store our quotes:
+
+```go
+// AddQuote creates a Quote object with the given inputs and
+// adds it to the Quotes collection
+func (q *Quotes) AddQuote(text, author string) {
+ q.quotes = append(q.quotes, &Quote{Text: text, Author: author})
+}
+```
+
+Now that we have our helper functions in place, let's populate the quotes. We can do this in the NewQuotes function:
+
+```go
+// NewQuotes creates a new Quotes Struct
+func NewQuotes() *Quotes {
+ result := &Quotes{}
+ result.AddQuote("Age is an issue of mind over matter. If you don't mind, it doesn't matter", "Mark Twain")
+ result.AddQuote("Anyone who stops learning is old, whether at twenty or eighty. Anyone who keeps learning stays young. The greatest thing in life is to keep your mind young", "Henry Ford")
+ result.AddQuote("Wrinkles should merely indicate where smiles have been", "Mark Twain")
+ result.AddQuote("True terror is to wake up one morning and discover that your high school class is running the country", "Kurt Vonnegut")
+ result.AddQuote("A diplomat is a man who always remembers a woman's birthday but never remembers her age", "Robert Frost")
+ result.AddQuote("As I grow older, I pay less attention to what men say. I just watch what they do", "Andrew Carnegie")
+ result.AddQuote("How incessant and great are the ills with which a prolonged old age is replete", "C. S. Lewis")
+ result.AddQuote("Old age, believe me, is a good and pleasant thing. It is true you are gently shouldered off the stage, but then you are given such a comfortable front stall as spectator", "Confucius")
+ result.AddQuote("Old age has deformities enough of its own. It should never add to them the deformity of vice", "Eleanor Roosevelt")
+ result.AddQuote("Nobody grows old merely by living a number of years. We grow old by deserting our ideals. Years may wrinkle the skin, but to give up enthusiasm wrinkles the soul", "Samuel Ullman")
+ result.AddQuote("An archaeologist is the best husband a woman can have. The older she gets the more interested he is in her", "Agatha Christie")
+ result.AddQuote("All diseases run into one, old age", "Ralph Waldo Emerson")
+ result.AddQuote("Bashfulness is an ornament to youth, but a reproach to old age", "Aristotle")
+ result.AddQuote("Like everyone else who makes the mistake of getting older, I begin each day with coffee and obituaries", "Bill Cosby")
+ result.AddQuote("Age appears to be best in four things old wood best to burn, old wine to drink, old friends to trust, and old authors to read", "Francis Bacon")
+ result.AddQuote("None are so old as those who have outlived enthusiasm", "Henry David Thoreau")
+ result.AddQuote("Every man over forty is a scoundrel", "George Bernard Shaw")
+ result.AddQuote("Forty is the old age of youth fifty the youth of old age", "Victor Hugo")
+ result.AddQuote("You can't help getting older, but you don't have to get old", "George Burns")
+ result.AddQuote("Alas, after a certain age every man is responsible for his face", "Albert Camus")
+ result.AddQuote("Youth is when you're allowed to stay up late on New Year's Eve. Middle age is when you're forced to", "Bill Vaughan")
+ result.AddQuote("Old age is like everything else. To make a success of it, you've got to start young", "Theodore Roosevelt")
+ result.AddQuote("A comfortable old age is the reward of a well-spent youth. Instead of its bringing sad and melancholy prospects of decay, it would give us hopes of eternal youth in a better world", "Maurice Chevalier")
+ result.AddQuote("A man growing old becomes a child again", "Sophocles")
+ result.AddQuote("I will never be an old man. To me, old age is always 15 years older than I am", "Francis Bacon")
+ result.AddQuote("Age considers youth ventures", "Rabindranath Tagore")
+ result.AddQuote("s/be/in", "Mat Ryer")
+ return result
+}
+```
+
+The only thing left to do now is to update our `GetQuote()` method to return a random quote:
+
+```go
+// GetQuote returns a quote
+func (q *Quotes) GetQuote() *Quote {
+ return q.quotes[rand.Intn(len(q.quotes))]
+}
+```
+
+You will also need to ensure that you import `math/rand`, if your IDE hasn't already:
+
+```go
+import "math/rand"
+```
+
+Run `wails serve` again to recompile and serve the app.
+
+Now when we press the button, we get a fabulous quote:
+
+
+
+
+
+Now with a slight tweak to the CSS, we can make this look even better. In the component CSS, let's add a margin to the author:
+
+```css
+blockquote:after {
+ content: "\2013 \2003" attr(cite);
+ display: block;
+ text-align: right;
+ font-size: 1.15em;
+ color: #53cdff;
+ margin: 1em;
+}
+```
+
+Now we have a great looking quotes app:
+
+
+
+
+
+## Building the app
+
+Now that we have the app working, we want to build it as a standalone app. We do this by running `wails build`. You should now have a `quotes` executable (or `quotes.exe` if on Windows).
+
+
+
+
+
+Running this should run the app. On MacOS, it looks like this:
+
+
+
+
+
+Pressing the button works as expected:
+
+
+
+
+
+## Packaging the app
+
+Wails provides the ability to package your application into a platform native format. Packaing is indicated by running `wails build -p`.
+
+### MacOS
+
+On MacOS, running `wails build -p` will generate a .app bundle. If we run this in the quotes project directory you should end up with a quotes.app bundle:
+
+
+
+
+
+If we open this in finder, you will see your application as a standard Mac app:
+
+
+
+
+
+Double clicking this will launch the app as expected. Minimising the app to the dock will show you that the icon works as expected:
+
+
+
+
+
+Of course, it's unlikely that you'll want to use the default icon, so Wails makes it easy for you to replace it. Just replace `appicon.png` with your own icon and rebuild.
+
+There's a cool icon [here](https://www.onlinewebfonts.com/icon/299634) which can be used. I made some changes so that it works better on my dark desktop, renamed the icon to `appicon.png` and rebuilt:
+
+
+
+
+
+### Windows
+
+Due to the nature of Windows, a standard build will also package the app with the default icon. If you run `wails build -p` it will leave the build artifacts, including the default icon, so that you can customise the build. Make your changes and run `wails build` again.
+
+### Linux
+
+Currently, packing on Linux isn't supported as it could mean many things. There is the potential to support snap packages in the future.
+
+## Exercises
+
+ * See if you can animate the quotes using something like [animate.css](https://daneden.github.io/animate.css/)
+ * See if you can pull quotes from the [They Said So](https://quotes.rest/) quotes API from the backend
+
+## Summary
+
+Hopefully you now understand how to build and package a basic application using Wails.
diff --git a/zh/tutorials/template.md b/zh/tutorials/template.md
new file mode 100644
index 0000000..e5931b0
--- /dev/null
+++ b/zh/tutorials/template.md
@@ -0,0 +1,217 @@
+# Default Vue Template
+
+In this tutorial, we will be looking at the default Vue template and gain an understanding of how Wails works. Whilst we do touch on Vue in this tutorial, it is assumed that the reader has a certain amount of experience with Vue. If not, we recommend the Vue tutorial [here](https://vuejs.org/v2/guide/).
+
+## Initialise Project
+
+Run `wails init` to generate your project. We will call our project 'Quotes'. Accept the defaults for the following 2 questions:
+
+```
+The name of the project (My Project): Quotes
+Project Name: Quotes
+The output binary name (quotes):
+Output binary Name: quotes
+Project directory name (quotes):
+Project Directory: quotes
+Project 'Quotes' generated in directory 'quotes'!
+To compile the project, run 'wails build' in the project directory.
+```
+
+## Serve Project
+
+As we will partially be developing this in the browser, we will serve the backend in bridged mode. This means that the Go functions will be available to us in the browser!
+
+Run `wails serve` in the project directory.
+
+You should have some output similar to this:
+
+```
+Quotes - Debug Build
+--------------------
+INFO[0000] [App] Starting
+INFO[0000] [Events] Starting
+INFO[0000] [IPC] Starting
+INFO[0000] [Events] Listening
+INFO[0000] [Bind] Starting
+INFO[0000] [Bind] Binding Go Functions/Methods
+INFO[0000] [Bind] Bound Function: main.basic()
+INFO[0000] [Headless] Headless mode started.
+INFO[0000] [Headless] The Wails bridge will connect automatically.
+>>>>> To connect, you will need to run 'npm run serve' in the 'frontend' directory <<<<<
+```
+
+Note: In the startup messages, we can see that the function main.basic() has been bound:
+
+```
+INFO[0000] [Bind] Bound Function: main.basic()
+```
+
+This means we can call it from the frontend, and in this instance, directly from the browser! ( The `main` part is simply the package name it was defined in. This is dropped when bound ).
+
+To start the frontend, do the following:
+
+```
+cd frontend
+npm run serve
+```
+
+This starts the standard Vue development server and will give you a URL that the application is being hosted on. It will be something like this:
+
+```
+ App running at:
+ - Local: http://localhost:8082/
+ - Network: http://localhost:8082/
+```
+
+Open a browser to that URL and you should see something similar to the following:
+
+
+
+
+
+If you click the button, you get a message:
+
+
+
+
+
+## Understanding the App
+
+*What just happened?*
+
+When you pressed the button, the frontend code called a backend function and printed the result to the page.
+
+*How does that work?*
+
+The sequence of operations are as follows:
+
+ * main.js runs and waits for the runtime to Initialise. As this is running in bridged mode, it waits until a connection to the backend is established
+ * Once established, it mounts the main app, defined in App.vue, just as you normally would in a Vue app
+ * App.vue uses a component called "HelloWorld", defined in the components directory and this defines a message and a button. When the button is pressed, a call is made to the backend basic() function and the result is placed in the message
+
+There is no more to the app than this. Let's look at those steps:
+
+### Waiting for the Backend Connection
+
+When running `wails serve`, the connection to the backend is managed by a bridge which is dynamically injected into the runtime library. This library does a lot of things, however we only need to be concerned with one function: Init(). The Init() function accepts a callback, which is invoked when the connection to the backend is established. This is demonstrated clearly in main.js:
+
+```javascript
+import Wails from '@wailsapp/runtime';
+
+Wails.Init(() => {
+ ...
+ ...
+ ...
+});
+```
+
+The bridge sets up the following things:
+
+ * The wails javascript runtime, available at through the `@wailsapp/runtime` module. This provides a number of useful systems that we will cover over the course of the tutorials. For now, we will not use it.
+ * The bindings to backend code. These are available at `window.backend`. If you have bound the Go function `Hello` to your application, it will be available at `window.backend.Hello`.
+
+When the bridge has initialised, it will call the given callback. At this point you know that both the bindings and the runtime is available.
+
+### Mount the main application
+
+When the callback from Init is invoked, we create and mount the main Vue App Component:
+
+```javascript
+Wails.Init(() => {
+ new Vue({
+ render: h => h(App)
+ }).$mount("#app");
+});
+```
+
+The App component is defined as a [single file component](https://vuejs.org/v2/guide/single-file-components.html) and is entirely defined in App.vue. Single file components define 3 things:
+
+ * HTML Template
+ * Javascript
+ * CSS
+
+The HTML Template part of App looks like this:
+```html
+
+
+
+
+
+
+```
+This defines a simple div containing the Wails logo and the HelloWorld component.
+
+The Javascript part is fairly simple:
+```javascript
+
+```
+
+The App component imports the HelloWorld component from HelloWorld.Vue. It also imports the main css file from the assets directory. It then exports its name and what components it uses.
+
+If we look at the HelloWorld.vue file we see the following HTML Template:
+
+```html
+
+
+
+```
+You can see that the container comprises of an h1 header and a link tag. The header has the text {{message}} between its tags. This is a binding in the component. If we set this.message in any component method, the h1 header is rendered with that value. The link tag has a `@click` directive which means that when it is clicked, it will call the function `getMessage`. This will all make more sense when we look at the javascript section:
+
+```javascript
+
+```
+Here we define 2 parts of the component:
+
+ - data defines the 'local' variables of the component
+ - methods defines the methods of the component :-)
+
+By default, message is a space. This means that a space will be rendered in the h1 tags at startup.
+
+In the methods section, we see there is a method defined called `getMessage`. This is the function that gets called when the link tag is clicked. When it is clicked, we call `window.backend.basic()`. This is a method that we bind in the main go file! It is defined as such in main.go:
+
+```go
+func basic() string {
+ return "Hello World!"
+}
+```
+
+All bound Go methods are presented in Javascript as functions that return Promises. Because of this, we call `.then()` which gives us the result and we assign it to the message variable. We use `self` as `this` references the callback function, rather that the component.
+
+## Exercises
+
+ * See if you can get it to print a different message
+ * Try changing the styling so that the message is a different colour or size
+ * See if you can work out how to send a name to the backend from Vue, and get it to return you `Hello !`
+
+## Summary
+
+Hopefully you now understand the basics of how Wails works and how the default template interacts with it.
diff --git a/zh/tutorials/todo.md b/zh/tutorials/todo.md
new file mode 100644
index 0000000..66d934c
--- /dev/null
+++ b/zh/tutorials/todo.md
@@ -0,0 +1,2649 @@
+# Todo
+
+In this tutorial, we will be creating a todo app based on the [Vue MVC Todo App](http://todomvc.com/examples/vue/). We will cover the following topics:
+
+ * Accessing the Wails runtime in both Javascript and Go
+ * Using native browser file dialogs
+ * Using the unified events system
+ * Binding both functions and structs to the application
+ * Basic usage of Vue
+
+The source code to the app is available [here](https://github.com/wailsapp/todo). I highly recommend not looking at it unless you really need to.
+
+## Setup
+
+Generate a new project using `wails init`. We will call the project 'todos' and accept the default answers:
+
+```bash
+The name of the project (My Project): todos
+Project Name: todos
+The output binary name (todos):
+Output binary Name: todos
+Project directory name (todos):
+Project Directory: todos
+Template: Vue2/Webpack Basic
+Project 'todos' built in directory 'todos'!
+```
+
+Next we will move into the `todos` directory. You should see the following files, including a todos binary:
+
+```bash
+.
+├── frontend
+├── go.mod
+├── go.sum
+├── main.go
+├── project.json
+└── todos
+```
+
+Now lets build our frontend!
+
+## Basic Frontend
+
+We are going to base our frontend on the cool [Todo MVC](http://todomvc.com/examples/vue/) app that the awesome [Evan You](evanyou.me) created. The main code repository can be located [here](https://github.com/tastejs/todomvc/tree/gh-pages/examples/vue).
+
+We will start by updating the template section of App.vue located in you project directory in frontend/src. If we look at [index.html](https://github.com/tastejs/todomvc/blob/gh-pages/examples/vue/index.html), we can see that the main html part of the application lies between lines 11 and 48:
+
+```html
+
+
+
todos
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+We'll base our template on this, using the bare minimum to begin with:
+
+
+```html
+
+
+
+
+
todos
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+We will also update our script section as we will not need the HelloWorld component:
+
+```javascript
+
+```
+
+So the whole App.vue should now look like this:
+
+```html
+
+
+
+
+
todos
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+The template has 2 sections: the header where the title is displayed (Lines 4-7) and the main todo list (Lines 8-16).
+
+So let's now start serving the front end so we can see what it looks like. As we haven't written our backend yet, let's temporarily comment out the Wails runtime parts of `frontend/src/main.js`:
+
+```javascript
+import Vue from "vue";
+import App from "./App.vue";
+
+Vue.config.productionTip = false;
+Vue.config.devtools = true;
+
+// import Wails from '@wailsapp/runtime';
+
+//Wails.Init((() => {
+ new Vue({
+ render: h => h(App)
+ }).$mount("#app");
+// });
+```
+
+Change into the 'frontend' directory and run `npm run serve`. If all goes well, you should see something like this on the console:
+
+```
+ DONE Compiled successfully in 187ms 8:20:54 AM
+
+
+ App running at:
+ - Local: http://localhost:8082/
+ - Network: http://localhost:8082/
+
+```
+
+Open a browser to this location and you should see something similar to this:
+
+
+
+
+
+It looks underwhelming but it's early days. For it to look like the original, we need to include the original stylesheets. The original template references 2:
+
+```html
+
+
+```
+
+Download the Base CSS from [here](https://raw.githubusercontent.com/tastejs/todomvc/gh-pages/examples/vue/node_modules/todomvc-common/base.css) and the App CSS from [here](https://raw.githubusercontent.com/tastejs/todomvc/gh-pages/examples/vue/node_modules/todomvc-app-css/index.css).
+
+Copy them to `frontend/src/assets/css` and rename `index.css` to `app.css`. Let's include those in the App.vue file. We can simply import them:
+
+```javascript {2-3}
+
+```
+
+As Vue is serving the frontend, these changes should now be reflected in the browser. You should see something like this:
+
+
+
+
+
+That looks much better! You'll notice that whilst the app is responding, it doesn't do much. Next we'll add some code into our component to manage the list.
+
+## Implementing the list
+
+We'll store our todo list in an array in the component:
+
+```javascript {6-10}
+
+```
+
+We'll also add a condition to the html that we should only show the todo items if we have any items. We do this using the [v-show](https://vuejs.org/v2/guide/conditional.html#v-show) directive:
+
+```html {1}
+
+
+
+
+
+
+
+
+
+```
+
+If we look at our app now, we will see our original to do item is now not there. This is because the todos array is empty so our list section of the template is now hidden.
+
+We will define our todo items in a Javascript object. To start with, this will simply be a unique number to identify the the item as well as its title:
+
+```javascript
+{
+ id: ,
+ title:
+}
+```
+
+Let's add a test entry into the main list:
+
+```javascript {3}
+ data() {
+ return {
+ todos: [{id: 0, title: "My test todo item"}]
+ }
+ }
+```
+
+In vue, we can iterate over a list using [v-for](https://vuejs.org/v2/guide/list.html#v-for-on-a-lt-template-gt) and we will use that to display our list items:
+
+```html {3-7}
+
+
+
+
+
+
+
+
+
+```
+
+After saving this, you should see the todo item in the list:
+
+
+
+
+
+You will notice that if you change the item title and save, it will update the title in the browser!
+
+Next let's add a checkbox to allow us to indicate that we have completed a task. To do this, we will need to update our todo item model with a variable to store this:
+
+```javascript
+{
+ id: ,
+ title: ,
+ completed:
+}
+```
+
+Let's add that to our test item and set it to true:
+
+```javascript {3}
+ data() {
+ return {
+ todos: [{id: 0, title: "My test todo item", completed: true}]
+ }
+ }
+```
+
+The todo-mvc stylesheet has a css class called "completed" that styles completed items. We will use the [class directive](https://vuejs.org/v2/guide/class-and-style.html) to add that styling based on our item's 'completed' property:
+
+```html {3}
+
+
+
+
+
+
+
+
+
+```
+
+You should now have a styled item like so:
+
+
+
+
+
+## Toggling items
+
+Let's now add a toggle for the todo item so we can set it as completed ourselves.
+
+We will add an html checkbox to the item and bind it to the item's 'completed' variable using [v-bind](https://vuejs.org/v2/guide/class-and-style.html). The 'toggle' class is part of the todo-mvc css and turns a boring checkbox into a nicely styled toggle:
+
+```html {5}
+
+
+
+
+
+
+
+
+
+
+```
+We will also default our test item's completed variable to false:
+
+```javascript
+ todos: [{id: 0, title: "My test item", completed: false}]
+```
+
+The app should now look like this:
+
+
+
+
+
+and when you select the item:
+
+
+
+
+
+## Using the Vue Dev Tools
+
+One of the motivators for Wails to fully support developing in the browser was so that developers could use the amazing array of developer extensions available. Vue has a brilliant extension called [Vue Dev Tools](https://github.com/vuejs/vue-devtools) which allows you to inspect your application from a Vue perspective. By default, Wails enables support for this extension.
+
+Once you install it, right click on the browser running your app and select "inspect". There should be a "Vue" tab available. Select that and it should show you the page layout from a component perspective. We only have one (App). If you click on it, you will see your component data on the right. We can see the todos array and if we open it up, we can see our test item:
+
+
+
+
+
+Not only can we see it, we can edit it! Click the pencil/pen icon next to the variable and you will be able to edit the value. Boolean values have a handy toggle icon.
+
+If we click our toggle button in the page, we can see the value in dev tools toggle. And the reverse is true also!
+
+Dev Tools is a great way to develop and debug your application. For more information, I highly recommend the awesome Flavio Copes [tutorial](https://flaviocopes.com/vue-devtools/) on it.
+
+## Adding Todo Items
+
+It's about time we had a way of adding todo items. We already have an input at the top of the list but it doesn't do anything. Let's add a binding between that and our component data. We will add a new data item called newTodo and it will be a string:
+
+```javascript {3}
+ data() {
+ return {
+ newTodo: "",
+ todos: [{id: 0, title: "My test item", completed: false}]
+ }
+ }
+```
+
+We then add the `v-bind` directive to link the input to the newTodo variable:
+
+```html{3}
+
+
todos
+
+
+```
+
+If you reload the page, you should now see the newTodo item in dev tools. If you type in the input at the top of the list you will see its value reflected in the data object.
+
+Now we need a way of saving this value into our todo list. To achieve this, we need to do 2 things:
+ * create a method on the component that adds the item to the list
+ * a mechanism to call this method when we press enter on the input
+
+The way to declare a [method](https://flaviocopes.com/vue-methods/) in a Vue component is to add it to a `methods` object in the component. For us, we will add a method called `addTodo`:
+
+```javascript {12-25}
+
+```
+
+This will check that we have a new todo item and trim off the spaces if we do. If we don't then have a title (only spaces were input), then we just return and do nothing. If we have a title, then we push a new todo item object into the todos list (component variables are accessible through `this`). We then reset the newTodo variable to a blank string.
+
+Now that we have a way of adding, let's add the trigger:
+
+```html {3}
+
+
todos
+
+
+```
+
+Vue has a handy directive called [v-on](https://vuejs.org/v2/api/#v-on) which allows us to listen for events. We can use it to listen for when the enter key is released by using `v-on:keyup.enter` or the equivalent shorthand `@keyup.enter`.
+
+Now if we enter text into the input field and press return, the item gets added to our list:
+
+
+
+
+
+## Removing Todo Items
+
+Now that we can add items and mark them as complete, we should also add the ability to remove items from the list. We will do this by using a simple button on each item:
+
+```html {7}
+
+
+
+
+
+
+
+
+
+
+
+```
+
+We are using the `v-on` shorthand again but this time instead of listening for a key event, we are listening for the click event emitted when the button is pressed. When the button is pressed, we call `removeTodo()` with our todo. Let's now add that method after `addTodo`:
+
+```javascript {14-21}
+ methods: {
+ addTodo: function() {
+ var value = this.newTodo && this.newTodo.trim();
+ if (!value) {
+ return;
+ }
+ this.todos.push({
+ id: this.todos.length,
+ title: value,
+ completed: false
+ });
+ this.newTodo = "";
+ },
+ removeTodo: function(todo) {
+ var index = this.todos.indexOf(todo);
+ this.todos.splice(index, 1);
+
+ for(var i=0; i
+
+
+
+## Editing a Todo Item
+
+Editing a todo item will be the most complicated thing we do with Vue in this tutorial. We need to do the following:
+
+ * Listen for a double click on an item
+ * Show a text input with the item's text
+ * Listen for enter signifying the edit is complete
+ * Set the title of the item to the new input
+ * Hide the input
+
+Let's start by adding a listener for a double click to an entry:
+
+```html {6}
+
+
+
+
+
+
+
+
+
+
+
+```
+When we double click on a todo item, it will now call a method called `editTodo`, passing in the todo we clicked on. We want to retain a reference to that item so let's add a data variable for it:
+
+```javascript {4}
+ data() {
+ return {
+ newTodo: "",
+ editedTodo: null,
+ todos: [{id: 0, title: "My test item", completed: false}]
+ }
+ },
+```
+
+Let's now define `editTodo` function:
+
+```javascript
+ editTodo: function(todo) {
+ this.editedTodo = todo;
+ },
+```
+
+Next, we need to add the input element to the template to show when editing. This needs to be bound to our todo's title. We also give it the 'edit' css class, as this is what is expected by the TodoMVC stylesheet:
+
+```html {9-13}
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Hiding and showing is part of the TodoMVC stylesheet - we simply need to set the class 'editing' when editing so we can add that too. We use a simple evaluation that when the todo item is the 'editedTodo', then we add the class 'editing':
+
+```html {3}
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+If we look at our app now, we notice a couple of things:
+
+ * When we double click, the text input does appear but it is not focused.
+ * When we press return, nothing happens.
+
+Let's address the first point. Vue uses directives in standard html elements to indicate how that element is processed. It also offers the ability to create [custom directives](https://vuejs.org/v2/guide/custom-directive.html) so you can tailor them to your needs. We will define a directive that will focus the element that uses it. Insert this between the 'data' and 'methods' fields in the component's javascript section.
+
+```javascript {4-10}
+ todos: [{id: 0, title: "My test item", completed: false}]
+ }
+ },
+ directives: {
+ "todo-focus": function(el, binding) {
+ if (binding.value) {
+ el.focus();
+ }
+ }
+ },
+ methods: {
+ addTodo: function() {
+```
+
+We can now use this in our input element:
+
+```html {5}
+
+```
+
+Now let's address the issue of completing the edit. We want to listen for when the return key is pressed.
+Vue allows us to do this using the `v-on` directive we used earlier. We will use [keyup](https://vuejs.org/v2/guide/events.html#Key-Modifiers) to listen for the release of the return key.
+
+```html {5}
+
+```
+
+Now, when the return (enter) key is released, the `doneEdit` method will be called with the todo. We will now define this method in the component:
+
+```javascript {5-14}
+ editTodo: function(todo) {
+ this.editedTodo = todo;
+ },
+
+ doneEdit: function(todo) {
+ if (!this.editedTodo) {
+ return;
+ }
+ this.editedTodo = null;
+ todo.title = todo.title.trim();
+ if (!todo.title) {
+ this.removeTodo(todo);
+ }
+ },
+ }
+```
+
+In this function, we are first ensuring we are actually editing an item by checking if the editedTodo variable is set. If it isn't, we just return and ignore it ever happened. If we are legit editing a todo, we set the editedTodo variable to null as we are now not editing. We then set the todo title to be the current value, but with any whitespace at the ends trimmed off. Finally, we check to see if we actually have any text in the title (because a blank string in javascript evaluates to false), we just call our `removeTodo` function.
+
+If you try the app now, you will see that the item edits correctly, however there's some slight oddities: if you click off the input, it doesn't end the editing. Also, if you try to create another todo, the input flicks over to the edit box. Let's address those issues by also listening to the [blur event](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event):
+
+```html {6}
+
+```
+
+That's better! Now it feels right. It would be good if we could also cancel our edits. We know how to listen to keypresses, so let's listen for escape and cancel the current editing session. To do this, we are first going to keep a copy of the item's text when we first start editing:
+
+```javascript {2}
+ editTodo: function(todo) {
+ this.beforeEditCache = todo.title;
+ this.editedTodo = todo;
+ },
+```
+
+We are using a variable called `beforeEditCache` to store the initial value. Now let's listen to the escape key:
+
+```html {7}
+
+```
+
+And finally, let's implement the `cancelEdit` method:
+
+```javascript
+ cancelEdit: function(todo) {
+ this.editedTodo = null;
+ todo.title = this.beforeEditCache;
+ }
+```
+
+Now let's try editing an item and hitting `escape`. It works!
+
+Finally, let's remove our original test item from the todo list:
+
+```javascript {5}
+ data() {
+ return {
+ newTodo: "",
+ editedTodo: null,
+ todos: []
+ }
+ },
+```
+
+Our final App.vue file should now look something like this:
+
+```html
+
+
+
+
+
todos
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Now that we have a basic todo app, let's build it into a desktop app by running `wails build` in the project's root directory. Once it has finished building, run the newly built `todos` binary. You should see something like this:
+
+
+
+
+
+The problem we now have is that every time we start the app, we lose our previous list. Let's look at using Go to persist our list.
+
+## Persistence using Go
+
+Currently, our app is completely standalone from the backend. Our main app is simply wrapping the Vue project and displaying it. What we want to do is load our list up from disk when we start our app. To do that, we must be able to bridge our frontend and backend code. We do this using the Wails Bridge.
+
+### Bridging Frontend and Backend
+
+To load our list we will want to call a function in Go which loads a file from a default location and if it doesn't exist, it creates it with an empty list.
+
+To allow the frontend to talk to the backend, Wails provides a bridge. If you run `wails serve` in your project directory, you will see something like this:
+
+
+
+
+
+This is now serving your backend Go functions and is waiting for the bridge to connect. We commented this out of `main.js` at the start, but now is the time to add it back in:
+
+```javascript {7,9,13}
+import Vue from "vue";
+import App from "./App.vue";
+
+Vue.config.productionTip = false;
+Vue.config.devtools = true;
+
+import Wails from "@wailsapp/runtime";
+
+Wails.Init(() => {
+ new Vue({
+ render: h => h(App)
+ }).$mount("#app");
+});
+```
+
+Ensure your frontend is running. If not, run `npm run serve` in the frontend directory to start it. Your app will load as normal, but you may have noticed something flash up quickly. This is message to indicate that there is no connection with the backend. Very quickly, it establishes the connection and disappears. You can see this if you press `ctrl-c` in the terminal running `wails serve`. If you now look at your app, you will see something like this:
+
+
+
+
+
+The bridge will now be attempting to reconnect to the backend. If we run `wails serve` again, the dialog will disappear. This means the reconnection has happened. If you are uncertain, check the developer console which will have some log output from the bridge:
+
+
+
+
+
+Now that we have established the connection, we can access the backend from the frontend. All bound functions are registered to the global `backend` object. The default template binds a simple function called `basic`. We can call this from the browser by calling `backend.basic()`. It is important to remember that every function on the `backend` object returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises). To quickly test this in the browser we can simply run `backend.basic().then(console.log)`. We get our response from our backend code:
+
+
+
+
+
+Awesome! We now have an easy means to call our Go code, so let's get cracking with our Save.
+
+## Saving the Todo List
+
+Our todo store in the frontend is just an array with a number of javascript objects in it. We can serialise this to JSON, which makes it an easy thing to load and save. We want to save whenever there are any changes to this list, whether it's adding, removing or editing. Vue allows us to [watch properties](https://vuejs.org/v2/guide/computed.html#Watchers) and call a function when it changes. We do this by adding the `watch` property on the component:
+
+```javascript {4-8}
+ todos: []
+ }
+ },
+ watch: {
+ todos: function(todos) {
+ console.log("Todo list: " + JSON.stringify(todos));
+ }
+ },
+ methods: {
+ addTodo: function() {
+```
+
+Now when we update the list, the list is printed to the console.
+
+Instead of just logging to the browser console, we will take our first step into using the Wails runtime from Javascript. It is accessible through the [@wailsapp/runtime](https://www.npmjs.com/package/@wailsapp/runtime) package and has some features that help with building your application:
+
+ * Logging
+ * Events
+ * Browser
+
+ To use the Runtime, we need to import it:
+
+```javascript{5}
+
+```
+
+Stop and restart the backend running `wails serve`. You will notice that the loadList function has now been bound:
+
+```bash {3}
+INFO[0000] [Bind] Binding Go Functions/Methods
+INFO[0000] [Bind] Bound Function: main.saveList()
+INFO[0000] [Bind] Bound Function: main.loadList()
+INFO[0000] [Headless] Headless mode started.
+INFO[0000] [Headless] The Wails bridge will connect automatically.
+```
+
+When you reload the page, you will notice the following in your terminal output:
+
+```bash
+INFO[0337] I got this list: Load the list!
+```
+Great! Now let's update our loadList function to load the list file and send that back:
+
+```go
+func loadList() (string, error) {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return "", err
+ }
+ filename := path.Join(cwd, "mylist.json")
+ result, err := ioutil.ReadFile(filename)
+ return string(result), err
+}
+```
+This is similar to our `saveList` function, but instead reads the file. Our `ioutil.ReadFile` function returns a byte array so the final line converts this into a string before returning it. Any errors are also returned (We will look at error handling) shortly.
+
+Reserve the backend again and you will notice the following output:
+
+```bash
+INFO[0014] I got this list: []
+```
+
+This is what is in our file (even if it is a blank list)! Last thing for us to do is to convert our loaded json into the todo list. We'll do the following updates to our `mounted` function:
+
+```javascript
+ mounted() {
+ window.backend.loadList().then((list) => {
+ Wails.Log.Info("I got this list: " + list)
+ this.todos = JSON.parse(list);
+ });
+ }
+```
+
+Now every time we reload the page, the todo list is loaded! We can also see the list data that was sent from the backend to the frontend on the console.
+
+If your editor/IDE has automatic file reload, then open the `mylist.json` file and watch how it gets updated when you add or remove items. It's saved in a compact form which is sometimes hard to read so lets format the data saved slightly. We used `JSON.stringify` to turn the todo list into a string and it has an option to "pretty print", so let's change that in `App.vue`:
+
+```javascript {3}
+ watch: {
+ todos: function(todos) {
+ window.backend.saveList(JSON.stringify(todos, null, 2));
+ }
+ },
+```
+Now our `mylist.json` file will look something like this:
+
+```json
+[
+ {
+ "id": 0,
+ "title": "I am a todo",
+ "completed": false
+ },
+]
+```
+One thing you may notice is that when you mark the item as complete, `mylist.json` isn't updated with `"completed": true`. The reason for that is that our watcher in Vue, by default, watches the array size for changes, not the elements within it. Vue offers a way to [customise this behaviour](https://vuejs.org/v2/api/#vm-watch) by providing an object describing how the watcher should work. Passing `deep: true` will indicate we want to watch for object changes within the array:
+
+```javascript
+ watch: {
+ todos: {
+ handler: function(todos) {
+ window.backend.saveList(JSON.stringify(todos, null, 2));
+ },
+ deep: true
+ }
+ },
+```
+
+Now when we mark an item for selection, the `completed` variable for the item in `mylist.json` is updated accordingly.
+
+## Error Handling
+
+Now that we have most of the functionality, let's look at error handling. There are 2 placed that errors can occur: frontend and backend. When errors occur in the frontend, they are logged to the browser console, but when the app is packaged, there is no browser console, so we need to ensure we handle them correctly before packaging. Backend errors are a little easier to deal with, but we'll come to that.
+
+### Frontend Errors
+
+To demonstrate an error in the frontend, let's look at this piece of code:
+
+```javascript
+ mounted() {
+ window.backend.loadList().then(list => {
+ Wails.Log.Info("I got this list: " + list);
+ this.todos = JSON.parse(list);
+ });
+ }
+```
+
+The [JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) method with throw an error if the structure of the JSON isn't correct. We don't handle this case so it will error. Let's see what happens if we edit our `mylist.json` directly to contain the following and reload the page:
+
+```
+I am not a JSON file
+```
+
+In the browser console, in the `console` tab, you'll see something like this:
+
+
+
+
+
+The problem is, there's no indication in the app that an error occurred. We need to capture the error if it occurs and show a message to the user.
+
+The standard way to catch [errors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) in Javascript is to use [try/catch](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) blocks. We can do that to catch the error and display an error to the user. Initially, let's just print it out to our console:
+
+```javascript {4-8}
+ mounted() {
+ window.backend.loadList().then(list => {
+ try {
+ this.todos = JSON.parse(list);
+ } catch (e) {
+ Wails.Log.Info("An error was thrown: " + e.message);
+ }
+ });
+ }
+```
+
+You should see something like this appear in your console:
+
+```bash
+INFO[3081] An error was thrown: Unexpected token I in JSON at position 0
+```
+
+Now that we are capturing the error, let's show a message to the user. We will do this by having a new component data property called 'errorMessage' and whenever that is set, we will show it. Let's first define the property, with a test error message
+
+```javascript {5}
+ data() {
+ return {
+ newTodo: "",
+ editedTodo: null,
+ errorMessage: "Guru Meditation",
+ todos: []
+ };
+ },
+```
+
+Next, let's create an error box in the template. We want to only show it if `errorMessage` is not an empty string, so we will use the [v-if]() directive. This will only display the element it is attached to if the condition is met.
+
+Let's add a new div for the error message in our template:
+
+```html {3}
+
+
+
{{errorMessage}}
+
+
+
todos
+```
+
+This simply displays an `h2` element containing the error message when the error message has a length greater than 0, IE: is set.
+
+It doesn't look good, so let's style it. As App.vue is a [single file component](https://vuejs.org/v2/guide/single-file-components.html), we can simply add a `
+```
+
+Now when you reload, you will see something like this:
+
+
+
+
+
+To test the error message (if you've installed the Vue dev tools), open up the inspector, select the Vue tab and select the `App` component. You may have to click `refresh` if it doesn't appear. Using the inbuilt property editor, you can set the message in realtime. Change the error message to an empty string and you should see the message disappear.
+
+Now that we have a way of raising an error to the user, let's set the default error message to blank string:
+
+```javascript {5}
+ data() {
+ return {
+ newTodo: "",
+ editedTodo: null,
+ errorMessage: "",
+ todos: []
+ };
+ },
+```
+
+Finally, let's update our error handling to show an error message:
+
+```javascript {6}
+ mounted() {
+ window.backend.loadList().then(list => {
+ try {
+ this.todos = JSON.parse(list);
+ } catch (e) {
+ this.errorMessage = "Unable to load todo list";
+ }
+ });
+ }
+```
+
+Now when we start the app, we get the error message telling us the todolist can't be parsed.
+It would be pretty annoying to keep that message there so let's use [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) to call a function after 3 seconds to reset the error message:
+
+```javascript {7-9}
+ mounted() {
+ window.backend.loadList().then(list => {
+ try {
+ this.todos = JSON.parse(list);
+ } catch (e) {
+ this.errorMessage = "Unable to load todo list";
+ setTimeout(() => {
+ this.errorMessage = "";
+ }, 3000);
+ }
+ });
+ }
+```
+
+Reload and watch the error message disappear after 3 seconds! Of course, you may not wish to display an error message and simply set the the todos to an empty list by default. That's entirely up to you 😉
+
+### Backend Errors
+
+Errors can also occur in the backend and Wails provides a simple strategy for handling them. But first let's discuss Javascript Promises. As we discussed earlier, all backend functions that are bound to the application are available in the frontend as functions that return Promises. Promises are functions that eventually indicate whether they have been successful or whether they have failed. The terminology for this is Resolve (success) and Reject (error). There are a number of ways to work with Promises. The first is to tell the Promise what function to call in the event of success (then) and which function should handle the errors (catch).
+
+```javascript
+ promiseFunction().then(mySuccessFunction).catch(myErrorHandler);
+```
+
+Another way is by using the [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) and [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) keywords. Quite simply, if a function is declared with the `async` keyword, you can use the `await` keyword within its body. What `await` allows you to do is simply write your function call as if it were a standard function. To catch the errors, you use a try/catch block:
+
+```javascript
+async function myfunction() {
+ try {
+ let result = await promiseFunction();
+ } catch(e) {
+ // e contains the error
+ }
+}
+```
+As you can see, this approach can be a bit easier to read and to reason about. The way you deal with Promises is entirely up to you. The important thing from our point of view is that you understand them.
+
+So what does this have to do with our backend? Well, it just so happens that Go has a standard way of dealing with errors that is a bit similar: Errors are returned by the functions that the error occurred in, to be handled at the appropriate level. This leads to functions very similar to this:
+
+```go
+func myFunction() (string, error) {
+ var result string
+ var err error
+
+ // Do some processing
+
+ return result, err
+}
+```
+When you bind a function to your application, Wails will map the result of the call directly to a Promise. This means that if you return an error, the Promise in Javascript will reject. If the call is successful, the Promise will resolve. Here's a concise example:
+
+Let's create a backend function that takes a boolean value and will either succeed or fail based on its value. Place it in your `main.go` file and remember to bind it to your app:
+
+```go
+func ErrorOrSuccess(success bool) (string, error) {
+ if success {
+ return "I was successful", nil
+ } else {
+ return "", fmt.Errorf("i am an error")
+ }
+}
+
+func main() {
+...
+...
+ app.Bind(ErrorOfSuccess)
+...
+```
+
+Re-run `wails serve` so that the Go function registers with the frontend. You can see by the logs if this has happened:
+
+```bash
+INFO[0000] [Bind] Bound Function: main.ErrorOrSuccess()
+```
+
+Let's now simply open our browser console and call the function:
+
+```javascript
+backend.ErrorOrSuccess(true)
+```
+You should see something like this:
+
+
+
+
+
+If you call it with `false`, you will see this instead:
+
+
+
+
+
+So now you can treat your backend code like any modern Javascript async function. Neat huh?
+
+Of course, sending errors back from the backend is optional - Wails will map what it is given and if there's no error, then the promise will only resolve, not reject.
+
+So let's see this in action! You may remember that our `loadList` function was also defined to return an error:
+
+```go
+func loadList() (string, error) {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return "", err
+ }
+ filename := path.Join(cwd, "mylist.json")
+ bytes, err := ioutil.ReadFile(filename)
+ var result = string(bytes)
+ return result, err
+}
+```
+
+There are 2 circumstances we return an error: either the current working directory doesn't exist (tricky!) or we are unable to read the default file. So what happens if we simply delete our `mylist.json` and reload our browser window? The browser console will tell you!
+
+
+
+
+
+That's a standard Go error, right there in your browser console. You may notice it says "Uncaught (in promise)". That's true, we did't catch this so the error has gone rogue. That's because we aren't handling the error case for our loadList function in `App.vue`. Let's fix that:
+
+```javascript {14-16}
+ mounted() {
+ window.backend
+ .loadList()
+ .then(list => {
+ try {
+ this.todos = JSON.parse(list);
+ } catch (e) {
+ this.errorMessage = "Unable to load todo list";
+ setTimeout(() => {
+ this.errorMessage = "";
+ }, 3000);
+ }
+ })
+ .catch(error => {
+ this.errorMessage = error;
+ });
+ }
+```
+Now our app shows the following message:
+
+
+
+
+
+Let's shut that down after 3 seconds as well:
+
+```javascript {3-5}
+ .catch(error => {
+ this.errorMessage = error;
+ setTimeout(() => {
+ this.errorMessage = "";
+ }, 3000);
+ });
+```
+
+Let's edit loadList to make the message a bit better by checking for an error, then rewriting it:
+
+```go {8-10}
+func loadList() (string, error) {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return "", err
+ }
+ filename := path.Join(cwd, "mylist.json")
+ bytes, err := ioutil.ReadFile(filename)
+ if err != nil {
+ err = fmt.Errorf("Unable to open list: %s", filename)
+ }
+ var result = string(bytes)
+ return result, err
+}
+```
+
+Re-run `wails serve` and refresh your app (resets the 3 second timer):
+
+
+
+
+
+Of course, as soon as we add a todo, the file will get written again and all will be good again with the world...
+
+Now that we've covered error handling, let's look at how we typically structure the backend - namely, using structs.
+
+## Using Structs
+
+Whilst our backend currently works, it's not perfect. For one, there is duplicate code in both loadList and saveList. Whilst we could create yet another function to handle this, as the app grows, it becomes a unmanageable to keep everything in the `main.go` file.
+
+What we are going to do is move both of these functions into a common [struct](https://gobyexample.com/structs). Let's start by creating a new file in the project directory called `todos.go` and create a basic struct in there:
+
+```go
+package main
+
+type Todos struct {
+ filename string
+}
+```
+This defines a struct with a single member variable to hold our todolist filename. Next let's create a function in `todos.go` to create a Todo struct and calculate our filename:
+
+```go
+// NewTodos attempts to create a new Todo list
+func NewTodos() (*Todos, error) {
+ // Create new Todos instance
+ result := &Todos{}
+ // Try and get the current working directory
+ cwd, err := os.Getwd()
+ if err != nil {
+ return nil, err
+ }
+ // Join the cwd with our todos filename
+ filename := path.Join(cwd, "mylist.json")
+ // Set the filename member of our new Todo list
+ result.filename = filename
+ // Return it
+ return result, nil
+}
+```
+This function creates a new Todos struct, then attempts to calculate the path to the json file. If it fails, it returns an error. If it works, it sets the filename in the newly created Todos and returns it.
+
+We have essentially copied our common code from `loadList` and `saveList`, but it is run only once.
+
+We will create a new Todo struct on application startup so let's add some code to our `main.go`
+
+Now let's convert our `loadList` method into a struct method. Let's first copy the whole function out of `main.go`:
+
+```go
+func loadList() (string, error) {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return "", err
+ }
+ filename := path.Join(cwd, "mylist.json")
+ bytes, err := ioutil.ReadFile(filename)
+ if err != nil {
+ err = fmt.Errorf("Unable to open list: %s", filename)
+ }
+ var result = string(bytes)
+ return result, err
+}
+```
+
+Then let's make it a [method](https://gobyexample.com/methods) of the struct by adding the [receiver](https://tour.golang.org/methods/4) to the declaration:
+
+```go {1}
+func (t *Todos) loadList() (string, error) {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return "", err
+ }
+ filename := path.Join(cwd, "mylist.json")
+ bytes, err := ioutil.ReadFile(filename)
+ if err != nil {
+ err = fmt.Errorf("Unable to open list: %s", filename)
+ }
+ var result = string(bytes)
+ return result, err
+}
+```
+
+We can now get rid of the code we used to calculate our filename, and simply refer to the filename that is calculated when we create a Todos instance. This simplifies the code:
+
+```go
+func (t *Todos) loadList() (string, error) {
+ bytes, err := ioutil.ReadFile(t.filename)
+ if err != nil {
+ err = fmt.Errorf("Unable to open list: %s", t.filename)
+ }
+ return string(bytes), err
+}
+```
+
+We now go through the same process with `saveList`. Copy and paste it from `main.go`, add the receiver and remove the filename code. This becomes radically simpler, going from:
+
+```go
+func saveList(todos string) error {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return err
+ }
+ filename := path.Join(cwd, "mylist.json")
+ return ioutil.WriteFile(filename, []byte(todos), 0600)
+}
+```
+
+to:
+
+```go
+func (t *Todos) saveList(todos string) error {
+ return ioutil.WriteFile(t.filename, []byte(todos), 0600)
+}
+```
+
+Our complete `todos.go` should now look like this:
+
+```go
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+)
+
+type Todos struct {
+ filename string
+}
+
+// NewTodos attempts to create a new Todo list
+func NewTodos() (*Todos, error) {
+ // Create new Todos instance
+ result := &Todos{}
+ // Try and get the current working directory
+ cwd, err := os.Getwd()
+ if err != nil {
+ return nil, err
+ }
+ // Join the cwd with our todos filename
+ filename := path.Join(cwd, "mylist.json")
+ // Set the filename member of our new Todo list
+ result.filename = filename
+ // Return it
+ return result, nil
+}
+
+func (t *Todos) loadList() (string, error) {
+ bytes, err := ioutil.ReadFile(t.filename)
+ if err != nil {
+ err = fmt.Errorf("Unable to open list: %s", t.filename)
+ }
+ return string(bytes), err
+}
+
+func (t *Todos) saveList(todos string) error {
+ return ioutil.WriteFile(t.filename, []byte(todos), 0600)
+}
+```
+
+Now let's attach our new struct to our application! First, let's remove the ErrorOrSuccess function as it's now not needed. Next, we will create an instance of our Todos instance and bind it to the app:
+
+```go {4,15-18,28}
+package main
+
+import (
+ "log"
+
+ "github.com/leaanthony/mewn"
+ "github.com/wailsapp/wails"
+)
+
+func main() {
+
+ js := mewn.String("./frontend/dist/app.js")
+ css := mewn.String("./frontend/dist/app.css")
+
+ myTodoList, err := NewTodos()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ app := wails.CreateApp(&wails.AppConfig{
+ Width: 1024,
+ Height: 768,
+ Title: "todos",
+ JS: js,
+ CSS: css,
+ Colour: "#131313",
+ })
+ app.Bind(myTodoList)
+ app.Run()
+}
+```
+We add an import to log in line 4 as we may need to log an error. Line 15 calls our `NewTodos` function and lines 16-18 logs an error if it was unsuccessful.
+We final bind our new struct to the app. It's worth noting that only struct instances may be bound to your app, not struct definitions. For example, this would have been invalid:
+
+```go
+ app.Bind(Todos)
+```
+
+If we now re-run `wails serve` we notice that there is no messages indicating that Todos has been bound. What happened? When binding structs to your app, Wails only binds those struct methods that are marked as [exported](https://www.callicoder.com/golang-structs/#exported-vs-unexported-structs-and-struct-fields). Struct method names that start with a capital letter are designated exported. This is a standard Go feature and Wails adheres to this model. It means we can create internal functions that are not exposed to the frontend.
+
+Let's capitalise the load and save method names and re-run `wails serve`:
+
+```bash {7,8}
+INFO[0000] [App] Starting
+INFO[0000] [Events] Starting
+INFO[0000] [Events] Listening
+INFO[0000] [IPC] Starting
+INFO[0000] [Bind] Starting
+INFO[0000] [Bind] Binding Go Functions/Methods
+INFO[0000] [Bind] Bound Method: main.Todos.LoadList()
+INFO[0000] [Bind] Bound Method: main.Todos.SaveList()
+INFO[0000] [Headless] Headless mode started.
+INFO[0000] [Headless] The Wails bridge will connect automatically.
+INFO[0000] [Headless] Connection from frontend accepted [0xc000282000].
+INFO[0000] [Headless] Connected to frontend.
+>>>>> To connect, you will need to run 'npm run serve' in the 'frontend' directory <<<<<
+```
+Now we can see that our methods have been bound. Let's update our frontend to use these new methods.
+
+In the same way functions are bound to the `backend` object in the frontend, structs are too. They are bound by name, so the `Todos` struct we have bound is accessible via `backend.Todos`. As expected, our `LoadList` and `SaveList` methods are available at `backend.Todos.LoadList` and `backend.Todos.SaveList` respectively.
+
+We now need to update `App.vue` to use these new methods. Let's start with `LoadList`:
+
+```javascript {2,3}
+ mounted() {
+ window.backend.Todos
+ .LoadList()
+ .then(list => {
+ try {
+ this.todos = JSON.parse(list);
+ } catch (e) {
+```
+Now lets update `SaveList`:
+
+```javascript{4}
+ watch: {
+ todos: {
+ handler: function(todos) {
+ window.backend.Todos.SaveList(JSON.stringify(todos, null, 2));
+ },
+ deep: true
+ }
+ },
+```
+Once we save this, we should see that the app works as expected. Add a couple of todos and check `mylist.json`. You will see it's getting updated correctly.
+
+You're probably wondering why go to the effort of using a struct if the functionality is the same? That's a good question and there's a number of reasons including separation of concerns, modularisation, it's common practice in Go... but the main reason, is that it allows you to gain access to the Wails runtime in Go, and this provides some cool things which we are about to use...
+
+## Wails Runtime in Go
+
+Just as there is a Javascript runtime available to the frontend, there is a [Go runtime](https://godoc.org/github.com/wailsapp/wails#Runtime) available to structs. This provides the following features:
+
+ * [Logging](https://godoc.org/github.com/wailsapp/wails#RuntimeLog)
+ * [Events](https://godoc.org/github.com/wailsapp/wails#RuntimeEvents)
+ * [Dialogs](https://godoc.org/github.com/wailsapp/wails#RuntimeDialog)
+ * [Window Control](https://godoc.org/github.com/wailsapp/wails#RuntimeWindow)
+ * [FileSystem](https://godoc.org/github.com/wailsapp/wails#RuntimeFileSystem)
+
+To access this runtime, we create a struct method called "WailsInit". This method requires the following signature:
+
+```go
+WailsInit(runtime *wails.Runtime) error {
+ // Initialisation code
+}
+```
+There are multiple purposes for this function:
+
+ * Performing initialisation tasks on structs
+ * Allow structs to raise an error if something went wrong during setup
+ * Presenting the runtime to the struct
+
+Let's start by updating our struct so we keep a reference to the runtime:
+
+```go {3}
+type Todos struct {
+ filename string
+ runtime *wails.Runtime
+}
+```
+If you aren't familiar with why there is an asterisk in front of "wails.Runtime", it would be worth reading [this](https://tour.golang.org/moretypes/1).
+
+Now that we have a place to store the runtime, let's create our `WailsInit` method in the same file:
+
+```go
+func (t *Todos) WailsInit(runtime *wails.Runtime) error {
+ t.runtime = runtime
+ return nil
+}
+```
+If we re-run our app, we will so there is nothing different, so let's use the runtime logger to output a message:
+
+```go {3,4}
+func (t *Todos) WailsInit(runtime *wails.Runtime) error {
+ t.runtime = runtime
+ myLog := t.runtime.Log.New("Todos")
+ myLog.Info("I'm here")
+ return nil
+}
+```
+Line 3 creates a brand new Logger and we give it the prefix "Todos". The struct can now use this like the frontend logger, which we do in line 4. If we re-run our backend, we will see the following line:
+
+```bash {6}
+INFO[0000] [IPC] Starting
+INFO[0000] [Bind] Starting
+INFO[0000] [Bind] Binding Go Functions/Methods
+INFO[0000] [Bind] Bound Method: main.Todos.LoadList()
+INFO[0000] [Bind] Bound Method: main.Todos.SaveList()
+INFO[0000] [Todos] I'm here
+INFO[0000] [Headless] Headless mode started.
+INFO[0000] [Headless] The Wails bridge will connect automatically.
+INFO[0000] [Headless] Connection from frontend accepted [0xc0002aa000].
+INFO[0000] [Headless] Connected to frontend.
+```
+As you can see, we have a nicely labelled message. We also can see that it is run straight after all the binding. The logging functions in the Go runtime and the Javascript runtime call the same code behind the scenes. This gives us a unified logging mechanism. Let's keep a reference to the logger in the main struct:
+
+```go {4}
+type Todos struct {
+ filename string
+ runtime *wails.Runtime
+ logger *wails.CustomLogger
+}
+```
+ And we'll update our `WailsInit` method to use it:
+
+ ```go
+ func (t *Todos) WailsInit(runtime *wails.Runtime) error {
+ t.runtime = runtime
+ t.logger = t.runtime.Log.New("Todos")
+ t.logger.Info("I'm here")
+ return nil
+}
+```
+
+We can now use this in our LoadList and SaveList methods:
+
+```go {2,11}
+func (t *Todos) LoadList() (string, error) {
+ t.logger.Infof("Loading list from: %s", t.filename)
+ bytes, err := ioutil.ReadFile(t.filename)
+ if err != nil {
+ err = fmt.Errorf("Unable to open list: %s", t.filename)
+ }
+ return string(bytes), err
+}
+
+func (t *Todos) SaveList(todos string) error {
+ t.logger.Infof("Saving list: %s", todos)
+ return ioutil.WriteFile(t.filename, []byte(todos), 0600)
+}
+```
+Now we have a means of logging, we can log anything we like there. Explore the [Logger documentation](https://godoc.org/github.com/wailsapp/wails#CustomLogger) for more details.
+
+## Event Handling
+
+Now that we have access to the Runtime in Go, we have access to the [Events](https://godoc.org/github.com/wailsapp/wails#RuntimeEvents) subsystem. The really powerful thing about Wails Events is that it is a unified event bus between Javascript and Go. This means that you can listen for an event in one, emit it in the other and it will work as expected. You can even pass data with events!
+
+We will demonstrate this by allowing the backend to send an error message, and the frontend will display it in the existing error box we created earlier. First, let's update `App.vue` so that we listen for an "error" event each time we mount the app:
+
+```javascript{2-7}
+ mounted() {
+ Wails.Events.On("error", message => {
+ this.errorMessage = message;
+ setTimeout(() => {
+ this.errorMessage = "";
+ }, 3000);
+ });
+ window.backend.Todos.LoadList()
+ .then(list => {
+ try {
+```
+This function will set the `errorMessage` property to the given message and then hide it after 3 seconds. This is the same code that we use when handling the JSON parse error. Save the file and open up the browser console. Let's test this code by using the Javascript Wails Runtime to emit the event. Type:
+
+```javascript
+window.wails.Events.Emit("error", "I am a message from Javascript!")
+```
+You should see the error message pop up for 3 seconds then disappear.
+
+Now let's emit the same event from the backend:
+
+```go {7}
+func (t *Todos) LoadList() (string, error) {
+ t.logger.Infof("Loading list from: %s", t.filename)
+ bytes, err := ioutil.ReadFile(t.filename)
+ if err != nil {
+ err = fmt.Errorf("Unable to open list: %s", t.filename)
+ }
+ t.runtime.Events.Emit("error", "I am a message from Go!")
+ return string(bytes), err
+}
+```
+
+
+
+
+Emit accepts an arbitrary number of arguments and that data will be passed to any listeners in the order it was given. To demonstrate, let's send a number back with the event. On the frontend, we'll read that number, double it, then print it with the message.
+
+Let's update our backend:
+
+```go {7}
+func (t *Todos) LoadList() (string, error) {
+ t.logger.Infof("Loading list from: %s", t.filename)
+ bytes, err := ioutil.ReadFile(t.filename)
+ if err != nil {
+ err = fmt.Errorf("Unable to open list: %s", t.filename)
+ }
+ t.runtime.Events.Emit("error", "I am a message from Go!", 1234)
+
+ return string(bytes), err
+}
+```
+
+Now let's update our event handler in `App.vue` to process the number:
+
+```javascript {2-4}
+ mounted() {
+ Wails.Events.On("error", (message, number) => {
+ let result = number * 2;
+ this.errorMessage = `${message}: ${result}`;
+ setTimeout(() => {
+ this.errorMessage = "";
+ }, 3000);
+ });
+```
+
+You should now see something similar when the app starts:
+
+
+
+
+
+This mechanism allows real flexibility in how you structure your application.
+In our instance, we will leave the error handling as is and we will look at using events for a different task: tracking file changes.
+
+## Tracking File Changes
+
+The real purpose of [Events](https://godoc.org/github.com/wailsapp/wails#RuntimeEvents) is to notify your application when something happens. As an example of this, we will run a filewatcher and let the frontend know when it has changed. To do this, we will be using the 3rd party library [fsnotify](https://github.com/fsnotify/fsnotify). To get started, let's install it using a terminal in the project root directory:
+
+```bash
+go get github.com/fsnotify/fsnotify
+```
+
+Next let's update our `todos.go` file. What we want to do is start a filewatcher during initialisation and listen for when the file is modified. We'll start by adding a private Method to our struct. This will start the watcher and listen for changes on our `mylist.json` file. It is a modified version of the fsnotify [example](https://github.com/fsnotify/fsnotify/blob/master/example_test.go):
+
+```go
+func (t *Todos) startWatcher() error {
+ t.logger.Info("Starting Watcher")
+ watcher, err := fsnotify.NewWatcher()
+ if err != nil {
+ return err
+ }
+
+ go func() {
+ for {
+ select {
+ case event, ok := <-watcher.Events:
+ if !ok {
+ return
+ }
+ if event.Op&fsnotify.Write == fsnotify.Write {
+ t.logger.Infof("modified file: %s", event.Name)
+ }
+ case err, ok := <-watcher.Errors:
+ if !ok {
+ return
+ }
+ t.logger.Error(err.Error())
+ }
+ }
+ }()
+
+ err = watcher.Add(t.filename)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+```
+
+If your IDE hasn't already done so, add the fsnotify import line:
+
+```go {9}
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+
+ "github.com/fsnotify/fsnotify"
+ "github.com/wailsapp/wails"
+)
+```
+
+The last thing we need to do now is to start the watcher when the struct is initialised:
+
+```go {5}
+func (t *Todos) WailsInit(runtime *wails.Runtime) error {
+ t.runtime = runtime
+ t.logger = t.runtime.Log.New("Todos")
+ t.logger.Info("I'm here")
+ return t.startWatcher()
+}
+```
+
+As `startWatcher` returns a single error, we can just return whatever it returns. If you now re-serve the application, you should see some output:
+
+```bash
+INFO[0000] [Bind] Starting
+INFO[0000] [Bind] Binding Go Functions/Methods
+INFO[0000] [Bind] Bound Method: main.Todos.LoadList()
+INFO[0000] [Bind] Bound Method: main.Todos.SaveList()
+INFO[0000] [Todos] I'm here
+INFO[0000] [Todos] Starting Watcher
+```
+Great! Now our watcher is running, try modifying the `mylist.json` file. When you save, you will notice a message:
+
+```bash
+INFO[0005] [Todos] modified file: /Users/lea/Projects/todos/mylist.json
+```
+Cool! Now we are picking up the modified event, let's notify our frontend by emitting an event:
+
+```go {6}
+ if !ok {
+ return
+ }
+ if event.Op&fsnotify.Write == fsnotify.Write {
+ t.logger.Infof("modified file: %s", event.Name)
+ t.runtime.Events.Emit("filemodified")
+ }
+ case err, ok := <-watcher.Errors:
+ if !ok {
+ return
+ }
+```
+Now let's get the frontend to listen for the event. Let's edit `App.vue`:
+
+```javascript {2-7}
+ mounted() {
+ Wails.Events.On("filemodified", () => {
+ this.errorMessage = "File Modified";
+ setTimeout(() => {
+ this.errorMessage = "";
+ }, 3000);
+ });
+ Wails.Events.On("error", (message, number) => {
+ let result = number * 2;
+ this.errorMessage = `${message}: ${result}`;
+```
+
+Try editing `mylist.json` and see what happens when you save. You should see something like this:
+
+
+
+
+
+Let's take a second to do some small refactors. We have code that sets the error message duplicated in 3 places. Let's create a component method to do this:
+
+```javascript {5-10}
+ cancelEdit: function(todo) {
+ this.editedTodo = null;
+ todo.title = this.beforeEditCache;
+ },
+ setErrorMessage: function(message) {
+ this.errorMessage = message;
+ setTimeout(() => {
+ this.errorMessage = "";
+ }, 3000);
+ }
+ },
+```
+
+Now we can simplify our `mounted` method:
+
+```javascript {2-21}
+ mounted() {
+ Wails.Events.On("filemodified", () => {
+ this.setErrorMessage("File Modified");
+ });
+
+ Wails.Events.On("error", (message, number) => {
+ let result = number * 2;
+ this.setErrorMessage(`${message}: ${result}`);
+ });
+
+ window.backend.Todos.LoadList()
+ .then(list => {
+ try {
+ this.todos = JSON.parse(list);
+ } catch (e) {
+ this.setErrorMessage("Unable to load todo list");
+ }
+ })
+ .catch(error => {
+ this.setErrorMessage(error.message);
+ });
+ }
+```
+
+As we will be reusing the loadlist function, we'll move it from `mounted()` to the `methods` section of the component:
+
+```javascript {7-19}
+ setErrorMessage: function(message) {
+ this.errorMessage = message;
+ setTimeout(() => {
+ this.errorMessage = "";
+ }, 3000);
+ },
+ loadList: function() {
+ window.backend.Todos.LoadList()
+ .then(list => {
+ try {
+ this.todos = JSON.parse(list);
+ } catch (e) {
+ this.setErrorMessage("Unable to load todo list");
+ }
+ })
+ .catch(error => {
+ this.setErrorMessage(error.message);
+ });
+ }
+ },
+ directives: {
+```
+We now call this method from `mounted`:
+
+```javascript {11-12}
+ mounted() {
+ Wails.Events.On("filemodified", () => {
+ this.setErrorMessage("File Modified");
+ });
+
+ Wails.Events.On("error", (message, number) => {
+ let result = number * 2;
+ this.setErrorMessage(`${message}: ${result}`);
+ });
+
+ // Load the list at the start
+ this.loadList();
+ }
+};
+```
+
+Now there's a small issue we need to address. We have a watcher listening for changes to the file and when it's updated it notifies the frontend that it has been modified. The frontend then reloads it. But when it updates the todo list, it also saves automatically. See the problem here?
+
+Let's use a variable to indicate we are loading and therefore don't need to resave:
+
+```javascript {6}
+ data() {
+ return {
+ newTodo: "",
+ editedTodo: null,
+ errorMessage: "",
+ loading: false,
+ todos: []
+ };
+ },
+```
+
+Let's modiy our watcher to only write if we aren't loading:
+
+```javascript {4-7}
+ watch: {
+ todos: {
+ handler: function(todos) {
+ if (this.loading) {
+ this.loading = false;
+ return;
+ }
+ window.backend.Todos.SaveList(JSON.stringify(todos, null, 2));
+ },
+ deep: true
+ }
+ },
+```
+
+Now let's update our `loadlist` method to set the loading flag after we load the list:
+
+```javascript {5-7}
+ loadList: function() {
+ window.backend.Todos.LoadList()
+ .then(list => {
+ try {
+ let todos = JSON.parse(list);
+ this.loading = true;
+ this.todos = todos;
+ } catch (e) {
+ this.setErrorMessage("Unable to load todo list");
+ }
+ })
+ .catch(error => {
+ this.setErrorMessage(error.message);
+ });
+ }
+ },
+```
+Now the final part is to call loadList when the file has been updated:
+
+```javascript {3}
+ mounted() {
+ Wails.Events.On("filemodified", () => {
+ this.loadList();
+ });
+
+ Wails.Events.On("error", (message, number) => {
+```
+
+Now if you edit your JSON list outside the app, it gets reflected in realtime in the app!
+
+This change has a side effect: the edit todo has now started exiting after every keypress. Why is this? Well, when we edit a todo item, we are updating the value of the todo item after every keypress (default behaviour of Vue's data binding). After the first keypress, the todo list data will change, our watcher will then trigger a save. Because the file has been modified, it will then trigger a load. The load will update the todo list and the editing will stop. What we want is to only update the value of the todo's title when we finish editing, not every keypress. This can be achieved through Vue's [lazy modifier](https://vuejs.org/v2/guide/forms.html#lazy). This will only sync the value after a change event, not an input event. Let's update our template:
+
+```html {4}
+
+```
+Retest the app, and you should see it now works as intended.
+
+Now that the app appears to be functioning well, let's build it as a native app.
+
+## Building the App
+
+To build the app we simply have to run `wails build` in your project directory. This will produce a binary in the current directory. If you run it, you should see something like this:
+
+
+
+
+
+Congratulations! You've made a single binary native app using Go and Vue!
+
+However, what happens if you move the binary, say, to `/tmp` and run it from there? Whoah! We get an error:
+
+```bash
+ERRO[0000] [App] lstat /tmp/mylist.json: no such file or directory
+```
+It's true, there's no `mylist.json`, but didn't we make it so that it will create it if it didn't exist? Yes we did, however, let's take a closer look at our struct initialisation:
+
+```go
+func (t *Todos) WailsInit(runtime *wails.Runtime) error {
+ t.runtime = runtime
+ t.logger = t.runtime.Log.New("Todos")
+ t.logger.Info("I'm here")
+ return t.startWatcher()
+}
+```
+We setup the logger then start the file watcher. Oh. The file watcher. After we set it up, we add the file we want to watch:
+
+```go {5-8}
+ }
+ }
+ }()
+
+ err = watcher.Add(t.filename)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+```
+Because the file doesn't exist, it returns an error, and this is what we are seeing. We need to test if the file exists and create a default one if it doesn't. Let's create a new method on our struct:
+
+```go
+func (t *Todos) ensureFileExists() {
+ // Check status of file
+ _, err := os.Stat(t.filename)
+ // If it doesn't exist
+ if os.IsNotExist(err) {
+ // Create it with a blank list
+ ioutil.WriteFile(t.filename, []byte("[]"), 0600)
+ }
+}
+```
+Now we just need to call this method before starting the watcher:
+
+```go {5}
+func (t *Todos) WailsInit(runtime *wails.Runtime) error {
+ t.runtime = runtime
+ t.logger = t.runtime.Log.New("Todos")
+ t.logger.Info("I'm here")
+ t.ensureFileExists()
+ return t.startWatcher()
+}
+```
+
+Now if we rebuild the app and copy it to a different directory, we can see that the app starts and it creates a new default list in the same directory.
+
+### Debug Build
+
+In some cases, it may be desirable to debug the native application rather than using a web browser via the `wails serve` command. This is done by creating a debug build of your application.
+
+To create a debug build we simply run `wails build -d`. A debug version of the app means the app will now:
+ * Log messages to the console
+ * Enable the webview inspector (Mac/Linux)
+
+Now you will be able to right click and use the native inspector to debug your app as well as inspect the log messages.
+
+## A Better List Location
+
+It's not often practical, or desirable, to keep data files in the same location as the binary. Often the directory containing the binary is not even writable. So we will change the default location of our list to the user's home directory. This is a fairly simple change and Wails offers an easy way to get this directory using the [HomeDir](https://godoc.org/github.com/wailsapp/wails#RuntimeFileSystem.HomeDir) method exposed by the runtime. We need to do this before starting the watcher so let's add it to `WailsInit`:
+
+```go {6-11}
+func (t *Todos) WailsInit(runtime *wails.Runtime) error {
+ t.runtime = runtime
+ t.logger = t.runtime.Log.New("Todos")
+ t.logger.Info("I'm here")
+
+ // Set the default filename to $HOMEDIR/mylist.json
+ homedir, err := runtime.FileSystem.HomeDir()
+ if err != nil {
+ return err
+ }
+ t.filename = path.Join(homedir, "mylist.json")
+
+ t.ensureFileExists()
+ return t.startWatcher()
+}
+```
+
+Now if we rebuild the app and run it, we should see a blank list, as it will be a new one. Add an item to the list. Now copy the binary to a different directory and re-run it. You will see that it displays the same list.
+
+For a bit of fun, run two copies of the app in different directories. If you edit the list in one app, it will instantly appear in the other! As we update our list for any changes, and we are listening to file changes, both apps are perfectly in sync.
+
+## Save As
+
+So far, our app only deals with one list. Wouldn't it be great if it dealt with multiple lists? To achieve this, we need to do the following:
+
+ * Have a "Save As" button
+ * Show a Save dialog for the user to input a filename
+ * Save the current todo list into this file
+ * Update the filewatcher
+
+Let's implement it in that order.
+
+### Save As Button
+
+We will add a button at the top of the list, above the input box. We will reuse some of the existing styles from the mvctodo app:
+
+```javascript {3-9}
+
+
+ {
+```
+Finally, let's add some styling to the `
+```
+
+The app should now have a `Save As` button, that when clicked, shows a message at the top of the page:
+
+
+
+
+
+### Show "Save As" Dialog
+
+The Wails runtime provides access to a number of [native dialogs](https://godoc.org/github.com/wailsapp/wails#RuntimeDialog). We are interested in using the [SelectSaveFile dialog](https://godoc.org/github.com/wailsapp/wails#RuntimeDialog.SelectSaveFile). We will call a `saveAs` method on the backend and do all the saving logic there:
+
+```javascript {6}
+ cancelEdit: function(todo) {
+ this.editedTodo = null;
+ todo.title = this.beforeEditCache;
+ },
+ saveAs: function() {
+ window.backend.Todos.SaveAs(JSON.stringify(this.todos, null, 2));
+ },
+ setErrorMessage: function(message) {
+ this.errorMessage = message;
+ }
+```
+Now let's create the method in the backend:
+
+```go {6-10}
+func (t *Todos) SaveList(todos string) error {
+ t.logger.Infof("Saving list: %s", todos)
+ return ioutil.WriteFile(t.filename, []byte(todos), 0600)
+}
+
+func (t *Todos) SaveAs(todos string) error {
+ filename := t.runtime.Dialog.SelectSaveFile()
+ t.logger.Info("Save As: " + filename)
+ return nil
+}
+
+func (t *Todos) ensureFileExists() {
+```
+
+Now when we click on the `Save As` button....nothing appears to happen! Let's check the logs:
+
+```bash
+WARN[0010] [Bridge] SelectSaveFile() unsupported in bridge mode
+INFO[0010] [Todos] Save As:
+```
+
+Currently, native dialogs are unsupported in bridge mode, so let's compile to a native app again, but in debug mode: `wails build -d`.
+
+When we run it and press the `Save As` button, we get a save dialog:
+
+
+
+
+
+Enter a name and press `Save`. The logging should look similar to this:
+
+```bash
+INFO[0000] [Bind] Bound Method: main.Todos.LoadList()
+INFO[0000] [Bind] Bound Method: main.Todos.SaveAs()
+INFO[0000] [Bind] Bound Method: main.Todos.SaveList()
+INFO[0000] [Todos] I'm here
+INFO[0000] [Todos] Starting Watcher
+INFO[0000] [WebView] Run()
+INFO[0000] [Todos] Loading list from: /Users/lea/mylist.json
+INFO[0077] [Todos] Save As: /Users/lea/Desktop/test.json
+```
+Now that we have the filename to save the list to, let's implement that next.
+
+### Saving the Todo List
+
+We already have a `Save` method so saving this list is as easy as updating the filename and saving using our `SaveList` method:
+
+```go {4-5}
+func (t *Todos) SaveAs(todos string) error {
+ filename := t.runtime.Dialog.SelectSaveFile()
+ t.logger.Info("Save As: " + filename)
+ t.filename = filename
+ t.SaveList(todos)
+ return nil
+}
+```
+Rebuild the app and try saving your list. You will see that the file gets written! Any updates to the list now get written to our new file. However, we aren't tracking changes to the file so we need to update our watcher. Fortunately, the fsnotify library supports [removing files](https://godoc.org/github.com/fsnotify/fsnotify#Watcher.Remove) from being watched so let's adjust our code so that every time we update the filename, we also update the watcher. Firstly, let's keep a reference to the watcher in the Todo's struct:
+
+```go {5}
+type Todos struct {
+ filename string
+ runtime *wails.Runtime
+ logger *wails.CustomLogger
+ watcher *fsnotify.Watcher
+}
+```
+And update startWatcher to save the watcher reference:
+
+```go {4}
+func (t *Todos) startWatcher() error {
+ t.logger.Info("Starting Watcher")
+ watcher, err := fsnotify.NewWatcher()
+ t.watcher = watcher
+ if err != nil {
+ return err
+ }
+```
+Now we can create a method to update the filename:
+
+```go
+func (t *Todos) setFilename(filename string) error {
+ var err error
+ // Stop watching the current file and return any error
+ err = t.watcher.Remove(t.filename)
+ if err != nil {
+ return err
+ }
+
+ // Set the filename
+ t.filename = filename
+
+ // Add the new file to the watcher and return any errors
+ err = t.watcher.Add(filename)
+ if err != nil {
+ return err
+ }
+ t.logger.Info("Now watching: " + filename)
+ return nil
+}
+```
+
+Now the problem we have is that we are currently setting the filename before saving but when we set the filename, we now try and watch it and we know what happens if that file doesn't exist...
+
+To solve this, we will create a new method for saving the file that takes a filename as a parameter:
+
+```go
+func (t *Todos) saveListByName(todos string, filename string) error {
+ return ioutil.WriteFile(filename, []byte(todos), 0600)
+}
+```
+Now we need to rector our `SaveList` method to use our new method:
+
+```go
+func (t *Todos) SaveList(todos string) error {
+ t.logger.Infof("Saving list: %s", todos)
+ return t.saveListByName(todos, t.filename)
+}
+```
+
+The last thing for us to do now is to update our `SaveAs` method:
+
+```go {4-8}
+func (t *Todos) SaveAs(todos string) error {
+ filename := t.runtime.Dialog.SelectSaveFile()
+ t.logger.Info("Save As: " + filename)
+ err := t.saveListByName(todos, filename)
+ if err != nil {
+ return err
+ }
+ return t.setFilename(filename)
+}
+```
+Now the list will be saved under the new name, and the file watcher updated to listen to it.
+
+One final touch, is to put the name of the file in the title bar. This uses the [SetTitle](https://godoc.org/github.com/wailsapp/wails#RuntimeWindow.SetTitle) method of the Runtime's [Window](https://godoc.org/github.com/wailsapp/wails#RuntimeWindow) methods. We will set it initially, then every time we save the list. Let's update `WailsInit` first:
+
+```go {12}
+func (t *Todos) WailsInit(runtime *wails.Runtime) error {
+ t.runtime = runtime
+ t.logger = t.runtime.Log.New("Todos")
+ t.logger.Info("I'm here")
+
+ // Set the default filename to $HOMEDIR/mylist.json
+ homedir, err := runtime.FileSystem.HomeDir()
+ if err != nil {
+ return err
+ }
+ t.filename = path.Join(homedir, "mylist.json")
+ t.runtime.Window.SetTitle(t.filename)
+ t.ensureFileExists()
+ return t.startWatcher()
+}
+```
+Now we'll update `setFilename`:
+
+```go {7}
+ // Add the new file to the watcher and return any errors
+ err = t.watcher.Add(filename)
+ if err != nil {
+ return err
+ }
+ t.logger.Info("Now watching: " + filename)
+ t.runtime.Window.SetTitle(t.filename)
+ return nil
+}
+```
+
+Now we have `Save As` working correctly, let's move on to loading a list.
+
+## Loading a Todo List
+
+Loading is going to require us to do the following things:
+
+ * Add a Load button
+ * Lo
+
+### Add a Load Button
+
+This is fairly trivial as we already have a `Save As` button:
+
+```html {6-8}
+
+````
+We will call "loadNewList" when it is pressed. This is simply going to call the backend to load the list. Let's add that:
+
+```javascript {4-6}
+ saveAs: function() {
+ window.backend.Todos.SaveAs(JSON.stringify(this.todos, null, 2));
+ },
+ loadNewList: function() {
+ window.backend.Todos.LoadNewList();
+ },
+ setErrorMessage: function(message) {
+ this.errorMessage = message;
+```
+Now we need to implement `LoadNewList` in the backend. All we are going to do is present a [File Select](https://godoc.org/github.com/wailsapp/wails#RuntimeDialog.SelectFile) dialog and if the user selects a file, we will set our todo list's filename to that file (which will also start watching it). To get it to load into the frontend, we will cheat a little by simply emitting the 'filemodified' event we created earlier:
+
+```go
+func (t *Todos) LoadNewList() {
+ filename := t.runtime.Dialog.SelectFile()
+ if len(filename) > 0 {
+ t.setFilename(filename)
+ t.runtime.Events.Emit("filemodified")
+ }
+}
+```
+
+Now clicking `Load` will allow you to select a new list and load it into the app!
+
+## Packaging the App
+
+To package the app, we simply run `wails build -p` in the project directory. This will package up the application into a platform-native format.
+
+On Mac, this means creating a `.app` package, with an icon.
+On Windows, this means creating a `.exe` file, with an icon.
+On linux, this simply means creating a binary.
+
+You should now see a binary in your project directory. The binary should have the default icon (Mac, Windows):
+
+
+
+
+
+You will also notice that there is another file `appicon.png`. If you want to change the icon of the application, change this file and re-run `wails build -p`. To demonstrate this, let's download [this awesome icon](http://www.iconarchive.com/show/kameleon.pics-icons-by-webalys/Checklist-icon.html) by [Kameleon Icons](http://www.kameleon.pics/). Select the 512x512 version. Name it `appicon.png` and copy it over the original `appicon.png`. Now run `wails build -p` again. You'll now see the icon updated:
+
+
+
+
+
+Congratulations! You have now created a fully portable, single binary desktop application using Go and Vue!