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
raxol lib raxol terminal config animation_cache.ex
Raw

lib/raxol/terminal/config/animation_cache.ex

defmodule Raxol.Terminal.Config.AnimationCache do
@moduledoc """
Manages caching for terminal animations using the unified caching system.
"""
alias Raxol.Core.Runtime.Log
# Module attributes for preload dir
@preload_dir "priv/animations"
@doc """
Initializes the animation cache.
"""
def init_animation_cache do
# The unified cache system is already initialized by the application
:ok
end
@doc """
Gets a cached animation.
## Parameters
* `animation_path` - Path to the animation file
"""
def get_cached_animation(animation_path) do
case animation_path do
nil -> {:error, :invalid_path}
path -> Raxol.Terminal.Cache.System.get(path, namespace: :animation)
end
end
@doc """
Caches animation data directly (for testing and in-memory usage).
## Parameters
* `animation_key` - Key to store the animation under
* `animation_data` - Animation data to cache
"""
def cache_animation_data(animation_key, animation_data) do
case Raxol.Terminal.Cache.System.put(animation_key, animation_data,
namespace: :animation,
metadata: %{
type: :data,
size: byte_size(:erlang.term_to_binary(animation_data)),
original_size: byte_size(:erlang.term_to_binary(animation_data)),
compressed: false
}
) do
:ok ->
Log.info("Animation data cached: #{animation_key}")
:ok
error ->
Log.info("Failed to cache animation data: #{inspect(error)}")
error
end
end
@doc """
Caches an animation from a file.
## Parameters
* `animation_path` - Path to the animation file
* `animation_type` - Type of animation (:gif, :video, :shader, :particle)
"""
def cache_animation(animation_path, animation_type) do
case File.read(animation_path) do
{:ok, animation_data} ->
compressed_data = compress_animation(animation_data, animation_type)
compressed_size = byte_size(compressed_data)
original_size = byte_size(animation_data)
metadata = %{
type: animation_type,
size: compressed_size,
original_size: original_size,
compressed: true
}
case Raxol.Terminal.Cache.System.put(animation_path, compressed_data,
namespace: :animation,
metadata: metadata
) do
:ok ->
handle_cache_success(animation_path, compressed_size, original_size)
error ->
handle_cache_error(error)
end
{:error, reason} ->
handle_file_error(reason)
end
end
@doc """
Decompresses an animation.
## Parameters
* `compressed_data` - Compressed animation data
"""
def decompress_animation(compressed_data) do
:zlib.uncompress(compressed_data)
end
@doc """
Gets the current cache size.
"""
def get_cache_size do
case Raxol.Terminal.Cache.System.stats(namespace: :animation) do
{:ok, stats} -> stats.size
_ -> 0
end
end
@doc """
Preloads animations from the preload directory.
"""
def preload_animations do
preload_path = Path.expand(@preload_dir)
case File.mkdir_p(preload_path) do
:ok ->
animation_files = find_animation_files(preload_path)
Enum.each(animation_files, fn {path, type} ->
# Ignore result for preload
cache_animation(path, type)
end)
Log.info("Preloaded #{length(animation_files)} animations")
:ok
{:error, reason} ->
IO.warn(
"Could not create preload directory #{preload_path}: #{inspect(reason)}"
)
:ok
end
end
@doc """
Clears the animation cache.
"""
def clear_animation_cache do
Raxol.Terminal.Cache.System.clear(namespace: :animation)
Log.info("Animation cache cleared")
:ok
end
@doc """
Gets animation cache statistics.
"""
def get_animation_cache_stats do
case Raxol.Terminal.Cache.System.stats(namespace: :animation) do
{:ok, stats} ->
{:ok,
%{
count: stats.size,
total_size: stats.size,
# We don't track original size in unified cache
total_original_size: stats.size,
average_size:
case stats.size > 0 do
true -> div(stats.size, stats.size)
false -> 0
end,
# We don't track compression ratio in unified cache
compression_ratio: 0,
max_size: stats.max_size,
used_percent:
case stats.max_size > 0 do
true -> round(stats.size / stats.max_size * 100)
false -> 0
end,
hit_count: Map.get(stats, :hit_count, 0),
miss_count: Map.get(stats, :miss_count, 0),
hit_ratio: Map.get(stats, :hit_ratio, 0.0)
}}
_ ->
{:ok,
%{
count: 0,
total_size: 0,
total_original_size: 0,
average_size: 0,
compression_ratio: 0,
max_size: 0,
used_percent: 0,
hit_count: 0,
miss_count: 0,
hit_ratio: 0.0
}}
end
end
@doc """
Preloads a single animation.
## Parameters
* `animation_path` - Path to the animation file
"""
def preload_animation(animation_path) do
preload_animation_with_validation(
animation_path,
File.exists?(animation_path)
)
end
defp preload_animation_with_validation(_animation_path, false) do
{:error, :file_not_found}
end
defp preload_animation_with_validation(animation_path, true) do
case determine_animation_type(animation_path) do
nil ->
{:error, :unsupported_animation_type}
animation_type ->
case cache_animation(animation_path, animation_type) do
:ok -> {:ok, animation_type}
error -> error
end
end
end
# Private Functions
defp handle_cache_success(animation_path, compressed_size, original_size) do
case original_size > 0 do
true ->
compression_ratio = round((1 - compressed_size / original_size) * 100)
Log.info(
"Animation cached: #{animation_path} (#{compressed_size} bytes, #{compression_ratio}% compression)"
)
false ->
Log.info(
"Animation cached: #{animation_path} (#{compressed_size} bytes, empty original file)"
)
end
:ok
end
defp handle_cache_error(error) do
Log.info("Failed to cache animation: #{inspect(error)}")
error
end
defp handle_file_error(reason) do
Log.info("Failed to cache animation: #{inspect(reason)}")
{:error, reason}
end
defp compress_animation(animation_data, _animation_type) do
:zlib.compress(animation_data)
end
defp find_animation_files(directory) do
case File.ls(directory) do
{:ok, files} ->
Enum.flat_map(files, &process_animation_file(&1, directory))
_ ->
[]
end
end
defp process_animation_file(file, directory) do
path = Path.join(directory, file)
case File.regular?(path) do
true ->
case determine_animation_type(path) do
nil -> []
type -> [{path, type}]
end
false ->
[]
end
end
defp determine_animation_type(path) do
ext = Path.extname(path) |> String.downcase()
case ext do
".gif" -> :gif
".mp4" -> :video
".webm" -> :video
".glsl" -> :shader
".frag" -> :shader
".vert" -> :shader
".particle" -> :particle
_ -> nil
end
end
end