devtui

package module
v0.2.38 Latest Latest
Warning

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

Go to latest
Published: Jan 13, 2026 License: MIT Imports: 13 Imported by: 0

README ΒΆ

DevTUI

Project Badges

DevTUI is a pure display layer built on Bubbletea that organizes messages from business logic into a clean terminal interface.

🎯 Design Philosophy (Consumer-Driven)

DevTUI follows consumer-driven interface design: your application defines the UI interface, and DevTUI implements it. This ensures:

  • Zero Coupling: Business logic never imports devtui.
  • High Testability: Easily mock UI dependencies.
  • Pluggability: Swap UI implementations without changing core logic.
Integration Interface

Define this in your application to use DevTUI:

type TuiInterface interface {
    NewTabSection(title, description string) any
    AddHandler(handler any, timeout time.Duration, color string, tabSection any)
    RemoveTabSection(section any) // Dynamic removal
    Start(args ...any)            // Optional *sync.WaitGroup support
    RefreshUI()
    ReturnFocus() error
}

πŸš€ Quick Start

func main() {
    tui := devtui.NewTUI(&devtui.TuiConfig{AppName: "Demo", ExitChan: make(chan bool)})

    // 1. Create a section
    ops := tui.NewTabSection("Operations", "System Tasks")
    
    // 2. Add handlers (DevTUI detects implemented interfaces automatically)
    tui.AddHandler(&MyHandler{}, 5*time.Second, "#10b981", ops)

    // 3. Optional: Remove section dynamically
    // tui.RemoveTabSection(ops)

    tui.Start()
}

🧩 Handler Interfaces

DevTUI detects 5 specialized interfaces to determine how to display and interact with your handlers:

Interface Purpose Key Methods
Display Read-only info Name(), Content()
Edit Interactive input Label(), Value(), Change(newVal)
Execution Action buttons Label(), Execute()
Interactive Rich interaction WaitingForUser(), Value(), Change()
Loggable Auto-logging SetLog(func(...any))
πŸ’‘ Clean Terminal Policy

Handlers implementing Loggable receive a logger. DevTUI only displays the most recent message per handler to keep the view focused. Full history is preserved for debugging.

πŸ”„ Animated Progress Logs

For long-running operations, you can use the LogOpen and LogClose prefixes as the first argument to the logger. This triggers an auto-animated spinner and groups messages under the same line.

// Start animation (use "[..." literal or devtui.LogOpen)
handler.log("[...", "Deploying to production")

// ... long operation ...

// Stop animation and show final result (use "...]" literal or devtui.LogClose)
handler.log("...]", "Deployment complete")

⌨️ Navigation & Shortcuts

  • Tab / Shift+Tab: Switch tabs
  • Arrows: Navigate fields / Scroll view
  • Enter / Esc: Execute (Edit) / Cancel
  • Global Shortcuts: Handlers can implement Shortcuts() []map[string]string to register keys (e.g., 't' for test) that work from any tab.

πŸ“š Further Reading


Contributing | Built with Bubbletea

Documentation ΒΆ

Index ΒΆ

Constants ΒΆ

View Source
const (
	LogOpen  = "[..." // Start or update same line with auto-animation
	LogClose = "...]" // Update same line and stop auto-animation
)

LogOpen and LogClose are special prefixes for progress indication. Use LogOpen at the start of a long operation to show an animated spinner. Use LogClose when the operation completes to stop the animation.

Example:

handler.log(devtui.LogOpen, "Deploying to production")
// ... long operation ...
handler.log(devtui.LogClose, "Deployment complete")
View Source
const HandlerNameWidth = 15
View Source
const (
	MCPToolName = "app_get_logs"
)

Variables ΒΆ

This section is empty.

Functions ΒΆ

func NewDisplayHandler ΒΆ

func NewDisplayHandler(h HandlerDisplay, color string) *anyHandler

func NewEditHandler ΒΆ

func NewEditHandler(h HandlerEdit, timeout time.Duration, color string) *anyHandler

func NewExecutionHandler ΒΆ

func NewExecutionHandler(h HandlerExecution, timeout time.Duration, color string) *anyHandler

func NewInteractiveHandler ΒΆ

func NewInteractiveHandler(h HandlerInteractive, timeout time.Duration, color string) *anyHandler

Types ΒΆ

type Cancelable ΒΆ added in v0.2.30

type Cancelable interface {
	Cancel() // Called when user presses ESC to exit interactive mode
}

Cancelable defines the optional interface for handlers that want to be notified when the user cancels. Interactive handlers can implement this to clean up or reset their state when ESC is pressed.

type ColorPalette ΒΆ

type ColorPalette struct {
	// Base (2 colores)
	Foreground string // #F4F4F4
	Background string // #000000

	// Accent (2 colores)
	Primary   string // #FF6600 (tu actual Primary)
	Secondary string // #666666 (tu actual Secondary)

	// Semantic (4 colores)
	Success string // #00FF00
	Warning string // #FFFF00
	Error   string // #FF0000
	Info    string // #00FFFF

	// UI (2-4 colores adicionales)
	Border   string // #444444
	Muted    string // #999999
	Selected string // Derivado de Primary
	Hover    string // Derivado de Primary
}

func DefaultPalette ΒΆ

func DefaultPalette() *ColorPalette

type DevTUI ΒΆ

type DevTUI struct {
	*TuiConfig

	TabSections []*tabSection // represent sections in the tui
	// contains filtered or unexported fields
}

DevTUI mantiene el estado de la aplicaciΓ³n

func NewTUI ΒΆ

func NewTUI(c *TuiConfig) *DevTUI

NewTUI creates a new DevTUI instance and initializes it.

Usage Example:

config := &TuiConfig{
    AppName: "MyApp",
    ExitChan: make(chan bool),
    Color: nil, // or your *ColorPalette
    Logger: func(err any) { os.Stdout.WriteString(fmt.Fmt("%v\n", err)) },
}
tui := NewTUI(config)

func (*DevTUI) AddHandler ΒΆ

func (t *DevTUI) AddHandler(handler any, timeout time.Duration, color string, tabSection any)

AddHandler is the ONLY method to register handlers of any type. It accepts any handler interface and internally detects the type. Does NOT return anything - enforces complete decoupling.

Supported handler interfaces (from interfaces.go):

  • HandlerDisplay: Static/dynamic content display
  • HandlerEdit: Interactive text input fields
  • HandlerExecution: Action buttons
  • HandlerInteractive: Combined display + interaction
  • HandlerLogger: Basic line-by-line logging (via MessageTracker detection)

Optional interfaces (detected automatically):

  • MessageTracker: Enables message update tracking
  • ShortcutProvider: Registers global keyboard shortcuts

Parameters:

  • handler: ANY handler implementing one of the supported interfaces
  • timeout: Operation timeout (used for Edit/Execution/Interactive handlers, ignored for Display)
  • color: Hex color for handler messages (e.g., "#1e40af", empty string for default)
  • tabSection: The tab section returned by NewTabSection (as any for decoupling)

Example:

tab := tui.NewTabSection("BUILD", "Compiler")
tui.AddHandler(myEditHandler, 2*time.Second, "#3b82f6", tab)
tui.AddHandler(myDisplayHandler, 0, "", tab)

func (*DevTUI) ContentView ΒΆ

func (h *DevTUI) ContentView() string

ContentView renderiza los mensajes para una secciΓ³n de contenido

func (*DevTUI) ContentViewPlain ΒΆ added in v0.2.24

func (h *DevTUI) ContentViewPlain(tabIndex int) string

ContentViewPlain renders messages for a content section without ANSI codes (for MCP)

func (*DevTUI) GetMCPToolsMetadata ΒΆ added in v0.2.24

func (d *DevTUI) GetMCPToolsMetadata() []MCPToolMetadata

GetMCPToolsMetadata returns MCP tools provided by DevTUI. This method is called via reflection by mcpserve to discover tools.

func (*DevTUI) GetTabSections ΒΆ added in v0.2.25

func (t *DevTUI) GetTabSections() []any

GetTabSections returns all tab sections as a slice of any for interface compatibility

func (*DevTUI) Init ΒΆ

func (h *DevTUI) Init() tea.Cmd

Init initializes the terminal UI application.

func (*DevTUI) Name ΒΆ added in v0.2.26

func (d *DevTUI) Name() string

Name implements Loggable interface for MCP integration

func (*DevTUI) NewTabSection ΒΆ

func (t *DevTUI) NewTabSection(title, description string) any

NewTabSection creates a new tab section and returns it as any for interface decoupling. The returned value must be passed to the AddHandler method.

Example:

tab := tui.NewTabSection("BUILD", "Compiler Section")
tui.AddHandler(myHandler, 2*time.Second, "#3b82f6", tab)

func (*DevTUI) RefreshUI ΒΆ

func (h *DevTUI) RefreshUI()

RefreshUI updates the TUI display for the currently active tab. This method is designed to be called from external tools/handlers to notify devtui that the UI needs to be refreshed without creating coupling.

Thread-safe and can be called from any goroutine. Only updates the view if the TUI is actively running.

Usage from external tools:

tui.RefreshUI() // Triggers a UI refresh for the active tab

func (*DevTUI) ReturnFocus ΒΆ

func (t *DevTUI) ReturnFocus() error

func (*DevTUI) SetActiveTab ΒΆ added in v0.2.30

func (t *DevTUI) SetActiveTab(section any)

SetActiveTab sets the currently active tab by section reference.

func (*DevTUI) SetLog ΒΆ added in v0.2.26

func (d *DevTUI) SetLog(log func(message ...any))

SetLog implements Loggable interface for MCP integration This allows mcpserve to inject a capturing logger

func (*DevTUI) SetTestMode ΒΆ

func (h *DevTUI) SetTestMode(enabled bool)

SetTestMode enables or disables test mode for synchronous behavior in tests. This should only be used in test files to make tests deterministic.

func (*DevTUI) Start ΒΆ

func (h *DevTUI) Start(args ...any)

Start initializes and runs the terminal UI application.

It accepts optional variadic arguments of any type. If a *sync.WaitGroup is provided among these arguments, Start will call its Done() method before returning.

The method runs the UI using the internal tea engine, and handles any errors that may occur during execution. If an error occurs, it will be displayed on the console and the application will wait for user input before exiting.

Parameters:

  • args ...any: Optional arguments. Can include a *sync.WaitGroup for synchronization.

func (*DevTUI) Update ΒΆ

func (h *DevTUI) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update maneja las actualizaciones del estado

func (*DevTUI) View ΒΆ

func (h *DevTUI) View() string

type HandlerDisplay ΒΆ

type HandlerDisplay interface {
	Name() string    // Full text to display in footer (handler responsible for content) eg. "System Status Information Display"
	Content() string // Display content (e.g., "help\n1-..\n2-...", "executing deploy wait...")
}

HandlerDisplay defines the interface for read-only information display handlers. These handlers show static or dynamic content without user interaction.

type HandlerEdit ΒΆ

type HandlerEdit interface {
	Name() string           // Identifier for logging: "ServerPort", "DatabaseURL"
	Label() string          // Field label (e.g., "Server Port", "Host Configuration")
	Value() string          // Current/initial value (e.g., "8080", "localhost")
	Change(newValue string) // Handle user input + content display via log
}

HandlerEdit defines the interface for interactive fields that accept user input. These handlers allow users to modify values through text input.

type HandlerExecution ΒΆ

type HandlerExecution interface {
	Name() string  // Identifier for logging: "DeployProd", "BuildProject"
	Label() string // Button label (e.g., "Deploy to Production", "Build Project")
	Execute()      // Execute action + content display via log
}

HandlerExecution defines the interface for action buttons that execute operations. These handlers trigger business logic when activated by the user.

type HandlerInteractive ΒΆ

type HandlerInteractive interface {
	Name() string           // Identifier for logging: "ChatBot", "ConfigWizard"
	Label() string          // Field label (updates dynamically)
	Value() string          // Current input value
	Change(newValue string) // Handle user input + content display via log
	WaitingForUser() bool   // Should edit mode be auto-activated?
}

HandlerInteractive defines the interface for interactive content handlers. These handlers combine content display with user interaction capabilities. All content display is handled through progress() for consistency.

type Loggable ΒΆ added in v0.2.25

type Loggable interface {
	Name() string
	SetLog(logger func(message ...any))
}

Loggable defines optional logging capability for handlers. Handlers implementing this receive a logger function from DevTUI when registered via AddHandler.

The log function provided by DevTUI: - Is never nil (safe to call immediately) - Automatically tracks messages by handler Name() - Stores full history internally - Displays only most recent log in terminal (clean view)

Example implementation:

type WasmClient struct {
    log func(message ...any)
}

func NewWasmClient() *WasmClient {
    return &WasmClient{
        log: func(message ...any) {}, // no-op until SetLog called
    }
}

func (w *WasmClient) Name() string { return "WASM" }

func (w *WasmClient) SetLog(logger func(message ...any)) {
    w.log = logger
}

func (w *WasmClient) Compile() {
    w.log("Compiling...")
}

type MCPParameterMetadata ΒΆ added in v0.2.24

type MCPParameterMetadata struct {
	Name        string
	Description string
	Required    bool
	Type        string // "string", "number", "boolean"
	EnumValues  []string
	Default     any
}

MCPParameterMetadata describes a tool parameter. Fields must match mcpserve.ParameterMetadata for reflection compatibility.

type MCPToolMetadata ΒΆ added in v0.2.24

type MCPToolMetadata struct {
	Name        string
	Description string
	Parameters  []MCPParameterMetadata
	Execute     ToolExecutor // Changed from 2-param to 1-param signature to match client pattern
}

MCPToolMetadata provides MCP tool configuration metadata. Fields must match mcpserve.ToolMetadata for reflection compatibility. DevTUI does NOT import mcpserve to maintain decoupling.

type ShortcutEntry ΒΆ

type ShortcutEntry struct {
	Key         string // The shortcut key (e.g., "c", "d", "p")
	Description string // Human-readable description (e.g., "coding mode", "debug mode")
	TabIndex    int    // Index of the tab containing the handler
	FieldIndex  int    // Index of the field within the tab
	HandlerName string // Handler name for identification
	Value       string // Value to pass to Change()
}

ShortcutEntry represents a registered shortcut

type ShortcutProvider ΒΆ

type ShortcutProvider interface {
	Shortcuts() []map[string]string // Returns ordered list of single-entry maps with shortcut->description, preserving registration order
}

ShortcutProvider defines the optional interface for handlers that provide global shortcuts. HandlerEdit implementations can implement this interface to enable global shortcut keys.

type ShortcutRegistry ΒΆ

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

ShortcutRegistry manages global shortcut keys

func (*ShortcutRegistry) Get ΒΆ

func (sr *ShortcutRegistry) Get(key string) (*ShortcutEntry, bool)

func (*ShortcutRegistry) GetAll ΒΆ

func (sr *ShortcutRegistry) GetAll() map[string]*ShortcutEntry

GetAll returns all registered shortcuts for UI display

func (*ShortcutRegistry) List ΒΆ

func (sr *ShortcutRegistry) List() []string

func (*ShortcutRegistry) Register ΒΆ

func (sr *ShortcutRegistry) Register(key string, entry *ShortcutEntry)

func (*ShortcutRegistry) Unregister ΒΆ

func (sr *ShortcutRegistry) Unregister(key string)

type StreamingLoggable ΒΆ added in v0.2.25

type StreamingLoggable interface {
	Loggable
	AlwaysShowAllLogs() bool // Return true to show all messages
}

StreamingLoggable enables handlers to display ALL log messages instead of the default "last message only" behavior.

type ToolExecutor ΒΆ added in v0.2.26

type ToolExecutor func(args map[string]any)

ToolExecutor defines how a tool should be executed

type TuiConfig ΒΆ

type TuiConfig struct {
	AppName  string    // app name eg: "MyApp"
	ExitChan chan bool //  global chan to close app eg: make(chan bool)
	/*// *ColorPalette style for the TUI
	  // if nil it will use default style:
	type ColorPalette struct {
	 Foreground string // eg: #F4F4F4
	 Background string // eg: #000000
	 Primary  string // eg: #FF6600
	 Secondary   string // eg: #666666
	}*/
	Color *ColorPalette

	Logger func(messages ...any) // function to write log error
}

Directories ΒΆ

Path Synopsis
demo command

Jump to

Keyboard shortcuts

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