Documentation
¶
Overview ¶
Package tui implements the bubbletea-based dev runtime that backs `hamr dev`. The headless dev runner lives in internal/devserver; this package owns the screen and adapts hotkeys, log output, and status updates between the two.
Index ¶
- type DockerLogLineMsg
- type HotkeySource
- type LogLineMsg
- type Model
- type Runtime
- func (r *Runtime) AppendMCPLog(line string)
- func (r *Runtime) HotkeyActions() <-chan devserver.HotkeyAction
- func (r *Runtime) Log(line string)
- func (r *Runtime) Quit()
- func (r *Runtime) RegisterDockerStacks(names []string) map[string]io.Writer
- func (r *Runtime) SetMCPStatus(enabled bool, tools int)
- func (r *Runtime) SetProxyURL(url string)
- func (r *Runtime) SetVersion(label string)
- func (r *Runtime) SetVersionStatus(status devserver.VersionStatus, msg string)
- func (r *Runtime) SetVersionUpdateIfOK(msg string)
- func (r *Runtime) Start() error
- func (r *Runtime) Wait(ctx context.Context)
- func (r *Runtime) Wire(opts []devserver.Option) []devserver.Option
- type Sink
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type DockerLogLineMsg ¶
DockerLogLineMsg carries one line from `docker compose logs -f` for a specific compose entry. The model routes these to a per-entry buffer so Tab can cycle between hamr logs and one log view per docker stack.
type HotkeySource ¶
type HotkeySource struct {
// contains filtered or unexported fields
}
HotkeySource is the bubbletea-side implementation of devserver.HotkeySource. The model dispatches r/o/q keys to Send; the runner's hotkey loop consumes them via Actions().
c (clear) and m (run) are handled inside the model and never enter this channel — c clears the viewport, m opens the Makefile-target fuzzy palette.
func NewHotkeySource ¶
func NewHotkeySource() *HotkeySource
NewHotkeySource returns a buffered source. The buffer absorbs short bursts (e.g. mash 'r' a few times) without blocking the bubbletea Update loop.
func (*HotkeySource) Actions ¶
func (h *HotkeySource) Actions() <-chan devserver.HotkeyAction
Actions implements devserver.HotkeySource.
func (*HotkeySource) Send ¶
func (h *HotkeySource) Send(a devserver.HotkeyAction)
Send pushes an action non-blockingly. If the buffer is full the action is dropped — the runner is already busy and another keypress is the user's problem to repeat.
type LogLineMsg ¶
type LogLineMsg string
LogLineMsg carries one fully-formed line from a subprocess or the dev runner's slog handler. The bubbletea model appends it to the viewport.
type Model ¶
type Model struct {
// contains filtered or unexported fields
}
Model is the top-level bubbletea model for hamr dev.
func NewModel ¶
func NewModel(hotkeys *HotkeySource) *Model
NewModel constructs a model that pushes hotkeys to the given source.
type Runtime ¶
type Runtime struct {
// contains filtered or unexported fields
}
Runtime bundles the bubbletea program with the adapters the dev runner needs (hotkey source, log sink). Keep references on the same value so the dev command can wire them via WithHotkeys / WithLogWriter / WithProcessOutput / WithActionsHook in one place.
func NewRuntime ¶
func NewRuntime() *Runtime
NewRuntime builds the TUI runtime. Call Wire on a Runner before its Run to feed all the right adapters in. Call Start to run the program (it blocks). The recommended flow is in dev.go:
rt := tui.NewRuntime()
go func() { runErr <- runDevLoop(ctx, rt, configPath, ...) }()
rt.Start() // blocks until the model returns tea.Quit
<-runErr
func (*Runtime) AppendMCPLog ¶
AppendMCPLog pushes one MCP request line to the model's dedicated MCP tab. Wired via WithMCPLogHook; fires on each gateway request. Safe from any goroutine.
func (*Runtime) HotkeyActions ¶
func (r *Runtime) HotkeyActions() <-chan devserver.HotkeyAction
HotkeyActions exposes the underlying hotkey channel so the dev runner can react to q / Ctrl+C while parked outside Run() — e.g. waiting for a config fix, where bubbletea owns the keyboard but the runner-side loop has nothing else to select on.
func (*Runtime) Log ¶
Log writes a single line to the TUI viewport. Intended for the dev command's own status messages (config errors, "config changed, retrying...") that don't flow through the runner's slog handler.
func (*Runtime) Quit ¶
func (r *Runtime) Quit()
Quit asks the program to exit. Safe to call from any goroutine; if the program has already exited this is a no-op.
func (*Runtime) RegisterDockerStacks ¶
RegisterDockerStacks publishes the ordered list of compose-entry names to the model (so Tab cycles through them and the status bar labels each tab) and returns the per-entry io.Writer map the runner should pass to WithDockerLogSinks. Sinks are created lazily and cached, so a config reload that re-registers the same name reuses the same docker sink (its already-buffered lines stay buffered until the new follower process emits more).
func (*Runtime) SetMCPStatus ¶
SetMCPStatus publishes the MCP gateway's state to the model's status-bar indicator. Wired via WithMCPStatusHook; fires at startup and on each M-toggle. Safe from any goroutine.
func (*Runtime) SetProxyURL ¶
SetProxyURL publishes the actual reachable proxy URL to the model so it can render in the status bar. Wired via WithProxyURLHook so the runner calls it once the listener has bound. Safe from any goroutine.
func (*Runtime) SetVersion ¶
SetVersion sets the version label shown on the right of the status bar. Safe from any goroutine.
func (*Runtime) SetVersionStatus ¶
func (r *Runtime) SetVersionStatus(status devserver.VersionStatus, msg string)
SetVersionStatus updates the version indicator state and message.
func (*Runtime) SetVersionUpdateIfOK ¶
SetVersionUpdateIfOK promotes the indicator to VersionUpdate only when the current status is VersionOK. Returning a bool would require synchronous access to the model state; the message is always sent and the model applies the guard. The caller logs the "update available" line unconditionally.
func (*Runtime) Start ¶
Start runs the bubbletea program. Blocks until the model returns tea.Quit (q / Ctrl+C inside the model, or an external Quit call).
type Sink ¶
type Sink struct {
// contains filtered or unexported fields
}
Sink is an io.Writer that emits one tea.Msg per newline-terminated line. toMsg builds the message from each line, so the same line-batching logic serves both the runner's combined log (LogLineMsg) and a docker stack's tagged log (DockerLogLineMsg) — see NewSink and NewDockerSink.
One Sink can fan in stdout, stderr, and the runner's slog writer because the only thing it does with each line is post a tea.Msg — colors and prefixes are preserved upstream by prefixWriter.
func NewDockerSink ¶
NewDockerSink returns a sink scoped to one docker compose stack, emitting DockerLogLineMsg tagged with the stack's name from hamr.toml (`[[dev.docker_compose]] name = ...`). The runtime binds the program shortly after construction.
func NewSink ¶
func NewSink() *Sink
NewSink returns a sink emitting LogLineMsg per line. It drops lines until Bind is called; the dev runtime binds the program right after constructing it, so the drop window is bounded by the few lines emitted between sink creation and program start — acceptable for a demo runtime.