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/cursor/state.ex
defmodule Raxol.Terminal.Cursor.State do
@moduledoc """
Handles cursor state management operations for the terminal cursor.
Extracted from Raxol.Terminal.Cursor.Manager to reduce file size.
"""
alias Raxol.Terminal.Cursor.Manager
@doc """
Saves the current cursor state.
"""
def save_state(%Manager{} = state) do
%{
state
| saved_row: state.row,
saved_col: state.col,
saved_style: state.style,
saved_visible: state.visible,
saved_blinking: state.blinking,
saved_color: state.color,
saved_position: {state.row, state.col}
}
end
@doc """
Restores the saved cursor state.
"""
def restore_state(%Manager{} = state) do
%{
state
| row: state.saved_row || state.row,
col: state.saved_col || state.col,
style: state.saved_style || state.style,
visible: state.saved_visible || state.visible,
blinking: state.saved_blinking || state.blinking,
color: state.saved_color || state.color,
position: state.saved_position || {state.row, state.col}
}
end
@doc """
Resets the cursor state to default values.
"""
def reset(%Manager{} = state) do
%{
state
| row: 0,
col: 0,
position: {0, 0},
visible: true,
blinking: true,
style: :block,
color: nil,
saved_row: nil,
saved_col: nil,
saved_style: nil,
saved_visible: nil,
saved_blinking: nil,
saved_color: nil
}
end
@doc """
Sets the cursor state based on a state atom.
Supported states: :visible, :hidden, :blinking
"""
def set_state(%Manager{} = state, state_atom),
do: do_set_state(state, state_atom)
defp do_set_state(state, :visible),
do: %{state | visible: true, state: :visible}
defp do_set_state(state, :hidden),
do: %{state | visible: false, state: :hidden}
defp do_set_state(state, :blinking),
do: %{state | blinking: true, blink: true, state: :blinking}
@doc """
Saves the current cursor position.
"""
def save_position(%Manager{} = state) do
%{
state
| saved_row: state.row,
saved_col: state.col,
saved_position: {state.row, state.col}
}
end
@doc """
Restores the saved cursor position.
"""
def restore_position(%Manager{} = state) do
if state.saved_row && state.saved_col do
%{
state
| row: state.saved_row,
col: state.saved_col,
position: {state.saved_row, state.saved_col}
}
else
state
end
end
@doc """
Adds the current cursor state to history.
"""
def add_to_history(%Manager{} = state) do
history_entry = %{
row: state.row,
col: state.col,
style: state.style,
visible: state.visible,
blinking: state.blinking,
state: state.state,
position: {state.row, state.col}
}
%{
state
| history: [history_entry | state.history],
history_index: state.history_index + 1
}
end
@doc """
Restores cursor state from history.
"""
def restore_from_history(%Manager{} = state) do
case state.history do
[entry | rest] ->
%{
state
| row: entry.row,
col: entry.col,
style: entry.style,
visible: entry.visible,
blinking: entry.blinking,
state: entry.state,
position: {entry.row, entry.col},
history: rest
}
[] ->
state
end
end
@doc """
Gets the cursor state atom (:visible, :hidden, :blinking).
"""
def get_state(%Manager{state: state}), do: state
@doc """
Sets the cursor margins.
"""
def set_margins(cursor, top, bottom) do
%{cursor | top_margin: top, bottom_margin: bottom}
end
@doc """
Gets the cursor margins.
"""
def get_margins(cursor) do
{cursor.top_margin, cursor.bottom_margin}
end
@doc """
Sets a custom cursor shape.
"""
def set_custom_shape(%Manager{} = state, shape, params),
do: %{
state
| style: :custom,
custom_shape: shape,
custom_dimensions: params,
shape: params
}
@doc """
Updates cursor position based on text input.
"""
def update_position_from_text(%Manager{} = cursor, text)
when is_binary(text) do
# Calculate new position based on text length
new_col = cursor.col + String.length(text)
%{cursor | col: new_col, position: {cursor.row, new_col}}
end
@doc """
Updates the cursor blink state.
"""
def update_blink(%Manager{state: :visible} = state), do: {state, true}
def update_blink(%Manager{state: :hidden} = state), do: {state, false}
def update_blink(%Manager{state: :blinking, blink: blink} = state) do
new_blink = !blink
{%{state | blink: new_blink}, new_blink}
end
end