A package to build progressive web apps with Go programming language and WebAssembly.

Overview

go-app

Circle CI Go build Go Report Card GitHub release pkg.go.dev docs Twitter URL

go-app is a package to build progressive web apps (PWA) with Go programming language and WebAssembly.

It uses a declarative syntax that allows creating and dealing with HTML elements only by using Go, and without writing any HTML markup.

The package also provides an http.handler ready to serve all the required resources to run Go-based progressive web apps.

Documentation

go-app documentation

Sneak peek

Install

go-app requirements:

go mod init
go get -u github.com/maxence-charriere/go-app/v7/pkg/app

Declarative syntax

go-app uses a declarative syntax so you can write component-based UI elements just by using the Go programming language.

package main

import "github.com/maxence-charriere/go-app/v7/pkg/app"

type hello struct {
    app.Compo
    name string
}

func (h *hello) Render() app.UI {
    return app.Div().Body(
        app.Main().Body(
            app.H1().Body(
                app.Text("Hello, "),
                app.If(h.name != "",
                    app.Text(h.name),
                ).Else(
                    app.Text("World"),
                ),
            ),
            app.Input().
                Value(h.name).
                Placeholder("What is your name?").
                AutoFocus(true).
                OnChange(h.OnInputChange),
        ),
    )
}

func (h *hello) OnInputChange(ctx app.Context, e app.Event) {
    h.name = ctx.JSSrc.Get("value").String()
    h.Update()
}

func main() {
    app.Route("/", &hello{})
    app.Route("/hello", &hello{})
    app.Run()
}

The app is built with the Go build tool by specifying WebAssembly as architecture and javascript as operating system:

GOARCH=wasm GOOS=js go build -o app.wasm

Note that the build output is named app.wasm because the HTTP handler expects the wasm app to be named that way in order to serve its content.

HTTP handler

Once the wasm app is built, the next step is to serve it.

This package provides an http.Handler implementation ready to serve a PWA and all the required resources to make it work in a web browser.

The handler can be used to create either a web server or a cloud function (AWS Lambda, Google Cloud function or Azure function).

package main

import (
    "net/http"

    "github.com/maxence-charriere/go-app/v7/pkg/app"
)

func main() {
    h := &app.Handler{
        Title:  "Hello Demo",
        Author: "Maxence Charriere",
    }

    if err := http.ListenAndServe(":7777", h); err != nil {
        panic(err)
    }
}

The server is built as a standard Go program:

go build

Once the server and the wasm app built, app.wasm must be moved in the web directory, located by the side of the server binary. The web directory is where to put static resources, such as the wasm app, styles, scripts, or images.

The directory should look like as the following:

.                   # Directory root
├── hello           # Server binary
├── main.go         # Server source code
└── web             # Directory for static resources
    └── app.wasm    # Wasm binary

Then launch the hello server and open the web browser to see the result.

Demo code and live demo are available on the following links:

Contributors

Code Contributors

This project exists thanks to all the people who contribute. [Contribute].

Financial Contributors

Become a financial contributor and help us sustain go-app development. [Contribute]

Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]

Issues
  • Example crashing right away after go build or go run

    Example crashing right away after go build or go run

    I tried to just go build the example project then execute the binary, but it crashes right after it starts. The weird thing is if I use macpack to build it then it doesn't crash. I'm guessing the app wrapper provides data where the raw build does not.

    Go ENV

    GOARCH="amd64"
    GOBIN=""
    GOEXE=""
    GOHOSTARCH="amd64"
    GOHOSTOS="darwin"
    GOOS="darwin"
    GOPATH="/Users/euforic/go"
    GORACE=""
    GOROOT="/usr/local/Cellar/go/1.7.5/libexec"
    GOTOOLDIR="/usr/local/Cellar/go/1.7.5/libexec/pkg/tool/darwin_amd64"
    CC="clang"
    GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/3x/0rnfwj_x1jz2p0gzh_gg7_600000gn/T/go-build912724629=/tmp/go-build -gno-record-gcc-switches -fno-common"
    CXX="clang++"
    CGO_ENABLED="1"
    

    Crash Output

    INFO  2017/01/30 14:00:47 driver.go:31: driver *mac.Driver is loaded
    INFO  2017/01/30 14:00:47 component.go:62: main.Hello has been registered under the tag Hello
    INFO  2017/01/30 14:00:47 component.go:62: main.AppMainMenu has been registered under the tag AppMainMenu
    INFO  2017/01/30 14:00:47 component.go:62: main.WindowMenu has been registered under the tag WindowMenu
    fatal error: unexpected signal during runtime execution
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x7fffb3c24b52]
    
    runtime stack:
    runtime.throw(0x41a1554, 0x2a)
    	/usr/local/Cellar/go/1.7.5/libexec/src/runtime/panic.go:566 +0x95
    runtime.sigpanic()
    	/usr/local/Cellar/go/1.7.5/libexec/src/runtime/sigpanic_unix.go:12 +0x2cc
    
    goroutine 1 [syscall, locked to thread]:
    runtime.cgocall(0x4141160, 0xc42004df20, 0x0)
    	/usr/local/Cellar/go/1.7.5/libexec/src/runtime/cgocall.go:131 +0x110 fp=0xc42004def0 sp=0xc42004deb0
    github.com/murlokswarm/mac._Cfunc_Driver_Run()
    	??:0 +0x41 fp=0xc42004df20 sp=0xc42004def0
    github.com/murlokswarm/mac.(*Driver).Run(0xc420010280)
    	/Users/euforic/go/src/github.com/murlokswarm/mac/driver.go:56 +0x14 fp=0xc42004df28 sp=0xc42004df20
    github.com/murlokswarm/app.Run()
    	/Users/euforic/go/src/github.com/murlokswarm/app/app.go:43 +0x35 fp=0xc42004df40 sp=0xc42004df28
    main.main()
    	/Users/euforic/go/src/github.com/blevein/play/examples/mac/hello/main.go:31 +0x30 fp=0xc42004df48 sp=0xc42004df40
    runtime.main()
    	/usr/local/Cellar/go/1.7.5/libexec/src/runtime/proc.go:183 +0x1f4 fp=0xc42004dfa0 sp=0xc42004df48
    runtime.goexit()
    	/usr/local/Cellar/go/1.7.5/libexec/src/runtime/asm_amd64.s:2086 +0x1 fp=0xc42004dfa8 sp=0xc42004dfa0
    
    goroutine 17 [syscall, locked to thread]:
    runtime.goexit()
    	/usr/local/Cellar/go/1.7.5/libexec/src/runtime/asm_amd64.s:2086 +0x1
    
    goroutine 5 [chan receive]:
    github.com/murlokswarm/app.startUIScheduler()
    	/Users/euforic/go/src/github.com/murlokswarm/app/ui.go:11 +0x51
    created by github.com/murlokswarm/app.init.1
    	/Users/euforic/go/src/github.com/murlokswarm/app/ui.go:17 +0x35
    
    opened by euforic 77
  • v9 Testing/Feedback

    v9 Testing/Feedback

    Hello there,

    I know it is pretty close to the v8 release but I had been working on a new version that removes the need to call Compo.Update() to update what is displayed on the screen.

    Unfortunately, it brings some API changes and since some of you are using this package, I wanted to share that work with you and get some feedback before releasing the new version.

    Install

    go get -u -v github.com/maxence-charriere/go-app/[email protected]
    

    Then replace your imports in your code

    What changed

    • No need to call Update anymore!
    • Compo.Defer() has been removed. Now all Dispatch()/Defer() are done from Context.Dispatch(func(Context))
    • Context.Dispatch(func()) is now Context.Dispatch(func(Context)), and UI elements are always checked before launching Component lifecycle event or HTML event handlers
    • Context has now a Defer(func(Context)) method that launches the given function after a component has been updated. It can be handy to perform action after the display is complete, like scrolling to a given location
    • Context has now an After() function that launches a function on the UI goroutine after the given time without blocking the UI
    • There is a new Component lifecycle event: OnUpdate(Context): it is launched only when a component gets updated by a parent, and should replace a scenario where a Compo.Defer() call was called in a Render() app.UI method
    • Context is now an interface. Fields are now methods:
      • Src => Src()
      • JSSrc => JSSrc()
      • AppUpdateAvailable => AppUpdateAvailable()
      • Page => Page()
    • Context has now a Context.Emit(func()) method that launches the given function and notifies parents to update their state. This should be used when implementing your own event handlers in your components, in order to properly update parent components that set a function to the event handler
    • Context has now method to encrypt and decrypt data
      • Encrypt(v interface{}) ([]byte, error)
      • Decrypt(crypted []byte, v interface{}) error
    • Context has now a method that returns device id: DeviceID() string
    • [EXPERIMENTAL] Changed the API for Stack()

    API design decisions

    • This release focuses mainly on getting the usage of the package more reactive
    • The API is designed to make unrecommended things difficult. A good example would be calling a Context.Dispatch() inside a Render() method
    • With the exception of Handler and JS-related things, everything that you might need is now available from Context. Still thinking about whether I should move Compo.ResizeContent() and Compo.ValueTo in Context though

    Please let me know what you think and where it makes things difficult to solve your problem. I will try to help you solve those and/or iterate to make things better.

    @pojntfx @beanpole135 @ZackaryWelch @mar1n3r0

    Thanks

    help wanted working on it 
    opened by maxence-charriere 41
  • Next: Windows

    Next: Windows

    Hello, some stuff are happening here. I'm writing changes that will bring better support for multi-platform dev. Would like to know what is the next driver you would like to be implement.

    opened by maxence-charriere 39
  • Recaptcha

    Recaptcha

    Hello. anyone know about getting recaptcha to properly work?

    I have it setup to prompt user with a captcha after they click register like so: https://streamable.com/4if31l

    However whenever the captcha submits the form it completely ignores my form submission handler:

    func (c *register) OnCaptchaComplete(ctx app.Context, e app.Event) {
    	e.PreventDefault()
    	print("Captcha complete")
    }
    

    here is the snippet for the element

    app.Div().Class("flex justify-center").Body(
    	app.Form().
    	ID("register-captcha-form").
    	OnSubmit(c.OnCaptchaComplete, nil).
    	Body(
    	&components.Captcha{Callback: "registerCaptchaCB"}),
    	),
    ),
    

    Is there a way to easily prevent the captcha from submitting normally and passing in the response and properly send to my handler with the recaptcha response so I can use the data to build a request to the api?

    I know I can just read the query value for the g-recaptcha-response but is there anyway I can prevent recaptcha from submitting a post / get request to the form action?

    opened by ItsVoltz 31
  • How to make wasm reactive?

    How to make wasm reactive?

    The way Go treats wasm as an application means it runs and exits. This is quite limiting compared to modern JS frameworks with their reactive APIs which allow real-time reactivity on external changes. Is this even possible with the current state of things? Maybe keeping dedicated channels open infinite? This would allow all kind of things from state management stores to webhooks to websockets etc.

    Go takes a different approach, Go treats this as an application, meaning that you start a Go runtime, it runs, then exits and you can’t interact with it. This, coincidentally, is the error message that you’re seeing, our Go application has completed and cleaned up.

    To me this feels more closer to the .NET Console Application than it does to a web application. A web application doesn’t really end, at least, not in the same manner as a process.

    And this leads us to a problem, if we want to be able to call stuff, but the runtime want to shut down, what do we do?

    https://www.aaron-powell.com/posts/2019-02-06-golang-wasm-3-interacting-with-js-from-go/

    Here is an example:

    I would like to connect the app to an external state management store and make it re-render on state changes. By the time the state changed the runtime has exited, hence unless there is a channel open to keep it alive it can't react to external events after exit.

    opened by mar1n3r0 27
  • Support for push manager

    Support for push manager

    It would be nice to add subscription bootstrap into page.js (https://github.com/maxence-charriere/app/blob/c5fc1cb2b7861d05ca80284f2e38b1b7c46a69b7/internal/http/page.js)

    And callbacks to goapp.js

    opened by bgokden 26
  • Custom Handler

    Custom Handler

    It would be nice if app made it easier to work with a custom Handler. For cases like #501 , I could implement my own handler and have fine grained control.

    opened by andrewrynhard 24
  • State management

    State management

    Looking for some broad feedback here.

    I have explored further the idea of implementing component composition also known as slots as described here: https://github.com/maxence-charriere/go-app/issues/434.

    While it definitely works in terms of simply compiling the HTML string the complexity it introduced in maintaining state between the root component and children components brought some questions up.

    The simplicity of using Go structs as state management pattern is very appealing but at the same time easily shows its limitations once the app goes beyond the scope of one page - one component structure. Surely with this pattern we can communicate between pages with HTML5 API storage solutions as long as there is no sensitive data. But as long as we outgrow this pattern in-memory state management becomes inevitable.

    This was an issue which most of JS frontend frameworks faced in their early stages and it evolved in to separate global state management modules and tools like Vuex for example.

    Global state management solves all those problems when scaling from small to enterprise-like apps but comes at a cost with it's own caveats and extra stuff to think about and handle.

    What are your thoughts on this ?

    Do you think this will become a hot topic in the near future as the project evolves or it's beyond the scope of current usage requirements ?

    Do you see the same pattern applied or a different technique Go can make use of to make it possible to scale apps ?

    opened by mar1n3r0 22
  • Implements custom elements with xml namespaces (for SVG)

    Implements custom elements with xml namespaces (for SVG)

    I added namespaces to elements and added a method to create custom tags. The reason is, that I wrote an auto converter that can translate HTML to go-app declarative syntax. While experimenting with it, I found that SVG support was missing. But adding all of the SVG seems to be a lot of work. After making a simple variant for custom elements I realized that this will not work for SVG because those elements need to be in a special namespace. So I added namespace support.

    I think my implementation is quite nice. I added a test and documentation with an example of the usage:

    func SVG(w, h int) app.HTMLCustom {
    	return app.Custom("svg", false, app.SVG).
    		Attr("width", w).Attr("height", h)
    }
    
    func Circle(cx, cy, r int, stroke string, width int, fill string) app.HTMLCustom {
    	return app.Custom("circle", false, app.SVG).
    		Attr("cx", cx).
    		Attr("cy", cy).
    		Attr("r", r).
    		Attr("stroke", stroke).
    		Attr("stroke-width", width).
    		Attr("fill", fill)
    }
    
    func (c *Dynamic) Render() app.UI {
    	return SVG(100, 100).Body(
    		Circle(50, 50, 40, "black", 3, "red"),
    		app.Text("Sorry, your browser does not support inline SVG"),
    	)
    }
    

    Of course, it adds the "xmlns" field to every element. But I think this is better than having another interface for SVG.

    I am contemplating if there should be an "xmlns" method instead which sets the namespace for every element. That may be a better solution than adding the namespace to the Custom() method. Maybe I try that too and push a commit that uses this later on.

    EDIT: There is another implementation using an XMLNS() method on the elements instead of the many parameters to Custom(). See below!

    opened by oderwat 21
  • Added MFi Controller Support

    Added MFi Controller Support

    Summary

    I've written out a basic implementation for handling MFi controller events and providing Go callbacks that can be used to interact with the application.

    As per CONTRIBUTING.md:

    • use gofmt ✅
    • use govet ✅
    • use golint ✅
    • test coverage for . stay at 100% ✅
    • test coverage for ./internal/appjs stays at 100% ✅
    • test coverage for ./internal/bridge stays at 100% ✅
    • test coverage for ./internal/html stays at 100% ✅
    • test coverage for ./internal/core stays at 100% ✅
    • try to keep consistent coding style ✅
    • avoid naked returns (if you deal with a part of the code that have some, please refactor). ✅ (I tried my best at least 😄)
    • run goreportcard with your branch, everything that is not gocyclo must be 100%. ✅

    Fixes

    Using GameController/GCController.h, I setup handlers for the various buttons and dpads that comply with MFi. I've tested this with the Steelseries Nimbus and all keys register with their respective button.

    I wanted to add DOM events, but the contributing doc said not to get too crazy. Happy to add if this is good.

    Example

    This is how I've sandboxed the following changes:

    package main
    
    import (
    	"github.com/murlokswarm/app"
    	"github.com/murlokswarm/app/drivers/mac"
    )
    
    type MainWindow struct {
    	Controller app.Controller
    }
    
    func (mw *MainWindow) Config() app.HTMLConfig {
    
    	mw.Controller = app.NewController(app.ControllerConfig{
    		OnConnected: func() {
    			app.Log("OnConnected")
    		},
    		OnDisconnected: func() {
    			app.Log("OnDisconnected")
    		},
    		OnPause: func() {
    			app.Log("OnPause")
    		},
    		OnDpadChange: func(input app.ControllerInput, x float64, y float64) {
    			switch input {
    			case app.DirectionalPad:
    				app.Logf("OnDpad -- directional pad, x: %f, y: %f", x, y)
    			case app.LeftThumbstick:
    				app.Logf("OnDpad -- left thumbstick, x: %f, y: %f", x, y)
    			case app.RightThumbstick:
    				app.Logf("OnDpad -- right thumbstick, x: %f, y: %f", x, y)
    			}
    		},
    		OnButtonChange: func(input app.ControllerInput, value float64, pressed bool) {
    			switch input {
    			case app.A:
    				app.Logf("OnButtonChange -- a button, value: %f, pressed: %t", value, pressed)
    			case app.B:
    				app.Logf("OnButtonChange -- b button, value: %f, pressed: %t", value, pressed)
    			case app.X:
    				app.Logf("OnButtonChange -- x button, value: %f, pressed: %t", value, pressed)
    			case app.Y:
    				app.Logf("OnButtonChange -- y button, value: %f, pressed: %t", value, pressed)
    			case app.L1:
    				app.Logf("OnButtonChange -- l1 button, value: %f, pressed: %t", value, pressed)
    			case app.L2:
    				app.Logf("OnButtonChange -- l2 button, value: %f, pressed: %t", value, pressed)
    			case app.R1:
    				app.Logf("OnButtonChange -- r1 button, value: %f, pressed: %t", value, pressed)
    			case app.R2:
    				app.Logf("OnButtonChange -- r2 button, value: %f, pressed: %t", value, pressed)
    			}
    		},
    		OnClose: func() {
    			app.Log("OnClose")
    		},
    	})
    	// mw.Controller.Close()
    	return app.HTMLConfig{}
    }
    
    func (mw *MainWindow) Render() string {
    	return `
    	<div class="MainWindow">Example</div>
    	`
    }
    
    func init() {
    	mw := &MainWindow{}
    	app.Import(mw)
    }
    
    func main() {
    	app.Run(&mac.Driver{
    		URL: "/mainwindow",
    	})
    }
    

    edit: updated sandbox example

    opened by jamesalbert 19
  • css and javascript issues

    css and javascript issues

    possibly related to #21

    Trying to include font-awesome stylesheets, using <link href="..."> worked fine, but having the css in resources/css the icons are rendering as squares.

    Only <script>...</script>'s seem to be working. Both files in resources/js and <script src="https..."> don't seem to work.

    If there is a javascript exception thrown, I don't hear about it in the logs.

    My questions are:

    • is this expected behavior, or have these features been known to work? In that case I can provide more info.
    • are there any plans for a dev console to inspect html / debug js? That would be glorious.
    opened by jamesalbert 19
  • Checkbox not updating

    Checkbox not updating

    Take the following example:

    package main
    
    import (
    	"log"
    	"net/http"
    
    	"github.com/maxence-charriere/go-app/v9/pkg/app"
    )
    
    type hello struct {
    	app.Compo
    	Select bool
    }
    
    func (h *hello) Render() app.UI {
    	checkbox := app.Input().Type("checkbox").Class("listitem-checkbox")
    	if h.Select {
    		checkbox.Checked(true)
    	} else {
    		checkbox.Checked(false)
    	}
    	return app.Div().Body(
    		app.P().Body(
    			app.Button().Text("Click me").OnClick(func(ctx app.Context, e app.Event) {
    				h.Select = !h.Select
    			}),
    			checkbox,
    		),
    	)
    }
    
    func main() {
    	// Components routing:
    	app.Route("/", &hello{})
    	app.Route("/hello", &hello{})
    	app.RunWhenOnBrowser()
    
    	// HTTP routing:
    	http.Handle("/", &app.Handler{})
    
    	if err := http.ListenAndServe(":8000", nil); err != nil {
    		log.Fatal(err)
    	}
    }
    

    The checkbox will get checked the first time the button is clicked, but then won't change back after if you click it again.

    opened by Naatan 1
  • Disable Offline Mode

    Disable Offline Mode

    Hi, I'm using go-app for a webview application, which is using a local http server. As such offline mode is of no use to me and further - it seems to make live reload functionality unreliable.

    Is there any way I can disable offline mode?

    Thanks.

    opened by Naatan 1
  • Panic with bootstrap table

    Panic with bootstrap table

    I want to make a collapsing navigation list with a bootstrap table in the panel. It works, but I get an error after a certain number of navigation operations. If without table, there is no error. I have written a test example. Please help me to understand it.

    package main
    
    import (
    	"log"
    	"math/rand"
    	"net/http"
    	"strconv"
    	"strings"
    
    	"github.com/maxence-charriere/go-app/v9/pkg/app"
    )
    
    // navlist component
    type navlist struct {
    	app.Compo
    	items map[string]string
    
    	folder string
    	item   string
    }
    
    func (c *navlist) OnNav(ctx app.Context) {
    	path := ctx.Page().URL().Path
    	switch {
    	case strings.HasPrefix(path, "/folder/"):
    		c.folder = strings.TrimPrefix(path, "/folder/")
    	case strings.HasPrefix(path, "/items/"):
    		c.folder = strings.TrimPrefix(path, "/items/")
    	}
    	c.item = ctx.Page().URL().Fragment
    
    	c.items = map[string]string{
    		"0:0": "/",
    		"1:0": "/folder/1", "1:1": "/items/1#1", "1:2": "/items/1#2", "1:3": "/items/1#3", "1:4": "/items/1#4",
    		"2:0": "/folder/2", "2:1": "/items/2#1", "2:2": "/items/2#2", "2:3": "/items/2#3",
    	}
    }
    
    func (c *navlist) Render() app.UI {
    	return app.Ul().Body(
    		app.Range(c.items).Map(func(k string) app.UI {
    			a := strings.Split(k, ":")
    			if c.folder != a[0] && a[1] != "0" {
    				return app.Text("")
    			}
    			return app.Li().Body(app.A().Href(c.items[k]).Text(c.items[k]))
    		}),
    	)
    }
    
    // folder component
    type folder struct {
    	app.Compo
    	name string
    }
    
    func (c *folder) OnNav(ctx app.Context) {
    	c.name = ctx.Page().URL().Path
    }
    
    func (c *folder) Render() app.UI {
    	return app.Div().Body(
    		&navlist{},
    		app.H1().Text("Folder:"+c.name),
    		&table{Data: tableData()},
    	)
    }
    
    // item component
    type item struct {
    	app.Compo
    	folder string
    	num    string
    }
    
    func (c *item) OnNav(ctx app.Context) {
    	c.folder = strings.TrimPrefix(ctx.Page().URL().Path, "/items/")
    	c.num = ctx.Page().URL().Fragment
    }
    
    func (c *item) Render() app.UI {
    	return app.Div().Body(
    		&navlist{},
    		app.H1().Text("Item folder:"+c.folder),
    		app.H2().Text("Item name:"+c.num),
    		&table{Data: tableData()},
    	)
    }
    
    // table component
    type table struct {
    	app.Compo
    	Data [][]int
    }
    
    func (c *table) Render() app.UI {
    	return app.Div().Body(
    		app.Table().
    			Attr("data-toggle", "table").Attr("data-search", "true").
    			Body(
    				app.THead().Body(
    					app.Tr().Body(
    						app.Th().Text("ID"), app.Th().Text("Value"),
    					),
    				),
    				app.TBody().Body(
    					app.Range(c.Data).Slice(func(i int) app.UI {
    						return app.Tr().Body(
    							app.Range(c.Data[i]).Slice(func(j int) app.UI {
    								return app.Td().Body(app.Text(strconv.Itoa(c.Data[i][j])))
    							}),
    						)
    					}),
    				),
    			),
    
    		app.Script().Src("https://unpkg.com/[email protected]/dist/bootstrap-table.min.js"),
    	)
    }
    
    // home component
    type home struct {
    	app.Compo
    }
    
    func (c *home) Render() app.UI {
    	return app.Div().Body(
    		&navlist{},
    		app.H1().Text("Home"),
    	)
    }
    
    func main() {
    	app.Route("/", &home{})
    	app.RouteWithRegexp("/folder.*", &folder{})
    	app.RouteWithRegexp("/items.*", &item{})
    	app.RunWhenOnBrowser()
    
    	http.Handle("/", &app.Handler{
    		Name:        "Hello",
    		Description: "An Hello World! example",
    		Scripts: []string{
    			"https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.slim.min.js",
    			"https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js",
    		},
    		Styles: []string{
    			"https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css",
    			"https://unpkg.com/[email protected]/dist/bootstrap-table.min.css",
    		},
    	})
    
    	if err := http.ListenAndServe(":8123", nil); err != nil {
    		log.Fatal(err)
    	}
    }
    
    func tableData() [][]int {
    	var data [][]int
    	for i := 0; i < rand.Intn(10); i++ {
    		data = append(data, []int{i, rand.Intn(100)})
    	}
    	return data
    }
    

    Error:

    panic: JavaScript error: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. [recovered]
    wasm_exec.js:22 	panic: JavaScript error: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
    wasm_exec.js:22 
    wasm_exec.js:22 goroutine 1 [running]:
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.RunWhenOnBrowser.func1()
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/app.go:108 +0x4
    wasm_exec.js:22 panic({0xb5da0, 0x9c4d90})
    wasm_exec.js:22 	/opt/homebrew/Cellar/go/1.18.3/libexec/src/runtime/panic.go:844 +0x33
    wasm_exec.js:22 syscall/js.Value.Call({{}, 0x7ff8000100000553, 0x9a9d18}, {0xd4a83, 0xb}, {0x8780a0, 0x1, 0x1})
    wasm_exec.js:22 	/opt/homebrew/Cellar/go/1.18.3/libexec/src/syscall/js/js.go:389 +0x31
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.value.Call({{{}, 0x7ff8000100000553, 0x9a9d18}}, {0xd4a83, 0xb}, {0x8780a0, 0x1, 0x1})
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/js_wasm.go:21 +0x7
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.value.removeChild(...)
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/js_wasm.go:103
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.(*elem).removeChildAt(0x9b7180, 0x1)
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/element.go:275 +0x24
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.(*elem).update(0x9b7180, {0xf6b6a8, 0x9c3d60})
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/element.go:189 +0x51
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.update({0xf6b6a8, 0x9b7180}, {0xf6b6a8, 0x9c3d60})
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/node.go:199 +0x8
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.(*elem).update(0x9b6e60, {0xf6b778, 0x9c3a40})
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/element.go:170 +0x38
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.update({0xf6b778, 0x9b6e60}, {0xf6b778, 0x9c3a40})
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/node.go:199 +0x8
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.(*elem).update(0x9b6dc0, {0x15c0b0, 0x9c39a0})
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/element.go:170 +0x38
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.update({0x15c0b0, 0x9b6dc0}, {0x15c0b0, 0x9c39a0})
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/node.go:199 +0x8
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.(*Compo).updateRoot(0x8b3ea0)
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/component.go:386 +0x5
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.(*Compo).update(0x8b3ea0, {0x15c3f0, 0x8b3f80})
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/component.go:369 +0xc9
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.update({0x15c3f0, 0x8b3ea0}, {0x15c3f0, 0x8b3f80})
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/node.go:199 +0x8
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.(*elem).update(0x9b6b40, {0x15c0b0, 0x9c37c0})
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/element.go:170 +0x38
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.update({0x15c0b0, 0x9b6b40}, {0x15c0b0, 0x9c37c0})
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/node.go:199 +0x8
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.(*Compo).updateRoot(0x980b80)
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/component.go:386 +0x5
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.(*engine).updateComponents(0x87e480)
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/engine.go:419 +0x18
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.(*engine).start.func1()
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/engine.go:347 +0x1c
    wasm_exec.js:22 sync.(*Once).doSlow(0x87e4ec, 0x879a50)
    wasm_exec.js:22 	/opt/homebrew/Cellar/go/1.18.3/libexec/src/sync/once.go:68 +0x9
    wasm_exec.js:22 sync.(*Once).Do(0x87e4ec, 0x879a50)
    wasm_exec.js:22 	/opt/homebrew/Cellar/go/1.18.3/libexec/src/sync/once.go:59 +0x6
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.(*engine).start(0x87e480, {0x15a9d0, 0x81e040})
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/engine.go:323 +0x4
    wasm_exec.js:22 github.com/maxence-charriere/go-app/v9/pkg/app.RunWhenOnBrowser()
    wasm_exec.js:22 	/Users/moreshnikov/go/pkg/mod/github.com/maxence-charriere/go-app/[email protected]/pkg/app/app.go:156 +0x9d
    wasm_exec.js:22 main.main()
    wasm_exec.js:22 	/Users/moreshnikov/work/projects/examples/wasm/go-app-issue2/main.go:137 +0x11
    wasm_exec.js:101 exit code: 2
    exit @ wasm_exec.js:101
    runtime.wasmExit @ wasm_exec.js:225
    $runtime.wasmExit @ app.wasm:0xe9f7c
    $runtime.exit @ app.wasm:0xe9daa
    $runtime.fatalpanic.func2 @ app.wasm:0x83b54
    $runtime.systemstack @ app.wasm:0xe7317
    $runtime.fatalpanic @ app.wasm:0x83cb6
    $runtime.gopanic @ app.wasm:0x8265b
    $github.com_maxence_charriere_go_app_v9_pkg_app.RunWhenOnBrowser.func1 @ app.wasm:0x72a312
    $runtime.gopanic @ app.wasm:0x82318
    $wasm_pc_f_loop @ app.wasm:0xe9d63
    $wasm_export_resume @ app.wasm:0xe9d44
    _resume @ wasm_exec.js:538
    (anonymous) @ wasm_exec.js:264
    setTimeout (async)
    runtime.scheduleTimeoutEvent @ wasm_exec.js:262
    $runtime.scheduleTimeoutEvent @ app.wasm:0xe9ff4
    $runtime.beforeIdle @ app.wasm:0x20125
    $runtime.findrunnable @ app.wasm:0x95c20
    $runtime.schedule @ app.wasm:0x99552
    $runtime.park_m @ app.wasm:0x9a5a3
    $runtime.mcall @ app.wasm:0xe7218
    $runtime.gopark @ app.wasm:0x890ce
    $runtime.selectgo @ app.wasm:0xadb09
    $github.com_maxence_charriere_go_app_v9_pkg_app.__engine_.start.func1 @ app.wasm:0x752bf4
    $wasm_pc_f_loop @ app.wasm:0xe9d63
    $wasm_export_resume @ app.wasm:0xe9d44
    _resume @ wasm_exec.js:538
    (anonymous) @ wasm_exec.js:549
    
    opened by maxim0r 2
  • Give the option to not defer scripts

    Give the option to not defer scripts

    I'm really liking the package, thank you for your work!

    As noted in #661 app.Handler.Scripts sets defer=true, but this causes problems when components depend on them.

    For instance my material components implementation instantiates the Javascript classes in OnMount, but sometimes that gets called before the mdc script has actually loaded.

    Putting a non-deferred <script> tag in RawHeaders appears to work OK, but it would be nice to have a way to choose deferred vs non-deferred loading for Scripts somehow.

    opened by usedbytes 2
  • github.com/maxence-charriere/go-app/v9/pkg/app Not found

    github.com/maxence-charriere/go-app/v9/pkg/app Not found

    VS code not finding github.com/maxence-charriere/go-app/v9/pkg/app in $ go-app package not find added package GOPATH even though it installed successfully via command prompt. I ave tried different ways do not seem to figure out what the problem is. NB: I am new to go and thought it might be interesting to work with go-app TIA

    opened by bridoux 0
Releases(v9.5.1)
  • v9.5.1(May 10, 2022)

  • v9.5.0(May 10, 2022)

  • v9.4.1(Mar 26, 2022)

  • v9.3.0(Dec 27, 2021)

  • v9.2.1(Nov 8, 2021)

  • v9.2.0(Nov 3, 2021)

  • v9.1.2(Oct 25, 2021)

  • v9.1.1(Oct 18, 2021)

  • v9.0.0(Sep 22, 2021)

  • v8.0.4(Jun 20, 2021)

  • v8.0.3(Jun 20, 2021)

  • v8.0.2(Apr 20, 2021)

  • v8.0.1(Mar 30, 2021)

  • v8.0.0(Mar 25, 2021)

  • v7.3.0(Feb 19, 2021)

    A scope can be added to event handler in order to trigger event handler updates.

    In a scenario where we want to remove a element from a list, a solution is to create an EventHandler which got the element id set outside. The event handler returned will always have the same addr, which prevent the package to define that an update should be done on the given element.

    To solve this, a scope has been added to the methods that set EventHandler. Here is the an example:

    type issue499Data struct {
    	ID    int
    	Value string
    }
    
    type issue499 struct {
    	app.Compo
    
    	data []issue499Data
    }
    
    func newIssue499Data() *issue499 {
    	return &issue499{}
    }
    
    func (c *issue499) OnMount(app.Context) {
    	c.data = []issue499Data{
    		{11, "one"},
    		{22, "two"},
    		{33, "three"},
    		{44, "four"},
    		{55, "five"},
    		{66, "six"},
    		{77, "sever"},
    		{88, "eight"},
    		{99, "nine"},
    	}
    	c.Update()
    }
    
    func (c *issue499) Render() app.UI {
    	return app.Div().Body(
    		app.H1().Text("Issue 499"),
    		app.Div().
    			Body(
    				app.Range(c.data).Slice(func(i int) app.UI {
    					d := c.data[i]
    					return app.Button().
    						ID(fmt.Sprintf("elem-%v", d.ID)).
    						OnClick(c.newListener(d.ID), d.ID). // HERE the element ID is added in order to trigger the handler update since the func pointer returned by newListener is always the same.
    						Text(d.Value)
    				}),
    			),
    	)
    }
    
    func (c *issue499) newListener(id int) app.EventHandler {
    	return func(app.Context, app.Event) {
    		for i, d := range c.data {
    			if id == d.ID {
    				c.data = append(c.data[:i], c.data[i+1:]...)
    				c.Update()
    				return
    			}
    		}
    	}
    }
    
    Source code(tar.gz)
    Source code(zip)
  • v7.2.0(Jan 8, 2021)

    Hello there,

    This release brings a way to be notified that that app window size changed within components. It can be done by implementing the Resizer interface:

    type myCompo struct{
        app.Compo
    }
    
    func (c *myCompo) OnAppResize(ctx app.Context) {
        // Handle app resizing.
    }
    

    It can be quite a handful when building layout components. Shell and Flow has been refactored and now uses this mechanism.

    Source code(tar.gz)
    Source code(zip)
  • v7.1.2(Jan 5, 2021)

  • v7.1.1(Jan 5, 2021)

    Hello, Small patch today:

    • Fixed a bug that prevented _bank navigation for paths within the app host
    • Context now have a variable that reports whether the app has been updated in the background
    Source code(tar.gz)
    Source code(zip)
  • v7.1.0(Jan 4, 2021)

    Hello there,

    This version introduces the Updater interface. It allows a component to be notified that the app has been updated.

    This will allow notifying your users that the app has been updated and modifications are available by reloading the page.

    Check the Lifecycle article in the doc to see how to use it.

    Source code(tar.gz)
    Source code(zip)
  • v7.0.7(Dec 29, 2020)

  • v7.0.6(Nov 26, 2020)

    Hello there. Big update today since this release introduces a dogfooded documentation for the package:

    go-app documentation

    Next to this here are the fixes:

    • Iframe attributes have been added
    • Fixed crash when using Javascript function New and Call with interface{}
    • manifest.json has been renamed manifest.webmanifest in order to follow Google PWA guidelines

    This release also introduces some experimental widgets that are layout related:

    Those widgets are used in the documentation introduced above. Since they are experimental, their API may change.

    Source code(tar.gz)
    Source code(zip)
  • v7.0.5(Aug 25, 2020)

    Hello,

    This version provides the following changes:

    • Fixed a bug where boolean attributes were not updated
    • Min() and Max() HTML element functions now take an interface{} argument rather than string
    Source code(tar.gz)
    Source code(zip)
  • v7.0.4(Aug 12, 2020)

    Hello,

    Here is a small release that brings the following:

    • The handler now proxy an ads.txt file that is located at the root of the static files directory. This allows complying with AdSense requirements.
    • app.Input().Step(v int) has been changed to app.Input().Step(v float64) since step can be a decimal value.
    Source code(tar.gz)
    Source code(zip)
  • v7.0.3(Jul 11, 2020)

    Hello,

    Here is v7.0.3 which introduces the following changes:

    • Fixed bug that did not remove event handlers when components were dismounted
    • Refactored how scripts are loaded in the Handler by using defer attribute
    • Added Iframe missing attributes
    • Generated GitHub pages are now installable with Chrome
    # How to update:
    go get -u github.com/maxence-charriere/go-app/v7
    
    Source code(tar.gz)
    Source code(zip)
  • v7.0.2(Jun 28, 2020)

    Hello there, I'm thrilled to release version 7 of go-app.

    This version is focused on internal improvements that improve code quality and maintainability. Unfortunately, those improvements bring a few small changes in the API, which is why there is a version bump.

    What is new?

    • Context that is bound to a UI element lifecycle. The context can be used with functions that accept context.Context. Contexts are canceled when the UI element they are bound with is dismounted. It is passed in Mounter and Navigator interfaces, and event handlers.

    • TestMatch: testing api to unit test specific UI element:

      tree := app.Div().Body(
          app.H2().Body(
              app.Text("foo"),
          ),
          app.P().Body(
              app.Text("bar"),
          ),
      )
      
      // Testing root:
      err := app.TestMatch(tree, app.TestUIDescriptor{
          Path:     TestPath(),
          Expected: app.Div(),
      })
      // OK => err == nil
      
      // Testing h2:
      err := app.TestMatch(tree, app.TestUIDescriptor{
          Path:     TestPath(0),
          Expected: app.H3(),
      })
      // KO => err != nil because we ask h2 to match with h3
      
      // Testing text from p:
      err = app.TestMatch(tree, app.TestUIDescriptor{
          Path:     TestPath(1, 0),
          Expected: app.Text("bar"),
      })
      // OK => err == nil
      
    • Support for aria HTML element property: Fix #419

      app.Div().
          Aria("foo", "bar").
          Text("foobar")
      
    • A default logger can be set

    • go-app PWA can now be deployed a GitHub Page with the help of GenerateStaticWebsite function:

      package main
      
      import (
      	  "fmt"
      
      	  "github.com/maxence-charriere/go-app/v7/pkg/app"
      )
      
      func main() {
      	  err := app.GenerateStaticWebsite("DST_DIR", &app.Handler{
      		  Name:      "Github Pages Hello",
      		  Title:     "Github Pages Hello",
      		  Resources: app.GitHubPages("goapp-github-pages"),
      	  })
      
      	  if err != nil {
      		  fmt.Println(err)
      		  return
      	  }
      
      	  fmt.Println("static website generated")
      }
      

      It generates files that can directly be dropped into a GitHub repository to serve the PWA. See live example: https://maxence-charriere.github.io/goapp-github-pages/

    • Support for robots.txt: A robots file is now served by the Handler when /web/robots.txt exists

    • Internal code refactored to be more maintainable

    • Serving static resources can now be customized by implementing the ResourceProvider interface

    How to migrate from v6 to v7

    go-app v7 mainly introduced internal improvements. Despite trying to keep the API the same as v6, those changes resulted in API minor modifications that break compatibility.

    Here is a guide on how to adapt v6 code to make it work with v7.

    Imports

    Go import instructions using v6:

    import (
        "github.com/maxence-charriere/go-app/v6/pkg/app"
    )
    

    must be changed to v7:

    import (
        "github.com/maxence-charriere/go-app/v7/pkg/app"
    )
    
    

    http.Handler

    • RootDir field has been removed. Handler now uses the Resources field to define where app resources and static resources are located. This has a default value that is retro compatible with local static resources. If static resources are located on a remote bucket, use the following:

      app.Handler{
          Resources: app.RemoteBucket("BUCKET_URL"),
      }
      
    • UseMinimalDefaultStyles field has been removed. go-app now use CSS styles that only apply to the loading screen and context menus. The former default styles have been removed since they could conflict with some CSS frameworks.

    Component interfaces

    Some interfaces to deals with the lifecycle of components have been modified to take a context as the first argument. Component with methods that satisfies the following interfaces must be updated:

    • Mounter: OnMount() become OnMount(ctx app.Context)
    • Navigator: OnNav(u *url.URL) become OnNav(ctx app.Context u *url.URL)

    Event handlers

    EventHandler function signature has been also modified to take a context as the first argument:

    • func(src app.Value, e app.Event) become func(ctx app.Context, e app.Event)

    • Source is now accessed from the context:

      // Event handler that retrieve input value when onchange is fired:
      func (c *myCompo) OnChange(ctx app.Context, e app.Event) {
          v := ctx.JSSrc().Get("value")
      }
      
    Source code(tar.gz)
    Source code(zip)
  • v6.6.2(Jun 23, 2020)

  • v6.6.1(Jun 10, 2020)

  • v6.6.0(Jun 10, 2020)

    Summary

    • Fixes a crash that occurred when a component was the root of another component
    • Indirect is now exported
    • The Updatable interface has been removed: code will compile but OnUpdate functions are not called anymore. This is removed because I felt it complicate components life cycle and was buggy.
    Source code(tar.gz)
    Source code(zip)
  • v6.5.0(May 15, 2020)

    Hello, I'm glad to release the version 6.5.0.

    Clean body

    go-app now provides the function CleanBody. This is to remove extra elements in the body that might be added by third-party libraries.

    While building an app, I ran into a problem where Amazon advertisements where popping into the screen when navigating fast to other pages. The problem was that Amazon is loading a script to display their ads. When the loaded code does not find the ad container (when a component is dismounted), it inserts the ads directly in the body.

    This is to prevent this kind of behavior from third-party libraries.

    Bug Fixes

    Some code has been refactored and hash navigation (eg. http://my.domain/hello#Input) now works properly.

    Source code(tar.gz)
    Source code(zip)
  • v6.4.1(May 8, 2020)

    Hello there, This version brings some stability improvements:

    • nil values are now ignored when they are put in an element body
    • Component updates are now properly skipped when a component is dismounted or when the update is done from the same pointer
    • OnNav from the Navigator interface is now propagated to all components
    Source code(tar.gz)
    Source code(zip)
Owner
Maxence Charriere
Maxence Charriere
UIKit - A declarative, reactive GUI toolkit for build cross platform apps with web technology with single codebase

UIKit - A declarative, reactive GUI toolkit for build cross platform apps with web technology with single codebase

zhuah 106 Apr 18, 2022
Build cross platform GUI apps with GO and HTML/JS/CSS (powered by Electron)

Thanks to go-astilectron build cross platform GUI apps with GO and HTML/JS/CSS. It is the official GO bindings of astilectron and is powered by Electr

Quentin Renard 4.4k Jun 23, 2022
Build cross platform GUI apps with GO and HTML/JS/CSS (powered by nwjs)

gowd Build cross platform GUI apps with GO and HTML/JS/CSS (powered by nwjs) How to use this library: Download and install nwjs Install this library g

Danny 361 Jun 22, 2022
Build cross-platform modern desktop apps in Go + HTML5

Lorca A very small library to build modern HTML5 desktop apps in Go. It uses Chrome browser as a UI layer. Unlike Electron it doesn't bundle Chrome in

Serge Zaitsev 7.3k Jun 29, 2022
Qt binding for Go (Golang) with support for Windows / macOS / Linux / FreeBSD / Android / iOS / Sailfish OS / Raspberry Pi / AsteroidOS / Ubuntu Touch / JavaScript / WebAssembly

Introduction Qt is a free and open-source widget toolkit for creating graphical user interfaces as well as cross-platform applications that run on var

null 9.3k Jun 27, 2022
Create desktop apps using Go and Web Technologies.

Build desktop applications using Go & Web Technologies. The traditional method of providing web interfaces to Go programs is via a built-in web server

Wails 7.7k Jun 26, 2022
A Windows GUI toolkit for the Go Programming Language

About Walk Walk is a "Windows Application Library Kit" for the Go Programming Language. Its primarily useful for Desktop GUI development, but there is

Alexander Neumann 6.1k Jun 24, 2022
Common library for Go GUI apps on Windows

winc Common library for Go GUI apps on Windows. It is for Windows OS only. This makes library smaller than some other UI libraries for Go.

Tad Vizbaras 137 Jun 9, 2022
An example desktop system tray application that can launch HTML5 windows. Go source with a build process for Windows, Mac and Linux.

ExampleTrayGUI An example cross-platform (Mac, Windows, Linux) system tray application that can launch HTML5 windows, developed in Go including functi

Grant Moore 48 Jun 20, 2022
An example desktop system tray application that can launch HTML5 windows. Go source with a build process for Windows, Mac and Linux.

ExampleTrayGUI An example cross-platform (Mac, Windows, Linux) system tray application that can launch HTML5 windows, developed in Go including functi

Grant Moore 48 Jun 20, 2022
An example desktop system tray application that can launch HTML5 windows. Go source with a build process for Windows, Mac and Linux.

An example cross-platform (Mac, Windows, Linux) system tray application that can launch HTML5 windows, developed in Go including functional build process. This repository is intended as a quick reference to help others start similar projects using the referenced libraries and will not be actively maintained.

Grant Moore 48 Jun 20, 2022
An example desktop system tray application that can launch HTML5 windows. Go source with a build process for Windows, Mac and Linux.

ExampleTrayGUI An example cross-platform (Mac, Windows, Linux) system tray application that can launch HTML5 windows, developed in Go including functi

Owen Moore 48 Jun 20, 2022
Wmi - One hot Go WMI package. Package wmi provides an interface to WMI. (Windows Management Instrumentation)

wmi Package wmi provides an interface to WMI. (Windows Management Instrumentation) Install go get -v github.com/moonchant12/wmi Import import "github.

MoonChant 2 Apr 22, 2022
QML support for the Go language

QML support for the Go language Documentation The introductory documentation as well as the detailed API documentation is available at gopkg.in/qml.v1

null 2k Jun 20, 2022
Go Web UI Toolkit - Public Releases and Development

Welcome! Gowut (Go Web UI Toolkit) is a full-featured, easy to use, platform independent Web UI Toolkit written in pure Go, no platform dependent nati

András Belicza 280 Jun 23, 2022
Pglet - Web UI framework for backend developers

Pglet - Web UI framework for backend developers

Web UI framework for backend developers 569 Jun 22, 2022
Pacz - Arch Linux package searcher with fzf-like UI

pacz pacz is an Arch Linux fuzzy searcher with fzf-like UI. This repository is s

sheepla 2 Apr 3, 2022
Flutter on Windows, MacOS and Linux - based on Flutter Embedding, Go and GLFW.

go-flutter - A package that brings Flutter to the desktop Purpose Flutter allows you to build beautiful native apps on iOS and Android from a single c

null 5.3k Jun 28, 2022
Slice and dice your TMUX windows and panes

Chaakoo is a wrapper over TMUX that can create sessions, windows and panes from a grid based layout. The idea here is inspired by the CSS grid template areas.

Pallav Jha 83 May 9, 2022