Skip to content

BigJk/StdUI

Repository files navigation

simple example


An experimental lightweight language-agnostic cross-platform UI engine — Check out the docs!

StdUI is a lightweight cross-platform UI engine that can be used with any programming language. It's designed to be spawned as process and communicate with the main application via stdin/stdout. This allows for easy integration with various programming languages and frameworks. It supports a reduced sub-set of HTML/CSS for UI rendering. No browser is involved.

The goal is to provide a simple way to build native desktop applications with similar layout and style options to what you would expect from the web, but without the overhead of a full browser engine. It should also enable languages where UI libraries are rare. Its target use case is smaller applications with low to medium complexity that need a simple and easy UI.

Warning

This project is experimental. Expect bugs, missing features and breaking changes. The API is not stable and may change without deprecation. Use at your own risk.

Features

  • Cross-platform (Windows, macOS, Linux)
  • Language agnostic (communicates via stdin/stdout)
  • Supports a reduced sub-set of HTML/CSS for UI layouting and styling
  • Pane-based layout system
  • Interactive widgets:
    • Buttons
    • Text/Number/Password inputs
    • Checkboxes
    • Sliders
    • Progress bars
    • Color Picker
  • Display of local images via img tag
  • Native file/folder dialogs
  • File drop support
  • Supports playing of sounds
  • Clipboard text access
  • Notification toasts
  • ~5MB binary size
  • ...

Screenshots

todo example

chat example

chat example

Examples

  • Simple: Minimal example just showing the StdUI logo and a button
  • Interactive Elements: Showcases all the built-in interactive widgets
  • Todo List: Simple todo list app
  • Chat: Simple chat interface with message input and display (no real networking, just simulates a conversation)
  • IRC Client: A working, but simple IRC client

All these examples are based on the Go SDK.

Limitations

This uses litehtml for html/css layouting. This means that only a reduced sub-set of HTML/CSS is supported and there are quirks in the way some html/css is interpreted. For example, the display: flex property is suported, but gap is not. Sometimes you might need to fall back to table layout to achieve the desired result or use some other slightly strange workarounds.

Usage

stdui is a compiled C++ binary that opens a native OS window and renders HTML with interactive widgets. Your application controls it by spawning it as a child process and exchanging newline-delimited JSON over stdin/stdout. Any language that can start a process and read/write pipes can drive it.

Your App (Go, Python, anything)
        │  stdin  → JSON commands
        │  stdout ← JSON events
        ▼
   stdui binary (C++)
   ┌-----------------------------─┐
   │  litehtml  — HTML/CSS layout │
   │  ImGui     — interactive     │
   │             widgets          │
   │  raylib    — window, audio   │
   └------------------------------┘

Startup

The binary blocks on startup waiting for a settings message. Send it immediately after spawning the process, before doing anything else:

{ "action": "settings", "data": { "title": "My App", "windowWidth": 800, "windowHeight": 600 } }

Once the window is open and ready, the binary replies:

{ "action": "ready" }

Only send content after you receive ready.

Rendering

Push HTML to the window with update-content. The binary re-renders immediately:

{ "action": "update-content", "data": "<h1>Hello from stdui</h1>" }

Whenever your state changes, build a new HTML string and send another update-content. There is no incremental patching — just replace the whole thing.

Interactive Widgets

Standard HTML/CSS handles layout and styling. For interactive elements, use the built-in <ui-*> tags:

<ui-button text="Save" action="save"></ui-button>
<ui-input id="name" type="text" placeholder="Your name" value=""></ui-input>
<ui-select id="theme" options="Light|Dark|System" value="Light"></ui-select>
<ui-slider id="vol" min="0" max="1" value="0.5"></ui-slider>
<ui-checkbox id="notify" label="Enable notifications" checked="false"></ui-checkbox>

When the user interacts with one of these, the binary emits an event on stdout.

Events

A button click:

{ "action": "button-clicked", "data": { "action": "save", "text": "Save", "pane": "main" } }

An input or widget value change:

{ "action": "input-changed", "data": { "id": "name", "value": "Alice", "pane": "main" } }

A link click (<a href="...">):

{ "action": "url-clicked", "data": { "url": "my-app://some-route", "pane": "main" } }

Window closed by the user:

{ "action": "window-closed" }

Read these lines from stdout in a loop, parse the JSON, and react however your app needs to.

Minimal Round-Trip

You → stdui   {"action":"settings","data":{"title":"Hello","windowWidth":400,"windowHeight":300}}
stdui → You   {"action":"ready"}
You → stdui   {"action":"update-content","data":"<h1>Hi</h1><ui-button text=\"Click me\" action=\"hi\"></ui-button>"}
  ... user clicks the button ...
stdui → You   {"action":"button-clicked","data":{"action":"hi","text":"Click me","pane":"main"}}
You → stdui   {"action":"update-content","data":"<h1>You clicked it!</h1>"}
  ... user closes the window ...
stdui → You   {"action":"window-closed"}

That's the entire model. Every interaction follows this same pattern: send action, receive events, send new action.

You can learn more about the protocol in the protocol specification.

SDKs

Go

A Go SDK is available in the sdk/go directory. It provides a simple API for communicating with the StdUI process and reacting to user interactions. Usage examples can be found in the ./example folder. To install the Go SDK, run:

go get github.com/BigJk/stdui/sdk/go@latest
package main

import (
	"fmt"
	"os"

	stdui "github.com/BigJk/stdui/sdk/go"
)

func main() {
	// If you don't want to download the binary yourself, you can use EnsureBinary to download the latest release
	// from GitHub and place it in the working directory.
	if err := stdui.EnsureBinary("", ""); err != nil {
		panic(err)
	}

	client, err := stdui.Start("./stdui", stdui.Settings{
		Title:        "Simple",
		WindowWidth:  stdui.Ptr(700),
		WindowHeight: stdui.Ptr(400),
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to start stdui: %v\n", err)
		os.Exit(1)
	}

	client.OnReady(func() {
		client.UpdateContent("Hello World") //nolint:errcheck
	})

	client.OnLog(func(namespace, message string) {
		fmt.Fprintf(os.Stderr, "[log] %s: %s\n", namespace, message)
	})

	client.OnError(func(err error) {
		fmt.Fprintf(os.Stderr, "[error] %v\n", err)
	})

	client.Wait()
}

Credits

This project is built on top of several excellent libraries:

  • raylib - Simple and easy-to-use library for video games and graphics
  • litehtml - Lightweight HTML/CSS layout rendering engine
  • ImGui - Immediate mode GUI library for interactive widgets
  • ImHTML - ImGui extension for rendering HTML
  • hjson - Human-friendly JSON format parser
  • Native File Dialog - Cross-platform file/folder dialog library

Found the project useful? 🥰

ko-fi

About

Experimental language-agnostic lightweight UI engine written in C++

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors