Documentation
¶
Overview ¶
Package runfx provides a robust, efficient, and highly responsive optional runtime loop for advanced terminal (TTY) management, multiplexed visuals, graceful degradation, and detailed observability.
RunFX is designed for explicit mounting of visuals, zero hidden dependencies, and advanced features to ensure reliability in complex CLI applications.
Key Features ¶
- Explicit TTY ownership with advanced multiplexing - Cross-platform signal handling (SIGWINCH on Unix, graceful fallback on Windows) - Double-buffered, flicker-free rendering - Thread-safe visual mounting/unmounting - Configurable tick rates for smooth animation (30-120ms) - Intelligent fallback for non-TTY environments - Zero reflection, global state, or hidden dependencies - Multipath API with three entry points for different usage patterns
Multipath API Usage ¶
RunFX follows TFX's multipath API philosophy with three distinct paths:
## Beginner Path - Simple & Convenient
Zero-config (uses sensible defaults):
loop := runfx.Start()
Config struct (explicit configuration):
cfg := runfx.Config{
TickInterval: 30 * time.Millisecond,
TestMode: true,
}
loop := runfx.Start(cfg)
## Hardcore Path - Fluent DSL Builder
Declarative chaining for maximum expressiveness:
loop := runfx.New(). SmoothAnimation(). // 30ms tick interval TestMode(). Output(os.Stderr). Start()
Available builder methods:
- TickInterval(duration) - Custom tick rate
- SmoothAnimation() - 30ms ticks for smooth animations
- FastAnimation() - 100ms ticks for less CPU usage
- TestMode() - Enable test mode for non-TTY environments
- Output(writer) - Custom output destination
## Experimental Path - Functional Options
Note: This path is experimental and not currently in active use:
loop := runfx.StartWith( runfx.WithSmoothAnimation(), runfx.WithTestMode(), )
Basic Usage Pattern ¶
Standard workflow with any of the above creation methods:
// Create loop (using any method above)
loop := runfx.Start() // or runfx.New().SmoothAnimation().Start()
// Mount your visual
unmount, err := loop.Mount(myVisual)
if err != nil {
return err
}
defer unmount()
// Run with context
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
return loop.Run(ctx)
Visual Interface ¶
Any type implementing the Visual interface can be mounted:
type Visual interface {
Render(w share.Writer) // Called during render cycles
Tick(now time.Time) // Called on each tick for animations
OnResize(cols, rows int) // Called when terminal is resized
}
Graceful Degradation ¶
RunFX automatically detects TTY capabilities and falls back to minimal output in non-TTY environments, ensuring reliability across different deployment scenarios.
Index ¶
- Variables
- func DebugLog(format string, args ...any)
- func EnableDebug()
- func FallbackOutput(msg string)
- func LogFlush(tickCount int, elapsed time.Duration)
- func LogMount(name string)
- func LogRender(name string)
- func LogTick(tickCount, count int, elapsed time.Duration)
- func LogUnmount(name string)
- func WithAutoTick() share.Option[Config]
- func WithFastAnimation() share.Option[Config]
- func WithInput(input io.Reader) share.Option[Config]
- func WithOutput(output io.Writer) share.Option[Config]
- func WithSmoothAnimation() share.Option[Config]
- func WithTickInterval(interval time.Duration) share.Option[Config]
- type Config
- type Interactive
- type Key
- type KeyCode
- type KeyReader
- type Loop
- type LoopBuilder
- func (b *LoopBuilder) AutoTick() *LoopBuilder
- func (b *LoopBuilder) FastAnimation() *LoopBuilder
- func (b *LoopBuilder) Input(input io.Reader) *LoopBuilder
- func (b *LoopBuilder) Output(output io.Writer) *LoopBuilder
- func (b *LoopBuilder) SmoothAnimation() *LoopBuilder
- func (b *LoopBuilder) Start() Loop
- func (b *LoopBuilder) TickInterval(interval time.Duration) *LoopBuilder
- type MainLoop
- type Modifier
- type Multiplexer
- type TTYInfo
- type Updatable
- type Visual
- type VisualID
Constants ¶
This section is empty.
Variables ¶
var ( ErrLoopClosed = errors.New("runfx: event loop is closed") ErrMountFailed = errors.New("runfx: failed to mount visual") ErrNotTTY = errors.New("runfx: not a TTY environment") ErrLoopAlreadyRunning = errors.New("runfx: loop is already running") ErrLoopNotRunning = errors.New("runfx: loop is not running") )
Functions ¶
func FallbackOutput ¶
func FallbackOutput(msg string)
FallbackOutput prints minimal output if not TTY
func WithAutoTick ¶
WithAutoTick returns an Option to set the tick interval based on detected TTY capabilities.
func WithFastAnimation ¶
WithFastAnimation returns an Option to set a 100ms tick interval for efficient animations.
func WithOutput ¶
WithOutput returns an Option to set a custom output writer.
func WithSmoothAnimation ¶
WithSmoothAnimation returns an Option to set a 30ms tick interval for smooth animations.
Types ¶
type Config ¶
Config provides structured configuration for RunFX Loop
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns default configuration for RunFX
type Interactive ¶
Interactive is a Visual that can respond to keyboard input.
type Key ¶
Key represents a single key press, with optional modifier info.
func (Key) IsNavigation ¶
IsNavigation returns true if the key is a navigation key (Arrow keys, WASD, Tab).
func (Key) IsPrintable ¶
func (Key) IsSelector ¶
IsSelector returns true if the key is used to select an option (navigation, accept or cancel).
type KeyCode ¶
type KeyCode int
KeyCode represents a keyboard input event code.
const ( KeyUnknown KeyCode = iota // Special keys KeyEnter KeyEscape KeyBackspace KeyTab KeySpace KeyDelete // Arrow keys KeyArrowUp KeyArrowDown KeyArrowLeft KeyArrowRight // Letter keys KeyA KeyB KeyC KeyD KeyE KeyF KeyG KeyH KeyI KeyJ KeyK KeyL KeyM KeyN KeyO KeyP KeyQ KeyR KeyS KeyT KeyU KeyV KeyW KeyX KeyY KeyZ // Number keys Key0 Key1 Key2 Key3 Key4 Key5 Key6 Key7 Key8 Key9 // Shortcuts KeyCtrlC KeyCtrlD KeyCtrlZ )
type KeyReader ¶
type KeyReader struct {
// contains filtered or unexported fields
}
KeyReader handles reading keyboard input from a terminal and converting it to Key events.
func NewKeyReader ¶
NewKeyReader creates a new keyboard input reader.
type Loop ¶
type Loop interface {
Mount(v Visual) (unmount func(), err error)
Run(ctx context.Context) error
Stop() error
IsRunning() bool
}
Loop defines the runtime loop for mounting and managing visuals.
func MustStart ¶ added in v0.2.0
MustStart creates a new Loop with multipath configuration support and panics on invalid multipath input.
Prefer TryStart when configuration may be user-provided or otherwise fallible.
func Start ¶
Start creates and starts a new Loop with multipath configuration support.
Start is kept as a zero-ceremony compatibility alias for MustStart. Prefer TryStart for fallible input or MustStart when panic semantics should be explicit at the callsite.
type LoopBuilder ¶
type LoopBuilder struct {
// contains filtered or unexported fields
}
LoopBuilder provides a fluent DSL interface for configuring RunFX loops
func New ¶
func New() *LoopBuilder
New creates a new LoopBuilder with default configuration. HARDCORE path - provides fluent DSL interface:
runfx.New().TickInterval(100*time.Millisecond).TestMode(true).Start()
func (*LoopBuilder) AutoTick ¶
func (b *LoopBuilder) AutoTick() *LoopBuilder
func (*LoopBuilder) FastAnimation ¶
func (b *LoopBuilder) FastAnimation() *LoopBuilder
FastAnimation sets tick interval to 100ms for faster/less resource intensive animations
func (*LoopBuilder) Input ¶
func (b *LoopBuilder) Input(input io.Reader) *LoopBuilder
Input sets the keyboard input source.
func (*LoopBuilder) Output ¶
func (b *LoopBuilder) Output(output io.Writer) *LoopBuilder
Output sets the output writer
func (*LoopBuilder) SmoothAnimation ¶
func (b *LoopBuilder) SmoothAnimation() *LoopBuilder
SmoothAnimation sets tick interval to 30ms for very smooth animations
func (*LoopBuilder) Start ¶
func (b *LoopBuilder) Start() Loop
Start creates and returns the configured Loop instance
func (*LoopBuilder) TickInterval ¶
func (b *LoopBuilder) TickInterval(interval time.Duration) *LoopBuilder
TickInterval sets the tick interval for the event loop
type MainLoop ¶
type MainLoop struct {
// contains filtered or unexported fields
}
MainLoop is handles terminal I/O, signals, and the render/tick cycle.
type Multiplexer ¶
type Multiplexer struct {
// contains filtered or unexported fields
}
Multiplexer safely manages a set of visual components. Now uses an atomic counter to generate unique IDs.
func NewMultiplexer ¶
func NewMultiplexer() *Multiplexer
NewMultiplexer creates a new instance of the multiplexer.
func (*Multiplexer) GetVisual ¶
func (m *Multiplexer) GetVisual(id VisualID) (Visual, bool)
GetVisual retrieves a visual component by its ID.
func (*Multiplexer) ListVisuals ¶
func (m *Multiplexer) ListVisuals() []VisualID
ListVisuals returns a list of the IDs of all mounted components.
func (*Multiplexer) Mount ¶
func (m *Multiplexer) Mount(v Visual) VisualID
Mount registers a new visual component and assigns it a unique ID. Returns the assigned ID.
func (*Multiplexer) OnResize ¶
func (m *Multiplexer) OnResize(cols, rows int)
OnResize notifies all visual components of a terminal resize event.
func (*Multiplexer) Render ¶
func (m *Multiplexer) Render() []byte
Render adds the bytes of all visual components for a single frame.
func (*Multiplexer) Unmount ¶
func (m *Multiplexer) Unmount(id VisualID)
Unmount removes a visual component using its ID.
type TTYInfo ¶
TTYInfo holds terminal capability details (uses writer.TerminalWriter for consistency)
func DetectTTY ¶
func DetectTTY() TTYInfo
DetectTTY returns TTYInfo using the same detection logic as writer.TerminalWriter
func DetectTTYForIO ¶
DetectTTYForIO returns TTYInfo for the controlling terminal device selected from the provided output/input pair.
func DetectTTYForOutput ¶
DetectTTYForOutput returns TTYInfo for a specific output writer