Help us shape the future of Go! Take the 2026 Go Developer Survey

luimygo

package module
v0.0.0-...-c956624 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 37 Imported by: 0

README

MyGo backend

luimygo renders LUI's retained declarative component tree with MyGo native UI. The OCaml application owns state, reducers, subscriptions and patches. Standard portable components use built-in adapters; Config.Renderers is reserved for deliberate overrides.

See the API migration guide before updating an existing application. The generated support inventory lists every component, applicable property, event, feature exception and test reference. The parity plan tracks remaining feature audits and desktop qualification; a built-in kind is not by itself evidence that every property has been verified.

Run the real applications

Requirements: Go 1.27.3 or later and the repository's OCaml/opam environment. Linux windows need GTK3; headless text needs Pango, Cairo and installed fonts. Chinese, Japanese and Korean text also requires installed CJK fonts; on Debian/Ubuntu install fonts-noto-cjk so native font fallback can render these glyphs. The module pins the logseq/mygo compatibility branch, based on MyGo 0.6.4, for local fitting, list tuning, frame completion, overlay anchoring and native lifecycle fixes, including abandoned handle bindings. The pin also preserves committed input capture, text/container geometry and live native-input snapshot APIs. Go downloads it as a normal versioned dependency; LUI does not include a copy of the third-party source tree.

From the repository root on macOS or Linux:

library=$(./tooling/build_mygo_todos.sh)
go -C platform/mygo run ./cmd/lui-mygo -library "$library"

components=$(./tooling/build_mygo_components.sh)
go -C platform/mygo run ./cmd/lui-mygo -library "$components" -title Components

Each application runs in its own process. The scripts build existing library and archive targets and link the shared C bridge; they do not build the Native OCaml renderer or alter Dune files. Windows hosts accept an equivalent OCaml DLL built with the Windows toolchain.

For a headless image of the actual application:

go -C platform/mygo run ./cmd/lui-mygo -library "$library" \
  -screenshot /tmp/lui-mygo-todos.png -width 800 -height 600

Embed a backend

backend := luimygo.New(luimygo.Config{})
defer backend.Close()
session, err := luimygo.OpenRuntime(libraryPath, backend)
if err != nil {
    return err
}
defer session.Close()
mygo.App.WhenReady(func() {
    mygo.NewWindow(mygo.WindowOptions{
        Title: "LUI",
        Content: ui.View(backend.View),
    })
})
return mygo.App.Run()

All backend and session methods belong to the UI goroutine. Use one backend per window, surface Err() failures, close the session before the backend, and retain application model ownership of controlled values and popup visibility. A process loads one OCaml runtime library and runs one session at a time. A later session reuses its registered OS thread; the runtime library stays loaded.

Go hosts keep importing github.com/egoist/mygo and github.com/egoist/mygo/ui. The fork preserves the original module identity and upstream links. The adapter uses a pinned Go replacement to select the compatibility changes. Go does not inherit replacements from dependency modules, so an embedding application's root go.mod must select the same fork version. To develop against a local LUI checkout from another module:

go mod edit -replace github.com/logseq/lui/platform/mygo=/path/to/lui/platform/mygo
go mod edit -replace github.com/egoist/mygo=github.com/logseq/mygo@v0.0.0-20261025230925-e952b021e9be
go get github.com/logseq/lui/platform/mygo
go mod tidy

For another transport, configure Dispatch and call ApplyJSON(data,false) for deltas or ApplyJSON(data,true) for an authoritative snapshot. Decoded callers can use ApplyBatch and ApplySnapshot. The adapter validates complete final contracts atomically, including property types, required labels, child shapes, size bounds, popup anchors and image crops. Invalid batches preserve live UI identity and asynchronous work.

Components and resources

Built-in adapters cover the 87 schema kinds, including local stacked/overlay layout, edge insets, measured fitting, controls, menus and submenus, tabs and bottom-tab pages, trees, bounded virtual lists, rich table cells, split panes, composites, images/media, file services and modal/anchored surfaces. Detailed feature qualification remains tracked by the parity plan.

LUI's model supplies retained selection, menu membership and popup lifetime. MyGo behavior bases supply native focus, keyboard interaction and accessibility. Stable node incarnation keys preserve editors across moves. Eager virtual lists retain every logical row. With virtual_rows, rows outside the requested window are disposed unless an active interaction pins them; persistent row state belongs in the application model. Detached nodes cancel owned work immediately after commit. Events from late row builders and fitting layouts dispatch after committed layout, while callbacks from retired incarnations are ignored.

For large application data sources, opt into bounded LUI row generation:

virtual_list ~height:400 ~overscan:5 ~item_extent:32. [
  virtual_rows ~source:rows ~key:(fun row -> row.id) ~cmp:Int.compare
    ~scroll_key:(fun row -> Printf.sprintf "row-%d" row.id)
    ~mount:row_view ()
]

virtual_rows must be the sole direct child. The full business source and compact key index remain available while only the window, overscan and active interaction pins own UI nodes. scroll_key is required when using string scroll targets. Focus, pointer holds, composition and owned menus retain their rows through the toolkit's existing lifecycle. Evicted rows mount afresh, so bind editor values and selection to application data. Rebuild the application library to enable the optional window-feedback ABI; older libraries keep eager behavior. Web also supports the helper; SwiftUI and Kotlin currently use its eager fallback.

Image hosts call RegisterImage(id,pixels) or PresentMediaSurfaceFrame(id,pixels) on the UI goroutine. The backend copies caller buffers. Invalid replacements leave prior resources intact; unregister methods or Close() release registrations. Canonical SVG icons are built in; custom app:slug icons use RegisterIcon(name,svg) and UnregisterIcon(name). URI/file decoding is bounded, cancellable and incarnation-aware.

Config.FileServices optionally replaces the OS dialog boundary. Its default uses native MyGo dialogs; request correlation, completion acknowledgement, retained identity and event payloads remain backend-owned. File previews use an owned modal with selectable UTF-8 text, images and paginated PDF rendering; missing/unsupported files show an owned error. Native cancellation and live host checks are release gates recorded in the parity plan.

Generic CSS transition declarations, DOM tags/classes/attributes, browser link targets and browser referrer policies remain Web features. Typed legacy virtual attributes retain their semantic compatibility behavior. This desktop C bridge cannot run in a browser; the existing LUI DOM backend remains available.

Extensions

Config.Extensions registers actual custom nodes by identifier, fingerprint, scalar defaults and renderer. Renderers receive a detached Node snapshot, a child builder and an emitter. Build children inside MyGo's Children callback; never retain a pass-scoped ui.Element. Extension events use Event{Kind:"extension-event",Identifier:...,Name:...,Values:...}. Standard portable components do not require application renderer registrations.

Reproduce tests

make test-mygo
./tooling/test_mygo.sh --unit
make test-mygo-integration

Unit mode checks schema freshness and runs real MyGo input/layout/pixel tests with the race detector. Wire allocation budgets run separately in the production configuration because race instrumentation changes allocation counts. It covers controlled rejection/normalization, focus, keyboard and timed gestures, menus, local fitting, 10,000-row virtualization, scroll tokens/ranges, resource pixels/retirement and actual PDF pages. Generated validation is checked against more than a million actual OCaml decisions in integration mode. Exported compiler data and oracle vectors live in temporary directories; only generated Go source and a small integrity manifest are tracked. Generation drift tests reject stale sources and edited output.

Integration mode requires OCaml, generates and checks the canonical oracle, builds both application libraries, runs Todos and Components in separate Go processes, then runs the fresh-process startup soak. Components verifies bounded 100,000-row generation and every standard event family through real retained subscriptions and its reducer, including UTF-8, embedded NULs and exact large integer payloads. Todos covers real editing, actions, resync, disposal and restart. Startup qualification preserves signal-stack/mask/handler ownership and exercises profiling, scheduler preemption, queued signals and repeated sessions without retrying failed processes.

Plain go test uses documented unit mode by skipping native suites without the relevant library variable. Explicit commands must use separate processes:

LUI_MYGO_TEST_LIBRARY="$library" go -C platform/mygo test -race -count=1 -run '^TestOCamlTodos' .
LUI_MYGO_COMPONENTS_LIBRARY="$components" go -C platform/mygo test -race -count=1 -run '^TestOCamlComponents' .
go -C platform/mygo test -run '^$' -fuzz FuzzApplyJSON -fuzztime 30s
go -C platform/mygo vet ./...

Regenerate authoritative metadata after canonical API/protocol changes:

make generate-component-schema
make generate-json-view

The exporter links the existing compiled OCaml library and derives kind, property, child, event and final-value rules. Full export and oracle data are temporary build inputs. CI freshness checks use the compact committed manifest and need no OCaml compiler. Go/race/fuzz/vet gates cover macOS, Linux and Windows; real OCaml integration runs where the desktop OCaml toolchain is configured. Native OCaml/Metal renderer work is outside this parity task.

Documentation

Overview

Package luimygo renders LUI patch batches with MyGo native UI. Backend methods belong to the UI goroutine. Runtime serializes C ABI calls on a dedicated OS thread and delivers copied batches back to that goroutine.

Code generated by tooling/generate_mygo_schema.py; DO NOT EDIT.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ComputeThemeVariables

func ComputeThemeVariables(parent, declarations map[string]string) map[string]string

ComputeThemeVariables computes declarations before inheritance. Invalid or cyclic declarations remove an inherited value; an absent declaration keeps it. Parent values must already be computed, not raw declarations from an ancestor.

func ResolveThemeValue

func ResolveThemeValue(value string, variables map[string]string) (string, bool)

ResolveThemeValue substitutes variables from a computed, inherited variable map. Missing/invalid references use their fallback, including an empty one.

func SupportedKinds

func SupportedKinds() []string

Types

type Backend

type Backend struct {
	// contains filtered or unexported fields
}

func New

func New(config Config) *Backend

func (*Backend) ApplyBatch

func (b *Backend) ApplyBatch(batch Batch) error

func (*Backend) ApplyJSON

func (b *Backend) ApplyJSON(data []byte, snapshot bool) error

func (*Backend) ApplySnapshot

func (b *Backend) ApplySnapshot(batch Batch) error

func (*Backend) BindHostElement

func (b *Backend) BindHostElement(id int64, element ui.Element) error

BindHostElement gives an extension or custom renderer's primary control the retained node's host identity. Call during rendering, on the UI thread.

func (*Backend) Bounds

func (b *Backend) Bounds(c *ui.Context, id int64) (ui.Rect, bool)

Bounds reads a nonempty committed box in window DIPs, never a speculative layout. Missing, hidden, unmounted and closed nodes report false.

func (*Backend) Close

func (b *Backend) Close()

Close releases node services and registered host resources. Call it on the UI goroutine after closing the OCaml session; repeated calls are harmless.

func (*Backend) CommittedBounds

func (b *Backend) CommittedBounds(c *ui.Context, id int64) (ui.Rect, bool)

CommittedBounds returns the transformed window-space box of a committed node, rejecting model changes that have not reached layout yet.

func (*Backend) CommittedScrollOffset

func (b *Backend) CommittedScrollOffset(c *ui.Context, id int64) (x, y float32, known bool)

CommittedScrollOffset reads an actual committed scroll host's offset in DIPs, including ordinary overflow:auto containers. Unlike ScrollOffset it never reports unapplied commands. Non-scrollers, missing, hidden, stale or uncommitted nodes report known=false. Call after layout on the UI thread.

func (*Backend) Err

func (b *Backend) Err() error

Err reports the first asynchronous event-dispatch failure.

func (*Backend) Generation

func (b *Backend) Generation() int64

func (*Backend) InputNode

func (b *Backend) InputNode(target ui.InputTarget) (InputNode, bool)

InputNode resolves the closest retained host binding on MyGo's actual focus or hit ancestry. Call only inside Context.CaptureInput, on the UI thread. Unbound internal control children resolve to their closest retained parent; missing, hidden, disabled, closed and expired targets do not resolve.

func (*Backend) InputTextSnapshot

func (b *Backend) InputTextSnapshot(c *ui.Context, id int64) (text string, anchor, caret int, ok bool)

InputTextSnapshot reads the actual native TextInput/TextArea's consumed edit state, not the model text property or a frame cache. Offsets count runes and preserve anchor/caret direction. Call on the UI thread in CaptureInput before default handling, or OnChange after input. A window-stable Context is safe for this query outside a build. Missing, hidden, disabled, retired, unbound, non-input and closed controls report false, not an invented empty selection. Custom/raw renderers must BindHostElement to their actual input each build; standard text-field/input/textarea controls are already bound.

func (*Backend) NaturalImageSize

func (b *Backend) NaturalImageSize(id int64) (width, height float32, ready bool)

NaturalImageSize reports cached decoded pixel dimensions for a retained image, file-image or avatar. Call on the UI goroutine. It does not start decoding or use layout, crop dimensions, avatar fallbacks or assets from retired nodes.

func (*Backend) Node

func (b *Backend) Node(id int64) (Node, bool)

func (*Backend) OpenMenuOwners

func (b *Backend) OpenMenuOwners(c *ui.Context) []InputNode

OpenMenuOwners returns detached snapshots of actual open menu/popover owners, including anchors outside a portal. Query after layout or between frames on the UI thread. Only committed presentations and live retained incarnations resolve; this does not change input, focus or presentation.

func (*Backend) PresentMediaSurfaceFrame

func (b *Backend) PresentMediaSurfaceFrame(id int64, pixels image.Image) error

PresentMediaSurfaceFrame copies an immutable frame into the named surface. Replacement keeps the existing toolkit texture allocation where possible.

func (*Backend) RegisterIcon

func (b *Backend) RegisterIcon(name string, svg []byte) error

RegisterIcon registers an application SVG under a canonical app:slug name. Parsing owns the document; later mutation of the supplied bytes is harmless.

func (*Backend) RegisterImage

func (b *Backend) RegisterImage(id int64, pixels image.Image) error

RegisterImage copies pixels into a host-owned resource. Call it on the UI goroutine; retained nodes reference the resource independently of their lives.

func (*Backend) RequestFocus

func (b *Backend) RequestFocus(id int64) error

RequestFocus waits for this node's next binding if it has not rendered yet. It does not acknowledge focus or change any model-controlled property.

func (*Backend) ResolveColor

func (b *Backend) ResolveColor(c *ui.Context, s string) (ui.Color, error)

ResolveColor uses the same current scoped tokens as standard typed paint properties. Call from a renderer while building; do not retain Context. Unlike the internal legacy paint fallback, an unresolved value is an error.

func (*Backend) Roots

func (b *Backend) Roots() []int64

func (*Backend) ScrollOffset

func (b *Backend) ScrollOffset(id int64) (x, y float32, err error)

ScrollOffset reads the retained offset, including an unapplied ScrollTo. The next committed layout clamps that offset to the content extent.

func (*Backend) ScrollTo

func (b *Backend) ScrollTo(id int64, x, y float32) error

ScrollTo sets an immediate offset in DIPs. Token-controlled scroll requests retain ownership until completion; use their standard protocol to replace one.

func (*Backend) SetThemeDefaults

func (b *Backend) SetThemeDefaults(tokens map[string]string) error

SetThemeDefaults replaces this window's host-owned tokens. It does not emit patches or modify application nodes. Nested theme props override these values only for their subtree. Call between frames on the UI goroutine; nil clears.

func (*Backend) TextGeometry

func (b *Backend) TextGeometry(c *ui.Context, id int64) (ui.CommittedText, bool)

TextGeometry reads the exact committed shaped text of a standard text node, including its range in a rich inline paragraph. Offsets are rune-relative to this node; coordinates are window DIPs. Query after layout or between frames on the UI thread. The returned value expires on the next MyGo build. Missing, hidden, detached, unmounted and closed nodes return false.

func (*Backend) UnregisterIcon

func (b *Backend) UnregisterIcon(name string)

func (*Backend) UnregisterImage

func (b *Backend) UnregisterImage(id int64)

func (*Backend) UnregisterMediaSurface

func (b *Backend) UnregisterMediaSurface(id int64)

func (*Backend) View

func (b *Backend) View(c *ui.Context)

type Batch

type Batch struct {
	Generation int64       `json:"generation"`
	Ops        []Operation `json:"ops"`
}

func DecodeBatch

func DecodeBatch(data []byte) (Batch, error)

DecodeBatch keeps integer IDs exact, including IDs beyond JavaScript's float64 integer range. Required fields must be present even when zero.

type Config

type Config struct {
	FileServices FileServices
	// Dispatch synchronously delivers a model-owned interaction proposal.
	// Apply accepted or normalized property updates before returning. If the
	// controlled property is unchanged, the native proposal is rejected.
	Dispatch   func(Event) error
	Extensions map[string]Extension
	// Renderers deliberately overrides built-in standard component behavior.
	Renderers map[string]Renderer
	// InputValue applies host-owned text commands before a standard text editor
	// is constructed. InputControl then binds its native focus and selection.
	// Both run during live construction on the UI thread, never fitting probes.
	// Do not retain the value pointer or Element; neither hook emits model events.
	InputValue   func(Node, *string)
	InputControl func(Node, ui.Element, string)
}

type Event

type Event struct {
	Kind        string         `json:"kind"`
	Node        int64          `json:"node"`
	Text        string         `json:"text,omitempty"`
	Checked     bool           `json:"checked,omitempty"`
	Value       float64        `json:"value,omitempty"`
	Modifiers   int32          `json:"modifiers,omitempty"`
	Identifier  string         `json:"identifier,omitempty"`
	Name        string         `json:"name,omitempty"`
	Values      map[string]any `json:"values,omitempty"`
	X           float64        `json:"x,omitempty"`
	Y           float64        `json:"y,omitempty"`
	Button      int32          `json:"button,omitempty"`
	TargetClass string         `json:"target-class,omitempty"`
	Token       int64          `json:"token,omitempty"`
	Outcome     string         `json:"outcome,omitempty"`
	Generation  int64          `json:"generation,omitempty"`
	Pins        []int64        `json:"pins,omitempty"`
	First       int64          `json:"first"`
	Last        int64          `json:"last"`
	AtStart     bool           `json:"at-start"`
	AtEnd       bool           `json:"at-end"`
	Key         string         `json:"key,omitempty"`
	Shift       bool           `json:"shift,omitempty"`
	Payload     string         `json:"payload,omitempty"`
	// contains filtered or unexported fields
}

type Extension

type Extension struct {
	Fingerprint string
	Defaults    map[string]any
	Render      Renderer
}

type FileDialogOptions

type FileDialogOptions struct {
	Multiple  bool
	Directory bool
	Accept    []string
	Types     []string
}

FileDialogOptions describes portable desktop selection constraints.

type FileServices

type FileServices interface {
	OpenFiles(context.Context, FileDialogOptions) ([]string, error)
}

FileServices is the operating-system dialog boundary. The backend keeps request correlation, retained lifetimes and event delivery on its UI thread. OpenFiles may block; implementations should observe cancellation where their native dialog API permits it. Nil paths represent user cancellation.

type InputNode

type InputNode struct {
	Node Node
	// contains filtered or unexported fields
}

InputNode is a snapshot of a retained node resolved from committed MyGo input. Current rejects removed or remounted node IDs, including reuse between a pointer press and release. Node is a copy and must not be changed.

func (InputNode) Current

func (n InputNode) Current() bool

Current reports whether the same visible retained incarnation still exists.

type Node

type Node struct {
	ID                            int64
	Kind, Identifier, Fingerprint string
	Parent                        int64
	Children                      []int64
	Props                         map[string]any
}

Node is a detached read snapshot. Parent is zero for a root; wire IDs are positive.

func (Node) Bool

func (n Node) Bool(k string) bool

func (Node) Number

func (n Node) Number(k string, fallback float64) float64

func (Node) String

func (n Node) String(k string) string

type Operation

type Operation struct {
	Op          string `json:"op"`
	ID          int64  `json:"id,omitempty"`
	Parent      int64  `json:"parent,omitempty"`
	Child       int64  `json:"child,omitempty"`
	Index       int    `json:"index"`
	Kind        string `json:"kind,omitempty"`
	Property    string `json:"property,omitempty"`
	Identifier  string `json:"identifier,omitempty"`
	Fingerprint string `json:"fingerprint"`
	Value       any    `json:"value,omitempty"`
}

type Renderer

type Renderer func(*ui.Context, Node, func(), func(Event))

Renderer builds a custom control, calling children within its container. emit queues an event for delivery after construction; never retain Context.

type Runtime

type Runtime struct {
	// contains filtered or unexported fields
}

Runtime owns a session in an OCaml shared library exporting lui_ocaml_*. OpenRuntime and all session methods are called on the backend's UI goroutine. A process can host one OCaml runtime library and one active session.

func OpenRuntime

func OpenRuntime(path string, b *Backend) (*Runtime, error)

func (*Runtime) Close

func (r *Runtime) Close() error

Close disposes the app once. The runtime library stays loaded because OCaml domains and registered callbacks cannot safely be unloaded from a process.

func (*Runtime) Dispatch

func (r *Runtime) Dispatch(e Event) error

func (*Runtime) Resync

func (r *Runtime) Resync() error

Directories

Path Synopsis
cmd
lui-mygo command
lui-mygo hosts an OCaml LUI shared library in a native MyGo window.
lui-mygo hosts an OCaml LUI shared library in a native MyGo window.

Jump to

Keyboard shortcuts

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