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
examples/plugins/spotify/04_custom_integration.exs
#!/usr/bin/env elixir
# Custom Terminal Integration with Spotify
#
# This example demonstrates embedding the Spotify plugin in a custom
# terminal application with additional UI elements.
#
# Shows how to:
# - Integrate plugin state into your own state
# - Combine plugin rendering with custom UI
# - Forward specific inputs to the plugin
Mix.install([
{:raxol, path: Path.expand("../../..", __DIR__)},
{:req, "~> 0.5"},
{:oauth2, "~> 2.1"}
])
defmodule CustomTerminalWithSpotify do
alias Raxol.Core.{Buffer, Box}
alias Raxol.Plugins.Spotify
defstruct [
:spotify_state,
:custom_message,
:mode
]
def run do
# Initialize Spotify plugin
{:ok, spotify_state} = Spotify.init([])
# Initialize our custom state
state = %__MODULE__{
spotify_state: spotify_state,
custom_message: "Welcome to Custom Terminal with Spotify!",
# :spotify or :custom
mode: :spotify
}
# Main loop
loop(state)
end
defp loop(state) do
# Create buffer
buffer = Buffer.create_blank_buffer(80, 30)
# Render custom header
buffer = render_header(buffer, state)
# Render Spotify plugin in its section
spotify_buffer = Buffer.create_blank_buffer(76, 20)
spotify_buffer = Spotify.render(spotify_buffer, state.spotify_state)
# Merge spotify buffer into main buffer at offset
buffer = Buffer.merge(buffer, spotify_buffer, 2, 5)
# Render custom footer
buffer = render_footer(buffer, state)
# Display
# Clear screen and move cursor to top
IO.write("\e[2J\e[H")
IO.write(Buffer.to_string(buffer))
# Handle input
case get_input() do
{:key, "q"} ->
Spotify.cleanup(state.spotify_state)
:ok
{:key, "\t"} ->
# Tab switches between modes
new_mode = if state.mode == :spotify, do: :custom, else: :spotify
loop(%{state | mode: new_mode})
{:key, key} when state.mode == :spotify ->
# Forward to Spotify plugin
modifiers = %{ctrl: false, alt: false, shift: false, meta: false}
case Spotify.handle_input(key, modifiers, state.spotify_state) do
{:ok, new_spotify_state} ->
loop(%{state | spotify_state: new_spotify_state})
{:exit, _} ->
:ok
{:error, reason} ->
loop(%{state | custom_message: "Error: #{inspect(reason)}"})
end
{:key, key} when state.mode == :custom ->
# Handle custom mode input
message =
"Custom mode - pressed: #{key} (press TAB to switch to Spotify)"
loop(%{state | custom_message: message})
end
end
defp render_header(buffer, state) do
# Draw header box
buffer = Box.draw_box(buffer, 0, 0, 80, 3, :double)
# Title
title = " Custom Terminal with Spotify Integration "
buffer =
Buffer.write_at(buffer, 18, 0, title, %{bold: true, fg_color: :cyan})
# Mode indicator
mode_text = "Mode: #{state.mode} (TAB to switch)"
buffer = Buffer.write_at(buffer, 2, 1, mode_text, %{fg_color: :yellow})
buffer
end
defp render_footer(buffer, state) do
# Draw footer box
buffer = Box.draw_box(buffer, 0, 26, 80, 4, :single)
# Custom message
buffer =
Buffer.write_at(buffer, 2, 27, state.custom_message, %{fg_color: :green})
# Help text
help =
"[Q: Quit | TAB: Switch Mode | SPACE: Play/Pause | N: Next | P: Previous]"
buffer = Buffer.write_at(buffer, 3, 28, help, %{fg_color: :bright_black})
buffer
end
defp get_input do
# Simple input - in production use proper terminal input handling
char = IO.gets("") |> String.trim() |> String.first()
{:key, char || ""}
end
end
IO.puts("""
Custom Terminal with Spotify Integration
=========================================
This example shows a custom terminal app with embedded Spotify controls.
Controls:
TAB - Switch between Custom and Spotify modes
Q - Quit
Spotify Mode (when active):
SPACE - Play/pause
N - Next track
P - Previous track
L - View playlists
Starting...
""")
Process.sleep(2000)
CustomTerminalWithSpotify.run()