Package: github.com/TIVerse/drav/pkg/dravya
Dravya (Sanskrit: द्रव्य, "substance, essence") is the runtime core of DRAV. It manages the application lifecycle, orchestrates the event loop, and coordinates all other modules.
The App struct is the central coordinator:
app := dravya.NewApp(
dravya.WithLogLevel(slog.LevelInfo),
dravya.WithFPS(60),
)Dravya manages five application states:
- Initializing: Setting up modules and resources
- Running: Main event loop active
- Paused: Suspended but can resume
- ShuttingDown: Cleanup in progress
- Terminated: Application ended
The FPS-capped render loop ensures smooth performance:
app.SetRoot(myComponent)
if err := app.Run(context.Background()); err != nil {
log.Fatal(err)
}Register callbacks for state transitions:
app.OnReady(func() {
// Called after initialization
setupEventHandlers()
})Connects keyboard, mouse, and system events to your components:
eventHub := app.EventHub()
eventHub.On(agni.EventTypeKey, handleKeyPress)Built-in focus system for keyboard navigation:
focusMgr := app.FocusManager()
focusMgr.Register("my-input-field")Observable state changes trigger UI updates automatically:
counter := prana.NewObservable(0)
counter.Set(42) // UI re-renders automaticallySet logging verbosity:
dravya.WithLogLevel(slog.LevelDebug)Target frame rate (default: 60):
dravya.WithFPS(30) // Lower for reduced CPU usageEnable profiling server:
dravya.WithPprof(":6060")Sets the root UI component to render.
Starts the application main loop. Blocks until shutdown.
Gracefully shuts down the application.
Returns the structured logger instance.
Returns the event hub for registering handlers.
Returns the focus manager for keyboard navigation.
Returns runtime statistics (uptime, frame count, etc.).
Always use context for cancellation:
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Shutdown on signal
go func() {
<-sigChan
cancel()
}()
app.Run(ctx)Register cleanup in shutdown hooks:
app.Lifecycle().OnState(dravya.StateShuttingDown, func(ctx context.Context) error {
// Clean up resources
return nil
})Use pprof for optimization:
app := dravya.NewApp(dravya.WithPprof(":6060"))
// View: http://localhost:6060/debug/pprof/package main
import (
"context"
"github.com/TIVerse/drav/pkg/dravya"
"github.com/TIVerse/drav/pkg/maya"
)
type HelloWorld struct{}
func (h *HelloWorld) Render(ctx maya.RenderContext) maya.View {
return maya.Text("Hello, DRAV!")
}
func main() {
app := dravya.NewApp()
app.SetRoot(&HelloWorld{})
app.Run(context.Background())
}app.OnReady(func() {
eventHub := app.EventHub()
eventHub.On(agni.EventTypeKey, func(ctx context.Context, event dravya.Event) error {
if keyEvent, ok := event.(*agni.KeyEvent); ok {
app.Logger().Info("Key pressed", "key", keyEvent.Key)
}
return nil
})
})- Frame Budget: 60 FPS = 16.67ms per frame
- Render Optimization: Only dirty regions are redrawn
- Event Batching: Multiple events processed per frame
- Memory: ~50MB baseline, scales with UI complexity
Check if tcell can initialize the terminal:
app := dravya.NewApp()
if err := app.Run(ctx); err != nil {
log.Printf("Failed to start: %v", err)
}Ensure handlers are registered in OnReady:
app.OnReady(func() {
// Register handlers here
})Use RequestRender() sparingly and unsubscribe from observables:
unwatch := observable.Watch(handler)
defer unwatch()