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/terminal/state_manager.ex
defmodule Raxol.Terminal.StateManager do
@moduledoc """
Manages terminal state transitions and state stack operations.
This module is responsible for maintaining and manipulating the terminal's state.
This module implements the StateManager behavior for consistent state management
patterns across the codebase while maintaining its specific terminal functionality.
"""
# Using consolidated StateManager for functional state operations
alias Raxol.Core.StateManager
# Delegate common state operations to consolidated StateManager
def init(), do: {:ok, %{}}
def init(opts), do: {:ok, Keyword.get(opts, :initial_state, %{})}
defdelegate get(state, key), to: StateManager
defdelegate get(state, key, default), to: StateManager
defdelegate put(state, key, value), to: StateManager
defdelegate update(state, key, func), to: StateManager
defdelegate delete(state, key), to: StateManager
defdelegate clear(state), to: StateManager
defdelegate validate(state), to: StateManager
defdelegate merge(state1, state2), to: StateManager
alias Raxol.Terminal.Emulator
require Raxol.Core.Runtime.Log
@doc """
Saves the current terminal state.
Returns {:ok, updated_emulator}.
"""
@spec save_state(Emulator.t()) :: {:ok, Emulator.t()}
def save_state(emulator) do
current_state = %{
cursor: get_cursor_state(emulator),
screen: get_screen_state(emulator),
modes: get_mode_state(emulator)
}
new_stack = [current_state | emulator.state_stack]
{:ok, %{emulator | state_stack: new_stack}}
end
@doc """
Restores the most recently saved terminal state.
Returns {:ok, updated_emulator}.
"""
@spec restore_state(Emulator.t()) :: {:ok, Emulator.t()}
def restore_state(emulator) do
case emulator.state_stack do
[state | new_stack] ->
emulator = apply_saved_state(emulator, state)
{:ok, %{emulator | state_stack: new_stack}}
[] ->
{:error, "No saved state to restore"}
end
end
@doc """
Clears all saved states.
Returns {:ok, updated_emulator}.
"""
@spec clear_states(Emulator.t()) :: {:ok, Emulator.t()}
def clear_states(emulator) do
{:ok, %{emulator | state_stack: []}}
end
@doc """
Gets the current terminal state.
Returns the current state map.
"""
@spec get_current_state(Emulator.t()) :: map()
def get_current_state(emulator) do
%{
cursor: get_cursor_state(emulator),
screen: get_screen_state(emulator),
modes: get_mode_state(emulator)
}
end
@doc """
Updates the current terminal state.
Returns {:ok, updated_emulator}.
"""
@spec update_current_state(Emulator.t(), map()) :: {:ok, Emulator.t()}
def update_current_state(emulator, new_state) do
emulator = apply_saved_state(emulator, new_state)
{:ok, emulator}
end
# Private helper functions
defp get_cursor_state(emulator) do
%{
position: emulator.cursor.position,
visible: emulator.cursor.visible,
style: emulator.cursor.style,
blink_state: emulator.cursor.blink_state
}
end
defp get_screen_state(emulator) do
%{
buffer: emulator.active_buffer,
scroll_region: emulator.scroll_region,
charset_state: emulator.charset_state
}
end
defp get_mode_state(emulator) do
%{
insert_mode: emulator.mode_manager.insert_mode,
origin_mode: emulator.mode_manager.origin_mode,
auto_wrap: emulator.mode_manager.auto_wrap,
cursor_visible: emulator.mode_manager.cursor_visible
}
end
defp apply_saved_state(emulator, state) do
emulator
|> apply_cursor_state(state.cursor)
|> apply_screen_state(state.screen)
|> apply_mode_state(state.modes)
end
defp apply_cursor_state(emulator, cursor_state) do
%{emulator | cursor: Map.merge(emulator.cursor, cursor_state)}
end
defp apply_screen_state(emulator, screen_state) do
emulator
|> Map.put(:active_buffer, screen_state.buffer)
|> Map.put(:scroll_region, screen_state.scroll_region)
|> Map.put(:charset_state, screen_state.charset_state)
end
defp apply_mode_state(emulator, mode_state) do
%{emulator | mode_manager: Map.merge(emulator.mode_manager, mode_state)}
end
end