An open API service indexing awesome lists of open source software.

https://github.com/crgimenes/glaze

Glaze: a CGO-free desktop WebView toolkit for Go, with practical helpers for building native app windows from web UIs.
https://github.com/crgimenes/glaze

cgo-free cross-platform desktop go golang purego webview

Last synced: 2 months ago
JSON representation

Glaze: a CGO-free desktop WebView toolkit for Go, with practical helpers for building native app windows from web UIs.

Awesome Lists containing this project

README

          

# Glaze

Glaze is a pure Go desktop WebView binding built on top of [webview/webview](https://github.com/webview/webview) and [purego](https://github.com/ebitengine/purego).

This repository started from the original `go-webview` project, but it has diverged significantly and is maintained as a separate codebase with its own goals and APIs.

## Goals

- Keep a CGo-free desktop integration layer for Go applications.
- Provide pragmatic helpers for desktop app workflows.
- Keep behavior explicit and testable.
- Stay friendly to multi-module `go.work` development.

## Key Features

- No CGo
- Windows, macOS, and Linux support
- Native library loading with embedded runtime assets
- JavaScript to Go binding support
- Utility helpers for desktop app patterns: `BindMethods`, `RenderHTML`, `AppWindow`

## Examples

| Desktop | Game of Life | Starfield |
| --- | --- | --- |
| [![Desktop example preview](imgs/desktop.gif)](examples/desktop/) | [![Game of Life example preview](imgs/gameoflife.gif)](examples/gameoflife/) | [![Starfield example preview](imgs/starfield.gif)](examples/starfield/) |

| Doom Fire | Mandelbrot | Falling Sand |
| --- | --- | --- |
| [![Doom Fire example preview](imgs/doomfire.gif)](examples/doomfire/) | [![Mandelbrot example preview](imgs/mandelbrot.gif)](examples/mandelbrot/) | [![Falling Sand example preview](imgs/fallingsand.gif)](examples/fallingsand/) |

| Raycasting | Filo REPL | |
| --- | --- | --- |
| [![Raycasting example preview](imgs/raycasting.gif)](examples/raycasting/) | [![Filo REPL example preview](imgs/filorepl.gif)](examples/filorepl/) | |

## Install

```bash
go get github.com/crgimenes/glaze@latest
```

To use embedded native libraries:

```go
import _ "github.com/crgimenes/glaze/embedded"
```

## Quick Start

```go
package main

import (
"log"

"github.com/crgimenes/glaze"
_ "github.com/crgimenes/glaze/embedded"
)

func main() {
w, err := glaze.New(true)
if err != nil {
log.Fatal(err)
}
defer w.Destroy()

w.SetTitle("Glaze")
w.SetSize(800, 600, glaze.HintNone)
w.SetHtml("

Hello from Glaze

")
w.Run()
}
```

Glaze pins the goroutine that creates the first window to its current OS thread.
Keep direct window calls on that goroutine, and use `Dispatch` to re-enter the UI
thread from background work.

## Desktop Helpers

### BindMethods

`BindMethods` is a convenience layer over `Bind` that exposes all exported
methods of a Go value as JavaScript-callable functions.

What it does:

- Reflects over exported methods on a struct or pointer receiver.
- Builds JavaScript names using a prefix and snake_case conversion.
- Example: `GetUserByID` with prefix `api` becomes `api_get_user_by_id`.
- Applies the same function signature rules as `Bind`:
- no return
- value
- error
- value and error
- Returns the list of bound names so you can log or verify registration.

This is useful when you have a service object and want to expose a consistent
JavaScript API without writing one `Bind` call per method.

```go
type Store struct{}

func (s *Store) GetItems() []string { return []string{"a", "b"} }

bound, err := glaze.BindMethods(w, "store", &Store{})
```

### RenderHTML

`RenderHTML` renders a named Go `html/template` into a string for `SetHtml`.

What it does:

- Executes a specific template by name (including nested template calls).
- Returns the final HTML string.
- Wraps template execution errors with template context.

This is useful when you want server-style template rendering in a local desktop
app without running an HTTP server for that page.

```go
html, err := glaze.RenderHTML(tpl, "page", data)
if err != nil {
return err
}
w.SetHtml(html)
```

### AppWindow

`AppWindow` wraps an `http.Handler` inside a native desktop window backed by a
local loopback HTTP server.

What it does:

- Supports selectable transport with platform-aware default:
- `auto` (default): `unix` on macOS/Linux, `tcp` on Windows
- `tcp`: direct loopback HTTP (`127.0.0.1`)
- `unix`: handler served on Unix socket with a lightweight loopback HTTP
gateway for browser navigation
- Starts listeners using random free ports/paths by default (or custom
`Addr`/`UnixSocketPath`).
- Creates a native window and navigates it to that local URL.
- Runs the UI loop and closes the HTTP server when the window exits.
- Supports window sizing, title, debug mode, and optional readiness callback.
- `OnReady`: receives browser URL (always `http://127.0.0.1:...`).
- `OnReadyInfo`: receives resolved backend details (`Transport`, `Backend`,
`Gateway`) so you can verify unix vs tcp in logs.

This is the simplest way to reuse an existing `net/http` application as a
desktop app with minimal changes to your routing, templates, and assets.

```go
err := glaze.AppWindow(glaze.AppOptions{
Title: "My App",
Width: 1280,
Height: 800,
Transport: glaze.AppTransportAuto,
Handler: mux,
OnReadyInfo: func(info glaze.AppReadyInfo) {
log.Printf("transport=%s backend=%s gateway=%s", info.Transport, info.Backend, info.Gateway)
},
})
```

## Running Examples

From the repository root:

```bash
go run ./examples/simple
go run ./examples/bind
go run ./examples/zero_tcp
```

From each example directory:

```bash
cd examples/appwindow && go run .
cd examples/desktop && go run .
cd examples/filorepl && go run .
```

`examples/zero_tcp` demonstrates a local-first UI with `SetHtml + BindMethods`
only. It does not start an HTTP server, so there is no loopback TCP gateway.

## Testing

Default tests (headless safe):

```bash
go test ./...
```

GUI integration test:

```bash
go test -tags=integration -run TestWebview ./...
```

## Building on Windows

Use `windowsgui` to hide the console window:

```bash
go build -ldflags="-H windowsgui" .
```

## Project Layout

- `webview.go` - core API and binding internals
- `appwindow.go` - desktop window plus local HTTP server helper
- `helpers.go` - utility helpers (`BindMethods`, `RenderHTML`)
- `embedded/` - embedded native library assets per platform
- `examples/` - runnable sample applications

## Security: Library Integrity Verification

Glaze embeds native libraries and extracts them to disk before loading. By
default, the extraction target is a temporary directory that may be writable by
other processes, which could allow an attacker to replace the library file.

To mitigate this, Glaze computes a BLAKE2b-256 hash of the embedded library
bytes at runtime and verifies every extracted (or pre-existing) file against
that hash before loading. If the hash does not match, extraction fails with an
integrity error and the library is **not** loaded.

Additionally, extracted files are created with restricted permissions (`0500`
owner read+execute) inside a directory with `0700` permissions.

### Custom Library Directory

For production deployments, use `ExtractTo` to place the library in a secure,
application-controlled directory instead of the system temp directory:

```go
package main

import (
"log"

"github.com/crgimenes/glaze"
"github.com/crgimenes/glaze/embedded"
)

func main() {
// Extract to a directory with restricted access.
if err := embedded.ExtractTo("/opt/myapp/lib"); err != nil {
log.Fatal(err)
}

w, err := glaze.New(true)
if err != nil {
log.Fatal(err)
}
defer w.Destroy()

w.SetTitle("Secure App")
w.SetSize(800, 600, glaze.HintNone)
w.SetHtml("

Hello

")
w.Run()
}
```

When using `ExtractTo`, do **not** use `import _ "github.com/crgimenes/glaze/embedded"` — that
blank import triggers `init()` which extracts to the default temp directory.
Call `ExtractTo` explicitly instead.

## Acknowledgments

- [abemedia/go-webview](https://github.com/abemedia/go-webview) for the original Go binding base
- [webview/webview](https://github.com/webview/webview) for the native WebView implementation
- [purego](https://github.com/ebitengine/purego) for dynamic linking without CGo