Reusable message presentation system for Go development tools. DevTUI is a pure display layer built on bubbletea that formats and organizes messages from your business logic handlers.
What DevTUI does: Takes messages from your handlers via progress()
callbacks and displays them in a clean, organized terminal interface with tabs, navigation, and automatic formatting.
What DevTUI doesn't do: Validate data, handle errors, or manage business logic. Your handlers are responsible for their own state and decisions - DevTUI just shows whatever they tell it to show.
β Why DevTUI? - Complete purpose and functionality description
If your handler implements both the HandlerEdit interface and a Content() method, DevTUI will only refresh the display when the value changes, and will not create a new timestamped message. This is ideal for cases like language selectors, dashboards, or any field where the content is always shown in a custom format.
Example:
type LanguageHandler struct {
lang string
}
func (h *LanguageHandler) Name() string { return "Language" }
func (h *LanguageHandler) Label() string { return "Language" }
func (h *LanguageHandler) Value() string { return h.lang }
func (h *LanguageHandler) Change(newValue string, progress func(msgs ...any)) {
h.lang = newValue
progress("Language changed to", newValue) // Will only refresh, not create a message
}
func (h *LanguageHandler) Content() string {
return "Current language: " + h.lang
}
When the user changes the value, the UI will update the content, but no new message will appear in the message area. This behavior is now fully tested and guaranteed by the DevTUI test suite.
DevTUI uses specialized handler interfaces that require minimal implementation. Here is a complete example using the new simplified API:
// HandlerExecution with MessageTracker - Action buttons with progress tracking
type BackupHandler struct {
lastOpID string
}
func (h *BackupHandler) Name() string { return "SystemBackup" }
func (h *BackupHandler) Label() string { return "Create System Backup" }
func (h *BackupHandler) Execute(progress func(msgs ...any)) {
progress("Preparing backup...")
time.Sleep(200 * time.Millisecond)
progress("Backing up database...")
time.Sleep(500 * time.Millisecond)
progress("Backup completed successfully")
}
// MessageTracker implementation for operation tracking
func (h *BackupHandler) GetLastOperationID() string { return h.lastOpID }
func (h *BackupHandler) SetLastOperationID(id string) { h.lastOpID = id }
func main() {
tui := devtui.NewTUI(&devtui.TuiConfig{
AppName: "Demo",
ExitChan: make(chan bool),
Color: &devtui.ColorPalette{
Foreground: "#F4F4F4",
Background: "#000000",
Primary: "#FF6600",
Secondary: "#666666",
},
Logger: func(messages ...any) {
// Replace with actual logging implementation
},
})
// Operations tab with ExecutionHandlers (action buttons)
ops := tui.NewTabSection("Operations", "System Operations")
ops.AddExecutionHandler(&BackupHandler{}, 5*time.Second) // Automatic MessageTracker detection
var wg sync.WaitGroup
wg.Add(1)
go tui.Start(&wg)
wg.Wait()
}
π See complete example with all handler types
DevTUI provides 6 specialized handler types, each requiring minimal implementation:
type HandlerDisplay interface {
Name() string // Full text to display in footer
Content() string // Content shown immediately
}
β See complete implementation example
type HandlerEdit interface {
Name() string // Unique identifier for logging
Label() string // Field label
Value() string // Current/initial value
Change(newValue string, progress func(msgs ...any))
}
Optional Shortcut Support: Add Shortcuts() map[string]string
method to enable global keyboard shortcuts. The map key is the keyboard shortcut and the value passed to Change()
. The map value is only for display/description:
// Shortcuts work from any tab and automatically navigate to this field
func (h *DatabaseHandler) Shortcuts() map[string]string {
return map[string]string{
"t": "test connection", // Pressing 't' calls Change("t", progress)
"b": "backup database", // Pressing 'b' calls Change("b", progress)
}
}
β See complete implementation example
type HandlerExecution interface {
Name() string // Unique identifier for logging
Label() string // Button label
Execute(progress func(msgs ...any))
}
β See complete implementation example
type HandlerInteractive interface {
Name() string // Identifier for logging
Label() string // Field label (updates dynamically)
Value() string // Current input value
Change(newValue string, progress func(msgs ...any)) // Handle user input + content display
WaitingForUser() bool // Should edit mode be auto-activated?
}
Key Features:
- Dynamic Content: All content updates through
progress()
for consistency - Auto Edit Mode:
WaitingForUser()
controls when edit mode is activated - Content Display: Use empty
newValue
+WaitingForUser() == false
to trigger content display - Perfect for: Chat interfaces, configuration wizards, interactive help systems
β See complete implementation example
type HandlerLogger interface {
Name() string // Writer identifier
}
β See complete HandlerLogger implementation example
β See complete HandlerLoggerTracker implementation example
DevTUI uses automatic MessageTracker detection - simply implement the MessageTracker
interface and DevTUI will automatically detect and use it for operation tracking:
// Display handlers (no timeout needed)
tab.AddDisplayHandler(handler)
// Edit handlers (timeout mandatory)
tab.AddEditHandler(handler, 5*time.Second) // Automatic MessageTracker detection
// Execution handlers (timeout mandatory)
tab.AddExecutionHandler(handler, 10*time.Second) // Automatic MessageTracker detection
// Interactive handlers (timeout mandatory)
tab.AddInteractiveHandler(handler, 5*time.Second) // Automatic MessageTracker detection
// Writers (returns func(message ...any))
logger := tab.NewLogger("LogWriter", false) // Basic writer (new lines)
loggerWithTracker := tab.NewLogger("TrackedWriter", true) // Advanced writer (tracking)
logger("Log message 1")
logger("Another log entry")
To enable operation tracking (updating existing messages instead of creating new ones), simply implement the MessageTracker
interface:
type MessageTracker interface {
GetLastOperationID() string
SetLastOperationID(id string)
}
DevTUI automatically detects when a handler implements MessageTracker
and enables operation tracking without any additional registration steps.
- Minimal Implementation: 1-4 methods per handler
- Specialized Interfaces: Clear separation by purpose (Display, Edit, Execution, Writing)
- Progress Callbacks: Real-time feedback for long-running operations
- Automatic MessageTracker Detection: Optionally implement
MessageTracker
interface for operation tracking - Simple Registration: Single method per handler type with automatic tracking detection
- Method Chaining: All handler registration methods return
*tabSection
for chaining - Thread-Safe: Concurrent handler registration and execution
- Tab/Shift+Tab: Switch between tabs
- Left/Right: Navigate fields within tab
- Up/Down: Scroll viewport line by line
- Page Up/Page Down: Scroll viewport page by page
- Mouse Wheel: Scroll viewport (when available)
- Enter: Edit/Execute
- Esc: Cancel edit
- Ctrl+C: Exit
- Global Shortcuts: Single key shortcuts (e.g., "t", "b") work from any tab when defined in handlers
Shortcut System: Handlers implementing Shortcuts() map[string]string
automatically register global keyboard shortcuts. When pressed, shortcuts navigate to the handler's tab/field and execute the Change()
method with the map key as the newValue
parameter.
Example: If shortcuts return {"t": "test connection"}
, pressing 't' calls Change("t", progress)
.
Note: DevTUI automatically loads a built-in ShortcutsHandler at position 0 in the first tab, which displays detailed keyboard navigation commands. This handler demonstrates the HandlerEdit
interface and provides interactive help within the application.
Text Selection: Terminal text selection is enabled for copying error messages and logs. Mouse scroll functionality may vary depending on bubbletea version and terminal capabilities.
DevTUI is built on top of the excellent libraries from github.com/charmbracelet: bubbletea, bubbles and lipgloss, which provide the solid foundation for creating terminal interfaces in Go.