Packages
Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.
Current section
Files
Jump to
Current section
Files
lib/raxol/events/event_server.ex
defmodule Raxol.Events.EventServer do
@moduledoc """
Unified event handler consolidating all event processing capabilities.
Replaces fragmented event handlers with a single, coherent system that handles:
- Terminal events (input, output, resize)
- Plugin events (lifecycle, state changes)
- UI events (interactions, updates)
- Accessibility events (focus, navigation)
- Core runtime events (system, performance)
## Design Principles
1. **Single Entry Point**: All events flow through this unified handler
2. **Event Routing**: Intelligent routing based on event type and context
3. **Handler Delegation**: Delegates to specialized handlers when needed
4. **Event Tracing**: Built-in event tracing for debugging and monitoring
5. **Performance**: Optimized event processing with minimal overhead
## Event Categories
- **Terminal Events**: Input/output, resize, cursor, buffer operations
- **Plugin Events**: Installation, activation, lifecycle, errors
- **UI Events**: Component interactions, theme changes, accessibility
- **System Events**: Performance metrics, error conditions, state changes
"""
use Raxol.Core.Behaviours.BaseManager
alias Raxol.Core.Runtime.Events.EventsHandler
alias Raxol.Plugins.EventHandler, as: PluginsEventHandler
alias Raxol.Terminal.EventHandler, as: TerminalEventHandler
alias Raxol.Core.Accessibility.EventHandler, as: AccessibilityEventHandler
@type event_type :: :terminal | :plugin | :ui | :accessibility | :system
@type event_data :: map()
@type handler_result :: {:ok, any()} | {:error, term()}
defstruct [
# %{event_type => handler_module}
:handlers,
# List of recent events for debugging
:event_history,
# Boolean for event tracing
:tracing_enabled,
# Event processing performance metrics
:performance_stats
]
## Client API
@doc """
Handles any event by routing to appropriate specialized handler.
"""
@spec handle_event(GenServer.server(), event_type(), event_data()) ::
handler_result()
def handle_event(server \\ __MODULE__, event_type, event_data) do
GenServer.call(server, {:handle_event, event_type, event_data})
end
@doc """
Handles terminal events (input, output, resize, etc.).
"""
@spec handle_terminal_event(GenServer.server(), atom(), any()) ::
handler_result()
def handle_terminal_event(server \\ __MODULE__, event_name, event_data) do
handle_event(server, :terminal, %{event: event_name, data: event_data})
end
@doc """
Handles plugin events (lifecycle, state changes, etc.).
"""
@spec handle_plugin_event(GenServer.server(), atom(), any()) ::
handler_result()
def handle_plugin_event(server \\ __MODULE__, event_name, event_data) do
handle_event(server, :plugin, %{event: event_name, data: event_data})
end
@doc """
Handles accessibility events (focus, navigation, etc.).
"""
@spec handle_accessibility_event(GenServer.server(), atom(), any()) ::
handler_result()
def handle_accessibility_event(server \\ __MODULE__, event_name, event_data) do
handle_event(server, :accessibility, %{event: event_name, data: event_data})
end
@doc """
Legacy compatibility methods for existing code.
"""
def handle_input(manager, input) do
handle_terminal_event(__MODULE__, :input, %{manager: manager, input: input})
end
def handle_output(manager, output) do
handle_terminal_event(__MODULE__, :output, %{
manager: manager,
output: output
})
end
def handle_resize(manager, width, height) do
handle_terminal_event(__MODULE__, :resize, %{
manager: manager,
width: width,
height: height
})
end
@doc """
Start the unified event handler.
"""
# BaseManager provides start_link/1 and start_link/2 automatically
## GenServer Implementation
@impl true
def init_manager(opts) do
state = %__MODULE__{
handlers: %{
terminal: TerminalEventHandler,
plugin: PluginsEventHandler,
accessibility: AccessibilityEventHandler,
system: EventsHandler
},
event_history: [],
tracing_enabled: Keyword.get(opts, :tracing, false),
performance_stats: %{}
}
{:ok, state}
end
@impl true
def handle_manager_call({:handle_event, event_type, event_data}, _from, state) do
start_time = System.monotonic_time(:microsecond)
result =
case Map.get(state.handlers, event_type) do
nil ->
Log.warning("No handler for event type: #{event_type}")
{:error, :no_handler}
handler_module ->
delegate_to_handler(handler_module, event_type, event_data, state)
end
end_time = System.monotonic_time(:microsecond)
duration = end_time - start_time
# Update performance stats and history
new_state = update_event_stats(state, event_type, duration, result)
{:reply, result, new_state}
end
## Private Functions
defp delegate_to_handler(handler_module, event_type, event_data, _state) do
try do
case event_type do
:terminal ->
handle_terminal_delegation(handler_module, event_data)
:plugin ->
handle_plugin_delegation(handler_module, event_data)
:accessibility ->
handle_accessibility_delegation(handler_module, event_data)
:system ->
handle_system_delegation(handler_module, event_data)
_ ->
{:error, :unknown_event_type}
end
rescue
error ->
Log.error("Event handler error: #{inspect(error)}")
{:error, {:handler_exception, error}}
end
end
defp handle_terminal_delegation(handler_module, %{
event: event_name,
data: data
}) do
case event_name do
:input ->
handler_module.handle_input(data.manager, data.input)
:output ->
handler_module.handle_output(data.manager, data.output)
:resize ->
handler_module.handle_resize(data.manager, data.width, data.height)
_ ->
apply(handler_module, :handle_event, [event_name, data])
end
end
defp handle_plugin_delegation(handler_module, %{event: event_name, data: data}) do
case event_name do
:input -> handler_module.handle_input(data.manager, data.input)
:output -> handler_module.handle_output(data.manager, data.output)
_ -> apply(handler_module, :handle_event, [event_name, data])
end
end
defp handle_accessibility_delegation(handler_module, %{
event: event_name,
data: data
}) do
apply(handler_module, :handle_event, [event_name, data])
end
defp handle_system_delegation(handler_module, %{event: event_name, data: data}) do
apply(handler_module, :handle_event, [event_name, data])
end
defp update_event_stats(state, event_type, duration, result) do
# Add to event history (keep last 100 events)
event_entry = %{
type: event_type,
timestamp: System.system_time(:millisecond),
duration_us: duration,
result:
case result do
{:ok, _} -> :ok
{:error, _} -> :error
end
}
new_history = [event_entry | state.event_history] |> Enum.take(100)
# Update performance stats
current_stats =
Map.get(state.performance_stats, event_type, %{count: 0, total_time: 0})
new_stats = %{
count: current_stats.count + 1,
total_time: current_stats.total_time + duration,
avg_time:
(current_stats.total_time + duration) / (current_stats.count + 1)
}
%{
state
| event_history: new_history,
performance_stats:
Map.put(state.performance_stats, event_type, new_stats)
}
end
end