client

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 23, 2026 License: MIT Imports: 4 Imported by: 6

README

Logo

GitHub release GitHub license

client-go is the Go library for writing looking-glass modules. Modules are compiled to WebAssembly (GOARCH=wasm GOOS=wasip1) and run inside the looking-glass host. This library provides everything a module needs: identity, configuration, asset access, rendering, HTTP, and logging.

Note: This is in active development, the API may change.


How a module works

A looking-glass module is a standard Go main package compiled to a WASI binary. The host launches one instance per module slot, injects configuration via environment variables, mounts an assets directory, and exposes a small set of host functions for rendering and HTTP. The module runs its own event loop and calls mod.Content(widget) whenever the display should update.

Writing a module

1. Initialise the module

client.NewModule() reads the MODULE_NAME environment variable set by the host. It must be called before any other client functions that require the module identity.

//go:build wasip1

package main

import "github.com/glasslabs/client-go"

func main() {
    log := client.NewLogger()

    mod, err := client.NewModule()
    if err != nil {
        log.Error("Could not create module", "error", err.Error())
        return
    }
    // ...
}

2. Parse configuration

The host encodes the module's configuration map as JSON and exposes it via MODULE_CONFIG. Call mod.ParseConfig with a pointer to your config struct; fields are populated from the JSON keys that match the struct tags. Set defaults before calling ParseConfig so that omitted keys retain sensible values.

type Config struct {
    Interval int    `json:"interval"`
    Label    string `json:"label"`
}

cfg := Config{Interval: 60} // set defaults first
if err := mod.ParseConfig(&cfg); err != nil {
    log.Error("Could not parse config", "error", err.Error())
    return
}

3. Load assets

Static files (templates, images, SVG) are placed alongside the module binary. The host mounts them at /assets inside the WASM sandbox. Use mod.Asset(path) to read them; path is relative to that directory.

data, err := mod.Asset("index.html")

4. Render content

Call mod.Content(widget) to push a new widget tree to the display. The host replaces the module's current content with the new tree on every call — there is no diffing. Call it once on startup and again whenever the display needs to change.

Widget trees are built from the composable types in this package:

Widget Behaviour
NewText(s, ...opts) A single styled line of text.
NewSVG(markup) Raw SVG markup rasterised into the slot.
NewVStack(children...) Lays out children vertically, top to bottom.
NewHStack(children...) Lays out children horizontally, left to right.
NewSpacer() Flexible empty space that fills remaining room in a stack.
NewCanvas(w, h, ops...) A fixed logical viewport scaled to fit, drawn with DrawOp commands.

Text can be styled with option functions: WithColor, WithFontSize, WithLight, WithBold, WithItalic, WithCondensed, and WithAlign.

func render(mod *client.Module, label string) {
    mod.Content(client.NewVStack(
        client.NewText(label,
            client.WithColor("#ffffff"),
            client.WithFontSize(48),
            client.WithLight(),
        ),
        client.NewSpacer(),
    ))
}
Canvas drawing

Canvas renders a list of DrawOp commands within a logical coordinate space. The viewport is scaled uniformly to fit the allocated slot.

DrawOp Behaviour
NewRect(x, y, w, h, ...opts) Filled and/or stroked rectangle. Options: WithFill, WithStroke, WithCornerRadius.
NewArc(cx, cy, radius, startAngle, sweepAngle, strokeWidth, color) Circular arc stroke. Angles are in degrees; 0° is right, clockwise positive.
NewLabel(x, y, align, runs...) Baseline-aligned multi-run text. align is "start", "middle", or "end".
NewPath(x, y, scale, d, fill) SVG path d string placed at an offset with optional uniform scale.

Label is built from TextRun segments created with NewRun(content, ...opts). Options: WithRunFontSize, WithRunBaselineShift, WithRunColor.

5. Make HTTP requests

The host provides an HTTP transport that is automatically installed into http.DefaultClient when the module starts. Use the standard net/http package directly — no special client is required.

resp, err := http.Get("https://example.com/api/data")

The transport supports streaming response bodies, so Server-Sent Events and other long-lived streams work as expected.

6. Log messages

client.NewLogger() returns a logger that writes logfmt lines to stderr. The host captures these lines and forwards them to its structured logging pipeline.

log := client.NewLogger()
log.Info("Module ready", "module", mod.Name())
log.Error("Something failed", "error", err.Error())

Methods: Debug, Info, Warn, Error. Each accepts a message followed by alternating key/value string pairs.


Building a module

Modules must be compiled for the wasip1 target:

GOARCH=wasm GOOS=wasip1 go build -o my-module.wasm .

All source files that use client-go should carry the //go:build wasip1 build constraint, as the host functions are only available inside the WASM sandbox.

Minimal example

//go:build wasip1

package main

import (
    "time"

    "github.com/glasslabs/client-go"
)

type Config struct {
    Format string `json:"format"`
}

func main() {
    log := client.NewLogger()

    mod, err := client.NewModule()
    if err != nil {
        log.Error("Could not create module", "error", err.Error())
        return
    }

    cfg := Config{Format: "15:04:05"}
    if err = mod.ParseConfig(&cfg); err != nil {
        log.Error("Could not parse config", "error", err.Error())
        return
    }

    log.Info("Module ready", "module", mod.Name())

    for {
        mod.Content(client.NewText(
            time.Now().Format(cfg.Format),
            client.WithColor("#ffffff"),
            client.WithFontSize(48),
        ))
        time.Sleep(time.Second)
    }
}

Documentation

Overview

Package client is the client library for writing looking-glass WASM modules.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Arc

type Arc struct {
	XMLName     xml.Name `xml:"arc"`
	Cx          float32  `xml:"cx,attr"`
	Cy          float32  `xml:"cy,attr"`
	Radius      float32  `xml:"radius,attr"`
	StartAngle  float32  `xml:"startAngle,attr"` // degrees; 0 = right, clockwise
	SweepAngle  float32  `xml:"sweepAngle,attr"` // degrees; clockwise positive
	StrokeWidth float32  `xml:"strokeWidth,attr"`
	Color       string   `xml:"color,attr"`
}

Arc draws a circular arc stroke.

func NewArc

func NewArc(cx, cy, radius, startAngle, sweepAngle, strokeWidth float32, color string) *Arc

NewArc returns an Arc draw operation.

type Canvas

type Canvas struct {
	XMLName xml.Name `xml:"canvas"`
	Width   float32  `xml:"-"`
	Height  float32  `xml:"-"`
	Ops     []DrawOp `xml:"-"`
}

Canvas renders draw operations within a fixed-size logical viewport scaled to fit the allocated space.

func NewCanvas

func NewCanvas(width, height float32, ops ...DrawOp) *Canvas

NewCanvas returns a Canvas widget with the given logical size and ordered draw list.

func (*Canvas) Equals

func (c *Canvas) Equals(o *Canvas) bool

Equals checks the logical equality of two Canvas widgets, ignoring differences that don't affect rendering such as the order of draw operations with identical parameters.

func (*Canvas) MarshalXML

func (c *Canvas) MarshalXML(e *xml.Encoder, start xml.StartElement) error

MarshalXML implements xml.Marshaler.

func (*Canvas) UnmarshalXML

func (c *Canvas) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

UnmarshalXML decodes the Canvas element, its width/height attributes, and DrawOp children.

type Column

type Column struct {
	XMLName  xml.Name `xml:"column"`
	MinWidth float32  `xml:"minWidth,attr,omitempty"`
	Child    Widget   `xml:"-"`
}

Column is a cell within a Row, holding a single child widget.

func NewColumn

func NewColumn(child Widget, minWidth float32) *Column

NewColumn returns a Column containing child with an optional minimum width in dp.

func (*Column) MarshalXML

func (c *Column) MarshalXML(e *xml.Encoder, start xml.StartElement) error

MarshalXML implements xml.Marshaler.

func (*Column) UnmarshalXML

func (c *Column) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

UnmarshalXML decodes the Column element, its minWidth attribute, and its single Widget child.

type DrawOp

type DrawOp interface {
	// contains filtered or unexported methods
}

DrawOp is a drawing command within a Canvas.

type HStack

type HStack struct {
	XMLName  xml.Name `xml:"hstack"`
	Children []Widget `xml:"-"`
}

HStack lays out children horizontally, left to right.

func NewHStack

func NewHStack(children ...Widget) *HStack

NewHStack returns an HStack widget that lays out children left to right.

func (*HStack) MarshalXML

func (h *HStack) MarshalXML(e *xml.Encoder, start xml.StartElement) error

MarshalXML implements xml.Marshaler.

func (*HStack) UnmarshalXML

func (h *HStack) UnmarshalXML(d *xml.Decoder, _ xml.StartElement) error

UnmarshalXML decodes the HStack element and its Widget children.

type Label

type Label struct {
	XMLName xml.Name   `xml:"label"`
	X       float32    `xml:"x,attr"`
	Y       float32    `xml:"y,attr"`
	Align   string     `xml:"align,attr,omitempty"` // "start", "middle", or "end"
	Runs    []*TextRun `xml:"run"`
}

Label draws baseline-aligned multi-run text at a canvas position.

func NewLabel

func NewLabel(x, y float32, align string, runs ...*TextRun) *Label

NewLabel returns a Label draw operation.

type Path

type Path struct {
	XMLName xml.Name `xml:"path"`
	X       float32  `xml:"x,attr,omitempty"`
	Y       float32  `xml:"y,attr,omitempty"`
	Scale   float32  `xml:"scale,attr,omitempty"`
	D       string   `xml:"d,attr"`
	Fill    string   `xml:"fill,attr,omitempty"`
}

Path draws an SVG path d-string at an offset with optional uniform scale.

func NewPath

func NewPath(x, y, scale float32, d, fill string) *Path

NewPath returns a Path draw operation.

type Rect

type Rect struct {
	XMLName      xml.Name `xml:"rect"`
	X            float32  `xml:"x,attr"`
	Y            float32  `xml:"y,attr"`
	W            float32  `xml:"w,attr"`
	H            float32  `xml:"h,attr"`
	CornerRadius float32  `xml:"cornerRadius,attr,omitempty"`
	Fill         string   `xml:"fill,attr,omitempty"`
	Stroke       string   `xml:"stroke,attr,omitempty"`
	StrokeWidth  float32  `xml:"strokeWidth,attr,omitempty"`
}

Rect draws a filled and/or stroked rectangle.

func NewRect

func NewRect(x, y, w, h float32, opts ...RectOption) *Rect

NewRect returns a Rect draw operation.

type RectOption

type RectOption func(*Rect)

RectOption configures a Rect draw operation.

func WithCornerRadius

func WithCornerRadius(cr float32) RectOption

WithCornerRadius sets the corner radius for a rounded rectangle.

func WithFill

func WithFill(c string) RectOption

WithFill sets the rectangle fill colour.

func WithStroke

func WithStroke(c string, w float32) RectOption

WithStroke sets the rectangle stroke colour and width.

type Row

type Row struct {
	XMLName xml.Name  `xml:"row"`
	Columns []*Column `xml:"-"`
}

Row is a horizontal sequence of Column cells within a Table.

func NewRow

func NewRow(cols ...*Column) *Row

NewRow returns a Row containing the given Column cells.

func (*Row) MarshalXML

func (r *Row) MarshalXML(e *xml.Encoder, start xml.StartElement) error

MarshalXML implements xml.Marshaler.

func (*Row) UnmarshalXML

func (r *Row) UnmarshalXML(d *xml.Decoder, _ xml.StartElement) error

UnmarshalXML decodes the Row element and its Column children.

type RunOption

type RunOption func(*TextRun)

RunOption configures a TextRun within a Label.

func WithRunBaselineShift

func WithRunBaselineShift(s float32) RunOption

WithRunBaselineShift shifts the run down from the shared baseline in canvas units.

func WithRunColor

func WithRunColor(c string) RunOption

WithRunColor sets the run text colour as an HTML hex string.

func WithRunFontSize

func WithRunFontSize(s float32) RunOption

WithRunFontSize sets the run font size in canvas units.

type SVG

type SVG struct {
	XMLName xml.Name `xml:"svg"`
	Content string   `xml:",chardata"`
}

SVG rasterizes raw SVG markup. Used for complex assets such as a floorplan.

func NewSVG

func NewSVG(content string) *SVG

NewSVG returns an SVG widget containing the given SVG markup.

type Spacer

type Spacer struct {
	XMLName xml.Name `xml:"spacer"`
	Min     float32  `xml:"min,attr,omitempty"`
}

Spacer inserts flexible empty space inside a stack.

func NewSpacer

func NewSpacer(opts ...SpacerOption) *Spacer

NewSpacer returns a Spacer that expands to fill available space with a default minimum size of 8 dp.

type SpacerOption

type SpacerOption func(*Spacer)

SpacerOption configures a Spacer widget.

func WithMinSize

func WithMinSize(minSize float32) SpacerOption

WithMinSize sets the minimum size in dp that the spacer occupies along its primary axis.

type Table

type Table struct {
	XMLName    xml.Name `xml:"table"`
	RowSpacing float32  `xml:"rowSpacing,attr,omitempty"`
	Rows       []*Row   `xml:"-"`
}

Table lays out Row children vertically, automatically sizing each column to the widest natural width across all rows.

func NewTable

func NewTable(rows []*Row, opts ...TableOption) *Table

NewTable returns a Table containing the given Row children.

func (*Table) MarshalXML

func (t *Table) MarshalXML(e *xml.Encoder, start xml.StartElement) error

MarshalXML implements xml.Marshaler.

func (*Table) UnmarshalXML

func (t *Table) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

UnmarshalXML decodes the Table element and its Row children.

type TableOption

type TableOption func(*Table)

TableOption configures a Table widget.

func WithRowSpacing

func WithRowSpacing(dp float32) TableOption

WithRowSpacing sets the vertical gap in dp between rows.

type Text

type Text struct {
	XMLName   xml.Name `xml:"text"`
	Content   string   `xml:",chardata"`
	Color     string   `xml:"color,attr,omitempty"`
	FontSize  float32  `xml:"fontSize,attr,omitempty"`
	Condensed bool     `xml:"condensed,attr,omitempty"`
	Light     bool     `xml:"light,attr,omitempty"`
	Bold      bool     `xml:"bold,attr,omitempty"`
	Italic    bool     `xml:"italic,attr,omitempty"`
	Align     string   `xml:"align,attr,omitempty"`
}

Text renders a single styled line of text.

func NewText

func NewText(content string, opts ...TextOption) *Text

NewText returns a Text widget with the given content and optional style options.

func (*Text) Equals

func (t *Text) Equals(o *Text) bool

Equals checks the logical equality of two Text widgets, ignoring differences that don't affect rendering such as the order of style options.

type TextOption

type TextOption func(*Text)

TextOption configures a Text widget.

func WithAlign

func WithAlign(a string) TextOption

WithAlign sets the text alignment: "left", "center", or "right".

func WithBold

func WithBold() TextOption

WithBold selects the bold (700) weight variant.

func WithColor

func WithColor(c string) TextOption

WithColor sets the text colour as an HTML hex string, e.g. "#ffffff".

func WithCondensed

func WithCondensed() TextOption

WithCondensed selects the Roboto Condensed typeface variant.

func WithFontSize

func WithFontSize(s float32) TextOption

WithFontSize sets the text size in sp.

func WithItalic

func WithItalic() TextOption

WithItalic selects italic style.

func WithLight

func WithLight() TextOption

WithLight selects the light (300) weight variant.

type TextRun

type TextRun struct {
	XMLName       xml.Name `xml:"run"`
	Content       string   `xml:",chardata"`
	FontSize      float32  `xml:"fontSize,attr,omitempty"`
	BaselineShift float32  `xml:"baselineShift,attr,omitempty"`
	Color         string   `xml:"color,attr,omitempty"`
}

TextRun is a single styled text segment within a Label.

func NewRun

func NewRun(content string, opts ...RunOption) *TextRun

NewRun returns a TextRun for use inside a Label.

type VStack

type VStack struct {
	XMLName  xml.Name `xml:"vstack"`
	Children []Widget `xml:"-"`
}

VStack lays out children vertically, top to bottom.

func NewVStack

func NewVStack(children ...Widget) *VStack

NewVStack returns a VStack widget that lays out children top to bottom.

func (*VStack) MarshalXML

func (v *VStack) MarshalXML(e *xml.Encoder, start xml.StartElement) error

MarshalXML implements xml.Marshaler.

func (*VStack) UnmarshalXML

func (v *VStack) UnmarshalXML(d *xml.Decoder, _ xml.StartElement) error

UnmarshalXML decodes the VStack element and its Widget children.

type Widget

type Widget interface {
	// contains filtered or unexported methods
}

Widget is a node in a widget tree passed to Module.Content.

func DecodeWidget

func DecodeWidget(b []byte) (Widget, error)

DecodeWidget decodes an XML-encoded Widget from b. The root element tag determines the concrete Widget type. Unknown root tags return an error; unknown child tags are silently skipped.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL