Documentation
¶
Overview ¶
Package tui provides a bubbletea-based interactive TUI for proto-cli. It implements the protocli.TUIProvider interface, allowing users to browse and invoke gRPC methods through a terminal UI.
Usage:
app, err := protocli.RootCommand("myapp",
protocli.Service(svc),
protocli.WithInteractive(tui.New()),
)
Theming ¶
The quickest way to retheme the TUI is WithTheme, which accepts a Theme value controlling colors, spacing tokens, border shapes, and optional custom styles. All lipgloss styles are derived automatically:
tui.New(tui.WithTheme(bubbles.Theme{
Colors: bubbles.ThemeColors{
Primary: lipgloss.Color("205"),
Secondary: lipgloss.Color("241"),
Accent: lipgloss.Color("213"),
Error: lipgloss.Color("196"),
Success: lipgloss.Color("2"),
Border: lipgloss.Color("238"),
},
}))
Spacing and border shapes can also be customised through the theme:
theme := bubbles.DefaultTheme() theme.Spacing.SM = 3 // wider tab padding theme.Border.Control = lipgloss.NormalBorder() // square control borders tui.New(tui.WithTheme(theme))
Register custom styles in Theme.Custom and retrieve them in your own bubbles via Styles.Get:
theme := bubbles.DefaultTheme()
theme.Custom = map[string]lipgloss.Style{
"badge": lipgloss.NewStyle().Bold(true).Background(lipgloss.Color("57")),
}
tui.New(tui.WithTheme(theme))
// Inside a custom FormControl or ResponseView:
badge := styles.Get("badge").Render("NEW")
For fine-grained per-field adjustments on top of a theme, use WithStyleOverride:
tui.New(
tui.WithTheme(myTheme),
tui.WithStyleOverride(func(s *bubbles.Styles) {
s.FocusedControl = s.FocusedControl.MaxWidth(80)
}),
)
For complete control, replace the full style set with WithStyles:
s := bubbles.DefaultStyles()
s.Title = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("205"))
tui.New(tui.WithStyles(s))
Custom response views ¶
Register a ResponseViewFactory via WithResponseView to replace the default JSON viewport with any bubbletea sub-model:
tui.New(tui.WithResponseView(bubbles.NewTableResponseView()))
Index ¶
- func NavigateToForm(serviceName, methodName string, fieldValues map[string]string) tea.Cmd
- func New(opts ...Option) protocli.TUIProvider
- func ShowModal(title, content string) tea.Cmd
- type NavigateToFormMsg
- type Option
- func WithCustomControl(messageFullName string, factory bubbles.ControlFactory) Option
- func WithCustomControlForField(fieldName string, factory bubbles.ControlFactory) Option
- func WithOptionalTimestampControl(opts ...bubbles.DateTimeControlOption) Option
- func WithResponseView(factory bubbles.ResponseViewFactory) Option
- func WithStyleOverride(fn func(*bubbles.Styles)) Option
- func WithStyles(styles bubbles.Styles) Option
- func WithTheme(theme bubbles.Theme) Option
- func WithTimestampControl(opts ...bubbles.DateTimeControlOption) Option
- type ShowModalMsg
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NavigateToForm ¶
NavigateToForm returns a tea.Cmd that navigates the TUI to the named service/method form, pre-filling any fields specified in fieldValues. Use it from a bubbles.CardSelectHandler to jump context after a card selection:
bubbles.WithOnSelect(func(ctx context.Context, msg proto.Message) bubbles.CardSelectResult {
person := msg.(*pb.PersonCard)
return bubbles.CardSelectCmd(tui.NavigateToForm("greeter", "greet", map[string]string{
"name": person.Name,
}))
})
func New ¶
func New(opts ...Option) protocli.TUIProvider
New creates a new TUI provider. Default styles are applied before any options.
Types ¶
type NavigateToFormMsg ¶
type NavigateToFormMsg struct {
ServiceName string
MethodName string
// Values override the proto-defined default for the matching field.
FieldValues map[string]string
}
NavigateToFormMsg instructs the root model to navigate to a specific method's form screen on a given service, optionally pre-populating named fields. Return it from a CardSelectHandler via CardSelectCmd to jump the user to a form after selecting a card — for example, to pre-fill a recipient name.
type Option ¶
type Option func(*provider)
Option configures the TUI provider.
func WithCustomControl ¶
func WithCustomControl(messageFullName string, factory bubbles.ControlFactory) Option
WithCustomControl registers a custom FormControl factory for a given proto message full name (e.g. "google.protobuf.Timestamp" or "mypackage.MyType"). The factory is called in newFormModel when a TUIFieldKindMessage field with a matching MessageFullName is encountered.
func WithCustomControlForField ¶
func WithCustomControlForField(fieldName string, factory bubbles.ControlFactory) Option
WithCustomControlForField registers a custom FormControl factory for a specific field identified by its flag name (e.g. "metadata", "when"). This works for any field kind — use it when you want to override a scalar, repeated, or message field's default control for a specific field. Name-based registrations take priority over type-based registrations (WithCustomControl / WithTimestampControl), so you can use this to give a particular field different behaviour from the global type-based default.
func WithOptionalTimestampControl ¶
func WithOptionalTimestampControl(opts ...bubbles.DateTimeControlOption) Option
WithOptionalTimestampControl is identical to WithTimestampControl but prepends WithOptionalDefault so the picker starts empty (Value returns "") rather than pre-filled with the current time. Submitting the form without touching the field leaves the timestamp unset — useful for optional filter fields such as "after" and "before" on a list-events request.
func WithResponseView ¶
func WithResponseView(factory bubbles.ResponseViewFactory) Option
WithResponseView registers a factory that creates a custom ResponseView for displaying RPC responses. The default implementation marshals the proto to indented JSON and presents it in a scrollable viewport.
func WithStyleOverride ¶
WithStyleOverride applies a modifier function to the current style set in place. Use this to adjust individual styles after WithPalette (or without replacing the entire set via WithStyles). Options are applied in order, so WithPalette followed by WithStyleOverride works as expected.
func WithStyles ¶
WithStyles replaces the provider's full style set. Obtain the defaults via bubbles.DefaultStyles, modify the fields you care about, then pass the result here.
func WithTheme ¶
WithTheme rebuilds the full style set from the given theme. The theme controls colors, spacing tokens, border shapes, and any custom styles forwarded to Styles.Custom. Use WithStyleOverride afterwards to tweak any individual style that the theme does not capture.
func WithTimestampControl ¶
func WithTimestampControl(opts ...bubbles.DateTimeControlOption) Option
WithTimestampControl registers an interactive date+time picker as the custom form control for google.protobuf.Timestamp fields. Use this instead of the default RFC3339 text input when you want a segment-by-segment picker UI.
Pass DateTimeControlOption values to configure timezone behaviour:
tui.WithTimestampControl(
bubbles.WithInputTimezone(bubbles.UserProvidedTimezone),
bubbles.WithTZNormalization(bubbles.RetainSourceTimezone),
)
WithOptionalTimestampControl is a variant that starts empty instead of defaulting to the current time. Prefer it for optional filter fields (e.g. "after", "before") where leaving the field blank should mean "no filter".
type ShowModalMsg ¶
type ShowModalMsg struct {
// Title is rendered at the top of the modal box. May be empty.
Title string
// Content is the body of the modal box.
Content string
}
ShowModalMsg is a bubbletea message that instructs the root model to display a modal overlay on top of the current screen. Return it from a bubbles.CardSelectHandler to pop up a modal when a card is selected:
bubbles.WithOnSelect(func(msg proto.Message) tea.Cmd {
return tui.ShowModal("Selected", renderContent(msg))
})