Build cross platform GUI apps with GO and HTML/JS/CSS (powered by Electron)

GoReportCard GoDoc Travis Coveralls

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 Electron.

Demo

To see a minimal Astilectron app, checkout out the demo.

It uses the bootstrap and the bundler.

If you're looking for a minimalistic example, run go run example/main.go -v.

Real-life examples

Here's a list of awesome projects using go-astilectron (if you're using go-astilectron and want your project to be listed here please submit a PR):

  • go-astivid Video tools written in GO
  • GroupMatcher Program to allocate persons to groups while trying to fulfill all the given wishes as good as possible
  • ipeye-onvif ONVIF Search Tool
  • Stellite GUI Miner An easy to use GUI cryptocurrency miner for Stellite

Bootstrap

For convenience purposes, a bootstrap has been implemented.

The bootstrap allows you to quickly create a one-window application.

There's no obligation to use it, but it's strongly recommended.

If you decide to use it, read thoroughly the documentation as you'll have to structure your project in a specific way.

Bundler

Still for convenience purposes, a bundler has been implemented.

The bundler allows you to bundle your app for every os/arch combinations and get a nice set of files to send your users.

Quick start

WARNING: the code below doesn't handle errors for readibility purposes. However you SHOULD!

Import go-astilectron

To import go-astilectron run:

$ go get -u github.com/asticode/go-astilectron

Start go-astilectron

// Initialize astilectron
var a, _ = astilectron.New(log.New(os.Stderr, "", 0), astilectron.Options{
    AppName: "<your app name>",
    AppIconDefaultPath: "<your .png icon>", // If path is relative, it must be relative to the data directory
    AppIconDarwinPath:  "<your .icns icon>", // Same here
    BaseDirectoryPath: "<where you want the provisioner to install the dependencies>",
    VersionAstilectron: "<version of Astilectron to utilize such as `0.33.0`>",
    VersionElectron: "<version of Electron to utilize such as `4.0.1` | `6.1.2`>",
})
defer a.Close()

// Start astilectron
a.Start()

// Blocking pattern
a.Wait()

For everything to work properly we need to fetch 2 dependencies : astilectron and Electron. .Start() takes care of it by downloading the sources and setting them up properly.

In case you want to embed the sources in the binary to keep a unique binary you can use the NewDisembedderProvisioner function to get the proper Provisioner and attach it to go-astilectron with .SetProvisioner(p Provisioner). Or you can use the bootstrap and the bundler. Check out the demo to see how to use them.

Beware when trying to add your own app icon as you'll need 2 icons : one compatible with MacOSX (.icns) and one compatible with the rest (.png for instance).

If no BaseDirectoryPath is provided, it defaults to the executable's directory path.

The majority of methods are asynchronous which means that when executing them go-astilectron will block until it receives a specific Electron event or until the overall context is cancelled. This is the case of .Start() which will block until it receives the app.event.ready astilectron event or until the overall context is cancelled.

HTML paths

NB! All paths in HTML (and Javascript) must be relative, otherwise the files will not be found. To make this happen in React for example, just set the homepage property of your package.json to "./".

{ "homepage": "./" }

Create a window

// Create a new window
var w, _ = a.NewWindow("http://127.0.0.1:4000", &astilectron.WindowOptions{
    Center: astikit.BoolPtr(true),
    Height: astikit.IntPtr(600),
    Width:  astikit.IntPtr(600),
})
w.Create()

When creating a window you need to indicate a URL as well as options such as position, size, etc.

This is pretty straightforward except the astilectron.Ptr* methods so let me explain: GO doesn't do optional fields when json encoding unless you use pointers whereas Electron does handle optional fields. Therefore I added helper methods to convert int, bool and string into pointers and used pointers in structs sent to Electron.

Open the dev tools

When developing in JS, it's very convenient to debug your code using the browser window's dev tools:

// Open dev tools
w.OpenDevTools()

// Close dev tools
w.CloseDevTools()

Add listeners

// Add a listener on Astilectron
a.On(astilectron.EventNameAppCrash, func(e astilectron.Event) (deleteListener bool) {
    log.Println("App has crashed")
    return
})

// Add a listener on the window
w.On(astilectron.EventNameWindowEventResize, func(e astilectron.Event) (deleteListener bool) {
    log.Println("Window resized")
    return
})

Nothing much to say here either except that you can add listeners to Astilectron as well.

Play with the window

// Play with the window
w.Resize(200, 200)
time.Sleep(time.Second)
w.Maximize()

Check out the Window doc for a list of all exported methods

Send messages from GO to Javascript

Javascript

// This will wait for the astilectron namespace to be ready
document.addEventListener('astilectron-ready', function() {
    // This will listen to messages sent by GO
    astilectron.onMessage(function(message) {
        // Process message
        if (message === "hello") {
            return "world";
        }
    });
})

GO

// This will send a message and execute a callback
// Callbacks are optional
w.SendMessage("hello", func(m *astilectron.EventMessage) {
        // Unmarshal
        var s string
        m.Unmarshal(&s)

        // Process message
        log.Printf("received %s\n", s)
})

This will print received world in the GO output

Send messages from Javascript to GO

GO

// This will listen to messages sent by Javascript
w.OnMessage(func(m *astilectron.EventMessage) interface{} {
        // Unmarshal
        var s string
        m.Unmarshal(&s)

        // Process message
        if s == "hello" {
                return "world"
        }
        return nil
})

Javascript

// This will wait for the astilectron namespace to be ready
document.addEventListener('astilectron-ready', function() {
    // This will send a message to GO
    astilectron.sendMessage("hello", function(message) {
        console.log("received " + message)
    });
})

This will print "received world" in the Javascript output

Play with the window's session

// Clear window's HTTP cache
w.Session.ClearCache()

Handle several screens/displays

// If several displays, move the window to the second display
var displays = a.Displays()
if len(displays) > 1 {
    time.Sleep(time.Second)
    w.MoveInDisplay(displays[1], 50, 50)
}

Menus

// Init a new app menu
// You can do the same thing with a window
var m = a.NewMenu([]*astilectron.MenuItemOptions{
    {
        Label: astikit.StrPtr("Separator"),
        SubMenu: []*astilectron.MenuItemOptions{
            {Label: astikit.StrPtr("Normal 1")},
            {
                Label: astikit.StrPtr("Normal 2"),
                OnClick: func(e astilectron.Event) (deleteListener bool) {
                    log.Println("Normal 2 item has been clicked")
                    return
                },
            },
            {Type: astilectron.MenuItemTypeSeparator},
            {Label: astikit.StrPtr("Normal 3")},
        },
    },
    {
        Label: astikit.StrPtr("Checkbox"),
        SubMenu: []*astilectron.MenuItemOptions{
            {Checked: astikit.BoolPtr(true), Label: astikit.StrPtr("Checkbox 1"), Type: astilectron.MenuItemTypeCheckbox},
            {Label: astikit.StrPtr("Checkbox 2"), Type: astilectron.MenuItemTypeCheckbox},
            {Label: astikit.StrPtr("Checkbox 3"), Type: astilectron.MenuItemTypeCheckbox},
        },
    },
    {
        Label: astikit.StrPtr("Radio"),
        SubMenu: []*astilectron.MenuItemOptions{
            {Checked: astikit.BoolPtr(true), Label: astikit.StrPtr("Radio 1"), Type: astilectron.MenuItemTypeRadio},
            {Label: astikit.StrPtr("Radio 2"), Type: astilectron.MenuItemTypeRadio},
            {Label: astikit.StrPtr("Radio 3"), Type: astilectron.MenuItemTypeRadio},
        },
    },
    {
        Label: astikit.StrPtr("Roles"),
        SubMenu: []*astilectron.MenuItemOptions{
            {Label: astikit.StrPtr("Minimize"), Role: astilectron.MenuItemRoleMinimize},
            {Label: astikit.StrPtr("Close"), Role: astilectron.MenuItemRoleClose},
        },
    },
})

// Retrieve a menu item
// This will retrieve the "Checkbox 1" item
mi, _ := m.Item(1, 0)

// Add listener manually
// An OnClick listener has already been added in the options directly for another menu item
mi.On(astilectron.EventNameMenuItemEventClicked, func(e astilectron.Event) bool {
    log.Printf("Menu item has been clicked. 'Checked' status is now %t\n", *e.MenuItemOptions.Checked)
    return false
})

// Create the menu
m.Create()

// Manipulate a menu item
mi.SetChecked(true)

// Init a new menu item
var ni = m.NewItem(&astilectron.MenuItemOptions{
    Label: astikit.StrPtr("Inserted"),
    SubMenu: []*astilectron.MenuItemOptions{
        {Label: astikit.StrPtr("Inserted 1")},
        {Label: astikit.StrPtr("Inserted 2")},
    },
})

// Insert the menu item at position "1"
m.Insert(1, ni)

// Fetch a sub menu
s, _ := m.SubMenu(0)

// Init a new menu item
ni = s.NewItem(&astilectron.MenuItemOptions{
    Label: astikit.StrPtr("Appended"),
    SubMenu: []*astilectron.MenuItemOptions{
        {Label: astikit.StrPtr("Appended 1")},
        {Label: astikit.StrPtr("Appended 2")},
    },
})

// Append menu item dynamically
s.Append(ni)

// Pop up sub menu as a context menu
s.Popup(&astilectron.MenuPopupOptions{PositionOptions: astilectron.PositionOptions{X: astikit.IntPtr(50), Y: astikit.IntPtr(50)}})

// Close popup
s.ClosePopup()

// Destroy the menu
m.Destroy()

A few things to know:

  • when assigning a role to a menu item, go-astilectron won't be able to capture its click event
  • on MacOS there's no such thing as a window menu, only app menus therefore my advice is to stick to one global app menu instead of creating separate window menus

Tray

// New tray
var t = a.NewTray(&astilectron.TrayOptions{
    Image:   astikit.StrPtr("/path/to/image.png"),
    Tooltip: astikit.StrPtr("Tray's tooltip"),
})

// Create tray
t.Create()

// New tray menu
var m = t.NewMenu([]*astilectron.MenuItemOptions{
    {
        Label: astikit.StrPtr("Root 1"),
        SubMenu: []*astilectron.MenuItemOptions{
            {Label: astikit.StrPtr("Item 1")},
            {Label: astikit.StrPtr("Item 2")},
            {Type: astilectron.MenuItemTypeSeparator},
            {Label: astikit.StrPtr("Item 3")},
        },
    },
    {
        Label: astikit.StrPtr("Root 2"),
        SubMenu: []*astilectron.MenuItemOptions{
            {Label: astikit.StrPtr("Item 1")},
            {Label: astikit.StrPtr("Item 2")},
        },
    },
})

// Create the menu
m.Create()

// Change tray's image
time.Sleep(time.Second)
t.SetImage("/path/to/image-2.png")

Notifications

// Create the notification
var n = a.NewNotification(&astilectron.NotificationOptions{
	Body: "My Body",
	HasReply: astikit.BoolPtr(true), // Only MacOSX
	Icon: "/path/to/icon",
	ReplyPlaceholder: "type your reply here", // Only MacOSX
	Title: "My title",
})

// Add listeners
n.On(astilectron.EventNameNotificationEventClicked, func(e astilectron.Event) (deleteListener bool) {
	log.Println("the notification has been clicked!")
	return
})
// Only for MacOSX
n.On(astilectron.EventNameNotificationEventReplied, func(e astilectron.Event) (deleteListener bool) {
	log.Printf("the user has replied to the notification: %s\n", e.Reply)
	return
})

// Create notification
n.Create()

// Show notification
n.Show()

Dock (MacOSX only)

// Get the dock
var d = a.Dock()

// Hide and show the dock
d.Hide()
d.Show()

// Make the Dock bounce
id, _ := d.Bounce(astilectron.DockBounceTypeCritical)

// Cancel the bounce
d.CancelBounce(id)

// Update badge and icon
d.SetBadge("test")
d.SetIcon("/path/to/icon")

// New dock menu
var m = d.NewMenu([]*astilectron.MenuItemOptions{
    {
        Label: astikit.StrPtr("Root 1"),
        SubMenu: []*astilectron.MenuItemOptions{
            {Label: astikit.StrPtr("Item 1")},
            {Label: astikit.StrPtr("Item 2")},
            {Type: astilectron.MenuItemTypeSeparator},
            {Label: astikit.StrPtr("Item 3")},
        },
    },
        {
        Label: astikit.StrPtr("Root 2"),
        SubMenu: []*astilectron.MenuItemOptions{
            {Label: astikit.StrPtr("Item 1")},
            {Label: astikit.StrPtr("Item 2")},
        },
    },
})

// Create the menu
m.Create()

Dialogs

Add the following line at the top of your javascript file :

const { dialog } = require('electron').remote

Use the available methods.

Basic auth

// Listen to login events
w.OnLogin(func(i astilectron.Event) (username, password string, err error) {
	// Process the request and auth info
	if i.Request.Method == "GET" && i.AuthInfo.Scheme == "http://" {
		username = "username"
		password = "password"
	}
    return
})

Features and roadmap

  • custom branding (custom app name, app icon, etc.)
  • window basic methods (create, show, close, resize, minimize, maximize, ...)
  • window basic events (close, blur, focus, unresponsive, crashed, ...)
  • remote messaging (messages between GO and Javascript)
  • single binary distribution
  • multi screens/displays
  • menu methods and events (create, insert, append, popup, clicked, ...)
  • bootstrap
  • dialogs (open or save file, alerts, ...)
  • tray
  • bundler
  • session
  • accelerators (shortcuts)
  • dock
  • notifications
  • loader
  • file methods (drag & drop, ...)
  • clipboard methods
  • power monitor events (suspend, resume, ...)
  • desktop capturer (audio and video)
  • window advanced options (add missing ones)
  • window advanced methods (add missing ones)
  • window advanced events (add missing ones)
  • child windows

Cheers to

go-thrust which is awesome but unfortunately not maintained anymore. It inspired this project.

Owner
Quentin Renard
Freelance | Senior backend developer (GO)
Quentin Renard
Comments
  • Provide a way to ease building cross-platform single binary distribution

    Provide a way to ease building cross-platform single binary distribution

    Related comments:

    • https://github.com/asticode/go-astilectron/issues/11#issuecomment-303970242
    • https://github.com/asticode/go-astilectron/issues/10#issuecomment-303819875
  • Integrating go-astilectron into existing Electron project

    Integrating go-astilectron into existing Electron project

    Hello! Love the project. I am wondering if there is any way to start go-astilectron from the javascript side (specifically the electron main process) so that I can integrate go-astilectron into my projects that already have a javascript backend.

    I'm looking for these two things:

    1. The ability to create event handlers in javascript that can communicate with windows created in go-astilectron (for services that don't have good go libraries yet, or have already been implemented in the project's js).
    2. The ability to run the project by calling $ electron ./electron.js rather than running go-astilectron directly. (that way any standard electron bundler can be used, e.g: electron-builder, electron-forge, etc.)

    I've provided below an example of the kind of API I'm thinking of.

    Is any of this possible? From what I understand, astilectron seems a little "all-or-nothing" in this regard, but perhaps I'm missing something.

    Thanks in advance.

    Example The javascript:
    // electron.js (entry point for the application)
    
    import { start, getWindowsSync } from "astilectron";
    import { exec } from "child_process";
    import { ipcMain } from "electron";
    
    // setup astilectron. (javascript side)
    const port = start();
    
    // Launch go-astilectron. (go side, see below)
    let go = exec("./go_astilectron_project_binary", port);
    
    // `main` same as `mainWindow` in main.go.
    // Sync blocks until window is created.
    const [ main ] = getWindowsSync();
    
    ipcMain.on("handle in js", (message) => {
      console.debug(`received ${message} in js`);
      main.webContents.send("reply");
    });
    

    And the go code:

    //main.go (entry point for go)
    
    /**
      * Package main is the go-astilectron bindings for this app
      */
    package main
    
    import (
        "github.com/asticode/astilog"
        "github.com/asticode/go-astilectron"
    )
    
    func main() {
        var app, _ := astilectron.New(&astilectron.WindowOptions{})
        var mainWindow, _ := app.newWindow("file://<working dir>/app/index.html", nil)
        mainWindow.create()
        mainWindow.On("handle in go", func(m *astilectron.Message) {
            // Unmarshal
            var s string
            m.Unmarshal(&s)
    
            // Process message
            astilog.Debugf("received %s in go", s)
        })
    }
    
    // package.json
    {
        "name": "project-name",
        "scripts": {
            "start:dev": "electron ./electron.js"
        },
        "dependencies": {
            "astilectron": "<version number>",
            "electron": "<version number>"
        }
    }
    

    To run the project (in development mode), one would simply call:

    $ electron ./electron.js
    

    or

    node run start:dev
    
  • RestoreAssets undefined on example 9 (bootstrap)

    RestoreAssets undefined on example 9 (bootstrap)

    The line 45 in "examples/9.bootstrap/main.go" is:

    RestoreAssets: RestoreAssets,
    

    But need be something like this:

    RestoreAssets: func(dir, name string) error {
        return nil
    },
    
  • Zombie child process after close if debug window was opened

    Zombie child process after close if debug window was opened

    I'd been running into a problem for a while with an electron process being left running after closing the main window.

    This process rockets up from ~2% CPU while the main process is running, to ~25% after the applicatoin has "closed", which has a very detremental effect on the computer after it happens a few times.

    After a bit of experimentation, it seems to reliably happen after opening the debug panel, and then closing the main window.

    The core process shuts down, but it seems the debug window remains orphaned.

    I'm relatively new to using astillectron with bootstrap, is there a way to find the list of active windows as it shuts down? Similarly, it would be nice to have an event handler for when new windows are created, so that if they are created from the window (by JavaScript or otherwise) that the Go backend can be updated with their creation.

    Ideally an end solution would be that on close, the application should clean up all active windows, so that it doesn't leave zombie children behind.

  • Standalone binary instructions?

    Standalone binary instructions?

    I see that example 5 concerns single binary distributions, but I'm not sure what these commands do exactly:

    $ go generate examples/5.single_binary_distribution/main.go
    $ go run examples/5.single_binary_distribution/main.go examples/5.single_binary_distribution/vendor.go -v
    

    Does this just apply to the last example you've run? Where does it put the binary? Also, the go generate command works on one of my machines, but on another machine I get:

    bindata: Failed to stat input path '..\vendor': GetFileAttributesEx ..\vendor: The system cannot find the file specified.
    examples\5.single_binary_distribution\main.go:12: running "go-bindata": exit status 1
    

    It seems as though this machine doesn't create the go-astilectron\examples\vendor directory when running the examples for some reason. Both are Windows 10, Go 1.8.3.

    Separate question: running go install on any of the examples seems to output a standalone binary in my go\bin directory. Are these true standalone binaries? If so, what is the purpose of example 5? Also, if someone could give me a clearer idea of what the go-astilectron-bundler is for I'd be very grateful. Thanks!

  • Can't end an app

    Can't end an app

    While I create a window for a url like "https://wx.qq.com" .if I close the app but the program doesn't end up. I try to find out the reason.Then I find the program bolcked in a.Wait(). so If I modify the function a.Stop()

    func (a *Astilectron) Stop() {
    	astilog.Debug("Stopping...")
    	a.canceller.Cancel()
    	close(a.channelQuit)
    	//	if a.channelQuit != nil {
    	//		close(a.channelQuit)
    	//		a.channelQuit = nil
    	//	}
    }
    

    a.Wait()

    func (a *Astilectron) Wait() {
    	//for {
    	astilog.Debug("wait for quit")
    	// select {
    	// case <-a.channelQuit:
    	// 	return
    	// }
    	select {
    	case _, ok := <-a.channelQuit:
    		if !ok {
    			a.channelQuit = nil
    		}
    		return
    	}
    	//}
    }
    

    It will not blolck and end up right now.But I don't clearly understand why the website is blocked.Maybe the channel need to close ,not set value nil.

  • TCP Connection Stream destroyed

    TCP Connection Stream destroyed

    After running my application, then leaving it open and idle for roughly 1-2 hours. It will randomly crash within that time frame and give the following errors. I have tested this twice now, both tests had the same results.

    1d99bdfa0f44d7fe99149429ff60444f a1c562114461eb9fe6b257b03426fa96

  • Astilectron not working on Windows 7

    Astilectron not working on Windows 7

    Hi,

    I am having a problem with Astilectron in which it doesn't load the window's contents properly. It just displays a blank screen.

    I've been having this issue mostly on Windows 7 computers and it started happening when I updated to the latest version of Astilectron. At first I thought it was a problem with the Electron version, so I updated from 7.1.0 to 9.3.0 and this solved the problem in some machines, but it is still happening on others.

    Here is a screenshot of what is happening:

    WhatsApp Image 2020-09-21 at 11 42 36

    Has anyone experienced this?

    How can I enable the Astilectron logs to see if there is a hidden error here?

  • Cannot load js chunks

    Cannot load js chunks

    Hi,

    I created a window using React and I bundled everything together using the create-react-app. I then added the resulting bundle into my Resources folder and created a new window:

           var err error
    	url := filepath.Join(a.Paths().DataDirectory(), "public/config/index.html")
    
    	opts := &astilectron.WindowOptions{
    		AutoHideMenuBar: astilectron.PtrBool(true),
    		AlwaysOnTop:     astilectron.PtrBool(false),
    		Transparent:     astilectron.PtrBool(false),
    		Closable:        astilectron.PtrBool(true),
    		Minimizable:     astilectron.PtrBool(true),
    		Frame:           astilectron.PtrBool(true),
    		Movable:         astilectron.PtrBool(true),
    		SkipTaskbar:     astilectron.PtrBool(false),
    		Resizable:       astilectron.PtrBool(true),
    		Show:            astilectron.PtrBool(true),
    		Icon:            astilectron.PtrStr("public/logo-onyo.ico"),
    	}
    
    	// Initialize the window
    	connectorConfig.Window, err = a.NewWindow(url, opts)
    	if err != nil {
    		connectorConfig.Window = nil
    		return err
    	}
    
    	// Register callbacks (must be done before the create so that the DidFinishLoad can execute).
    	connectorConfig.Window.On(astilectron.EventNameWindowEventDidFinishLoad, connectorConfig.onWindowLoaded)
    	connectorConfig.Window.OnMessage(connectorConfig.onConfigMessage)
    
    	// Create the window
    	err = connectorConfig.Window.Create()
    	if err != nil {
    		connectorConfig.Window = nil
    		return err
    	}
    

    The problem is that when this window loads, I get an error saying that it could not load the js chunks in the index file:

    <script src="/static/js/2.8e8a5637.chunk.js"></script>
    <script src="/static/js/main.673b3637.chunk.js"></script>
    

    I've checked my code against the demo and I believe that the problem is the /, because Electron is considering it as the root of my file system instead of a relative path (as you stated here). When I removed it my app worked:

    <script src="static/js/2.8e8a5637.chunk.js"></script>
    <script src="static/js/main.673b3637.chunk.js"></script>
    

    My question is: is there a way to make Electron load the scripts correctly without publishing them on a local server or having to change the index.html to remove the /?

  • Error when compile linux app on Windows

    Error when compile linux app on Windows

    Everything goes well when I comopiled my codes on Windows. But when I try to run the binary on Linux, which was compiled on Windows with astilectron-bundler -l, things goes wrong.

    Does it mean that I can only use the flag -l on Linux?

    Here is the log file on Linux:

    2021/01/10 23:03:50 Running app built at 2021-01-10 22:43:43.9301191 +0800 CST m=+30.178947301
    2021/01/10 23:03:50 astikit: starting worker...
    2021/01/10 23:03:50 astikit: received signal urgent I/O condition
    2021/01/10 23:03:50 astikit: received signal urgent I/O condition
    2021/01/10 23:03:50 astikit: received signal urgent I/O condition
    2021/01/10 23:03:50 astikit: received signal urgent I/O condition
    2021/01/10 23:03:50 astikit: received signal urgent I/O condition
    2021/01/10 23:03:50 astikit: received signal urgent I/O condition
    2021/01/10 23:03:50 Skipping restoring resources...
    2021/01/10 23:03:50 Starting...
    2021/01/10 23:03:50 Provisioning...
    2021/01/10 23:03:50 Provisioning Astilectron...
    2021/01/10 23:03:50 Removing directory /home/aaa/src/Foldest/vendor/astilectron
    2021/01/10 23:03:50 Disembedding vendor_astilectron_bundler/astilectron.zip into /home/aaa/src/Foldest/vendor/astilectron-v0.42.0.zip...
    2021/01/10 23:03:50 /home/aaa/src/Foldest/vendor/astilectron-v0.42.0.zip already exists, skipping disembed...
    2021/01/10 23:03:50 Creating directory /home/aaa/src/Foldest/vendor/astilectron
    2021/01/10 23:03:50 Unzipping /home/aaa/src/Foldest/vendor/astilectron-v0.42.0.zip/astilectron-0.42.0 into /home/aaa/src/Foldest/vendor/astilectron
    2021/01/10 23:03:50 Removing /home/aaa/src/Foldest/vendor/astilectron...
    2021/01/10 23:03:50 Closing...
    2021/01/10 23:03:50 astikit: stopping worker...
    2021/01/10 23:03:50 running bootstrap failed: starting astilectron failed: provisioning failed: provisioning astilectron failed: unzipping /home/aaa/src/Foldest/vendor/astilectron-v0.42.0.zip/astilectron-0.42.0 into /home/aaa/src/Foldest/vendor/astilectron failed: unzipping /home/aaa/src/Foldest/vendor/astilectron-v0.42.0.zip/astilectron-0.42.0 into /home/aaa/src/Foldest/vendor/astilectron failed: astikit: content in archive does not match specified internal path astilectron-0.42.0
    

    The mentioned file astilectron-v0.42.0.zip is also attached. astilectron-v0.42.0.zip

  • Implement ses.flushStorageData as writing to localStorage doesn't always persist

    Implement ses.flushStorageData as writing to localStorage doesn't always persist

    I find that when writing to localStorage, data will not persist if the go-astilectron app closes or crashes within about 10 seconds, before the localStorage has had a chance to be written - easily reproducible by loading a page with a script which writes to localstorage then closing the window after 2 seconds. With devtools open you can see the item is written to localstorage, but when the app is restarted it isn't there.

    I know this is due to electron localstorage not being flushed to disk, and there are numerous issues about it (like https://github.com/electron/electron/issues/1731) however even when we explicitly call app.Quit() on the astilectron app, which calls app.quit() for electron, the problem still occurs if we quit 2 seconds after writing to localstorage.

    I would really like writing to localStorage to be reliable - 10 secs is a bit too long to have to wait to make sure it has been properly written.

    Is it possible for astilectron.Session to implement flushStorageData so we can explicitly make electron localStorage be written? I'm not sure how to go about this.

  • Facilitate running custom javascript on the main JS process

    Facilitate running custom javascript on the main JS process

    Note this PR is dependent on https://github.com/asticode/astilectron/pull/63

    The rationale is explained in the PR above.

    The eventProxy feels very awkward, I'm probably doing more than I need to. Happy to receive some pointers on how to improve this code.

  • Tray context menu popup

    Tray context menu popup

    Hello, I'm developing a windows application with go-astilectron and i couldn't find a proper way to open tray context menu on click event or manually, so is there any way to open tray context menu? This action could be done in electron by below link https://www.electronjs.org/docs/latest/api/tray#traypopupcontextmenumenu-position-macos-windows

    Thanks

  • Manipulate WindowOptions

    Manipulate WindowOptions

    I'd like to dynamically set AlwaysOnTop based on a setting inside my app.

    Is there a way to access the WindowOptions outside the bootstrapping procedure (I've been unable to find it, or rather direct access is private) or will I have to make-do with require("electron").remote.BrowserWindow.getFocusedWindow().setAlwaysOnTop(bool_value) from inside JS? I'd rather like to avoid doing it like this.

Kita is a declarative, reactive GUI toolkit for build cross platform apps with web technology with single codebase
Kita is a declarative, reactive GUI toolkit for build cross platform apps with web technology with single codebase

Kita is a declarative, reactive GUI toolkit for build cross platform apps with web technology with single codebase. Inspired by Flutter, React. S

Apr 18, 2022
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

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

Apr 18, 2022
Build cross-platform modern desktop apps in Go + HTML5
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

Jan 6, 2023
GPU based Electron on a diet

Muon is a lightweight alternative to Electron written in Golang in about ~300 LoC, using Ultralight instead of Chromium. Ultralight is a cross-platfor

Jan 2, 2023
Cross-platform GUI for go is never this easy and clean.
Cross-platform GUI for go is never this easy and clean.

gimu Strongly suggest NOT to use this project anymore, the auto-generated cgo wrapper of Nuklear has a random crash issue which is hard to fix (becaus

Jul 12, 2022
Golang bindings of Sciter: the Embeddable HTML/CSS/script engine for modern UI development
Golang bindings of Sciter: the Embeddable HTML/CSS/script engine for modern UI development

Go bindings for Sciter Check this page for other language bindings (Delphi / D / Go / .NET / Python / Rust). Attention The ownership of project is tra

Dec 23, 2022
Cross platform GUI in Go based on Material Design
Cross platform GUI in Go based on Material Design

About Fyne is an easy to use UI toolkit and app API written in Go. It is designed to build applications that run on desktop and mobile devices with a

Jan 3, 2023
RobotGo, Go Native cross-platform GUI automation @vcaesar

Robotgo Golang Desktop Automation. Control the mouse, keyboard, bitmap, read the screen, Window Handle and global event listener. RobotGo supports Mac

Jan 7, 2023
Cross platform rapid GUI framework for golang based on Dear ImGui.
Cross platform rapid GUI framework for golang based on Dear ImGui.

giu Cross platform rapid GUI framework for golang based on Dear ImGui and the great golang binding imgui-go. Any contribution (features, widgets, tuto

Dec 28, 2022
Cross-Platform GUI Framework for Go

⚠️ I'm currently working on this project as part of my master's thesis at the Berlin University of Applied Sciences and Technology. It is under active

Oct 31, 2022
Cross-platform Go/Golang GUI library.

中文 | English GoVCL Cross-platform Golang GUI library, The core binding is liblcl, a common cross-platform GUI library created by Lazarus. GoVCL is a n

Dec 30, 2022
Common library for Go GUI apps on Windows
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.

Dec 12, 2022
Platform-native GUI library for Go.

ui: platform-native GUI library for Go This is a library that aims to provide simple GUI software development in Go. It is based on my libui, a simple

Jan 9, 2023
A package to build progressive web apps with Go programming language and WebAssembly.
A package to build progressive web apps with Go programming language and WebAssembly.

go-app is a package to build progressive web apps (PWA) with Go programming language and WebAssembly. It uses a declarative syntax that allows creatin

Dec 28, 2022
Tiny cross-platform webview library for C/C++/Golang. Uses WebKit (Gtk/Cocoa) and Edge (Windows)

webview A tiny cross-platform webview library for C/C++/Golang to build modern cross-platform GUIs. Also, there are Rust bindings, Python bindings, Ni

Dec 28, 2022
Go cross-platform library for displaying dialogs and input boxes

dlgs dlgs is a cross-platform library for displaying dialogs and input boxes. Installation go get -u github.com/gen2brain/dlgs Documentation Document

Dec 24, 2022
Tiny cross-platform webview library for C/C++/Golang. Uses WebKit (Gtk/Cocoa) and Edge (Windows)

webview A tiny cross-platform webview library for C/C++/Golang to build modern cross-platform GUIs. Also, there are Rust bindings, Python bindings, Ni

Jan 1, 2023
An experimental Go cross platform UI library.

GXUI - A Go cross platform UI library. Notice: Unfortunately due to a shortage of hours in a day, GXUI is no longer maintained. If you're looking for

Jan 6, 2023
Cross-platform Go library to place an icon in the host operating system's taskbar.

trayhost Package trayhost is a cross-platform Go library to place an icon in the host operating system's taskbar. Platform Support macOS - Fully imple

Nov 6, 2022