A2UI ↔ Bubble Tea renderer bridge.
a2tea maps A2UI JSON messages onto
Bubble Tea models so AI agents
can drive rich terminal UI from a structured wire format instead of dumping
raw text. The eventual consumer is Joe's fork of
charmbracelet/crush — this
library is the bridge that lets an agent speak A2UI and have crush draw the
result.
This repository is currently scaffolding. The public API surface is fixed, but the rendering and event roundtrip are stubbed. See the Roadmap for what is and is not yet implemented.
package main
import (
"encoding/json"
"log"
"os"
tea "charm.land/bubbletea/v2"
"github.com/joestump/a2tea"
)
func main() {
raw, err := os.ReadFile("sample.json")
if err != nil {
log.Fatal(err)
}
model, err := a2tea.Render(json.RawMessage(raw))
if err != nil {
log.Fatal(err)
}
if _, err := tea.NewProgram(model).Run(); err != nil {
log.Fatal(err)
}
}A runnable version of the same flow lives in
examples/hello. Today it prints a [a2tea: card]
placeholder; once the renderers land it will draw a real card.
github.com/joestump/a2tea— public entry point.Render(raw) (tea.Model, error).github.com/joestump/a2tea/component— typed union of A2UI components and the JSON unmarshaler.github.com/joestump/a2tea/render— onetea.Modelper component kind, plus aFor(c)dispatcher.github.com/joestump/a2tea/event— outboundtea.Msgtypes:ButtonClicked,InputSubmitted,ChoiceSelected.
What is not yet implemented (and is currently marked with
// TODO(a2tea): in source):
- JSON unmarshaling.
component.Unmarshalonly reads thekinddiscriminator. Field-level decoding, schema validation, and nestedchildrenhandling are stubs. - Real renderers. Every
render/*Modelreturns a[a2tea: <kind>]placeholder. The real implementations should usecharm.land/bubbles/v2(textinput, list, progress),charm.land/glamour/v2(markdown), andcharm.land/lipgloss/v2(layout). - Form support.
FormModelshould wraphuh.Formso field navigation, validation, and submission work without bespoke code. Thehuhdependency is intentionally not ingo.modyet — the publishedcharmbracelet/huh v1.0.0pins oldercharm.land/x/ansiinternals that conflict with the v2 stack this module shares with crush. Pull it back in once a huh release compatible with the v2 ecosystem ships. - Event roundtrip. The types in
event/are defined but no renderer emits them yet. Once they do, agents will consume them from the standardtea.Msgchannel inside their ownUpdate. - Live streaming.
StreamModelonly accepts a staticChunksslice; there's no channel-based live append yet.
This is pre-1.0 and the public API may change. Once the renderers and
event roundtrip are real and exercised by a downstream consumer (crush),
a v0.1.0 tag will be cut.
Apache 2.0 — see LICENSE.