This document provides a high-level introduction to the Etch library, covering its purpose, architecture, and main components. For detailed documentation on specific modules, see the Core Modules section 3. For platform-specific implementation details, see Platform Support 5.
Etch is a terminal user interface (TUI) backend library for Gleam that provides cross-platform terminal control, event handling, and text styling capabilities. It compiles to both Erlang/BEAM and JavaScript targets, enabling TUI applications to run on multiple runtimes including Erlang, Bun, Deno, and Node.js.
Core Capabilities:
terminal.enter_raw(), terminal.exit_raw(), alternate screen control, window size queries via terminal.get_size()Command type variantsEvent types for keyboard (KeyEvent), mouse (MouseEvent), focus, and resizestyle.Color (RGB, ANSI-256, standard) and style.Attribute (bold, italic, underline)cursor.move_to(), cursor.hide(), cursor.show()The library has zero third-party dependencies beyond gleam_stdlib, gleam_erlang, and gleam_javascript.
Sources: README.md1-9 gleam.toml1-6 gleam.toml21-24
The following table describes the main public modules and their responsibilities:
| Module | Purpose | Key Types |
|---|---|---|
etch/command | Unified command interface for all terminal operations | Command |
etch/stdout | Command queue management and execution | Queue |
etch/event | Event parsing, event server, and event reading | Event, KeyEvent, MouseEvent |
etch/terminal | Terminal state control (raw mode, screen, dimensions) | ClearType, WindowSize |
etch/cursor | Cursor positioning and visibility control | - |
etch/style | Text colors and attributes | Color, Attribute |
Internal modules (not part of the public API):
etch/internal/* - Implementation detailsinput/* - Platform-specific input FFIterminal/* - Platform-specific terminal FFISources: gleam.toml8-14 README.md11-46
System Component Diagram
Architectural Layers:
etch/stdout, etch/event, etch/terminal, etc.)etch/internal/consts provides ANSI sequences, input module handles raw input parsingterminal_ffi.mjs/terminal_ffi.erl and input_ffi.mjs/event_ffi.erlData flows downward for output (commands → ANSI sequences → stdout) and upward for input (stdin → parsed events → application).
Sources: gleam.toml6-17 gleam.toml8-12 README.md7-9 src/etch/command.gleam1-9 src/etch/stdout.gleam1-10
Dependency Relationships Between Modules
Key Dependencies:
etch/stdout: Orchestrates all other modules, depends on command, style, cursor, terminal, eventetch/command: Aggregates types from specialized modules (cursor, style, terminal, event)etch/internal/consts: Provides ANSI escape sequence constants to cursor, style, terminalinput module: Hidden internal module that interfaces with input_ffi.mjs, event_ffi.erl, signal_handler.erl.erl (Erlang) and .mjs (JavaScript) filesThe stdout module acts as the primary coordinator, while command serves as the type aggregator. Internal modules are marked non-public via internal_modules in gleam.toml.
Sources: gleam.toml8-14 gleam.toml21-24 src/etch/command.gleam1-9 src/etch/stdout.gleam1-27
| Target | Primary Runtime | Raw Mode | Event Loop | Mouse Support | Build Command |
|---|---|---|---|---|---|
| JavaScript | Bun | ✓ | Promise-based | ✓ | gleam build |
| JavaScript | Deno | ✓ | Promise-based | ✓ | gleam build |
| JavaScript | Node.js | ✓ | Promise-based | ✓ | gleam build |
| Erlang | BEAM | ✓ | Process-based | ✓ | gleam build --target erlang |
Platform-specific code is selected at compile time using @target annotations:
@target(erlang)
pub fn init_event_server() -> Result(Subject(EventMessage), String)
@target(javascript)
pub fn init_event_server() -> Result(Subject(EventMessage), String)
Sources: gleam.toml6-17 README.md98-163 test/etch_test.gleam8-28
Command Processing Flow
Execution Strategies:
stdout.execute(commands: List(Command)) - Directly calls flush_inner() and writes to stdout immediatelyQueue([]), call queue(q, commands) repeatedly to accumulate, then flush(queue) - More efficient for multiple operationsImplementation Details:
flush_inner() at src/etch/stdout.gleam53-166 pattern-matches each Command variantstyle, cursor, terminal) to generate ANSI escape sequencesstring_builder (via StringTree) accumulates sequences with O(1) append operationsio.print() call, minimizing I/O and reducing flickerSources: README.md50-77 src/etch/stdout.gleam35-51 src/etch/stdout.gleam53-166
Event Pipeline from Terminal to Application
Event Processing Flow:
event.init_event_server() at src/etch/event.gleam137-158 spawns background process and returns Subject(EventMessage)input_loop() continuously calls input_ffi.get_chars() to read raw bytes from stdin in raw modeparse_events() at src/etch/event.gleam160-296 interprets escape sequenceshandle_escape_code() routes to specialized parsers based on sequence formatparse_u_encoded_key_code(), parse_special_key_code(), parse_modifier_key_code()parse_normal_mouse(), parse_sgr_mouse(), parse_rxvt_mouse() (supports 3 mouse protocols)Subject(EventMessage) queueevent.poll(timeout) (non-blocking) or event.read() (blocking)Supported Event Types: KeyEvent, MouseEvent, FocusGained, FocusLost, Resize(width, height), CursorPosition(row, col)
Sources: src/etch/event.gleam137-158 src/etch/event.gleam160-296 test/event_test.gleam19-96 test/event_test.gleam311-331
All terminal operations are represented as data (Command variants) rather than being executed immediately. This enables:
Sources: README.md43-46 src/etch/command.gleam3-11
Etch uses Gleam's multi-target compilation with platform-specific FFI implementations:
shell:start_interactive, ETS tables for state, process-based concurrencyprocess.stdin.setRawMode(), Promises for async, Result types for errorsThe same high-level API compiles to platform-appropriate implementations.
Sources: gleam.toml6-22 src/terminal/terminal_ffi.erl5-8 src/terminal/terminal_ffi.mjs7-19
Etch depends only on Gleam's official platform libraries:
gleam_stdlib (>= 0.44.0) - core types and functionsgleam_erlang (>= 1.3.0) - Erlang/BEAM platform integrationgleam_javascript (>= 1.0.0) - JavaScript runtime integrationNo third-party terminal libraries are used. All ANSI sequence generation (in etch/internal/consts) and escape code parsing (in event.parse_events()) is implemented directly in Gleam.
Sources: gleam.toml21-24 README.md8
Implementation details are hidden via internal_modules configuration in gleam.toml, preventing users from depending on unstable internals:
These modules (etch/internal/consts, input, terminal) provide ANSI sequence constants and FFI bridge code but are not part of the public API. Only the six documented public modules are stable:
| Public Module | Key Functions/Types |
|---|---|
etch/stdout | execute(), queue(), flush(), Queue |
etch/event | init_event_server(), poll(), read(), Event, KeyEvent, MouseEvent |
etch/terminal | enter_raw(), exit_raw(), get_size(), ClearType |
etch/command | Command type with all variants |
etch/style | Color, Attribute, rgb(), ansi() |
etch/cursor | move_to(), hide(), show(), save(), restore() |
Sources: gleam.toml8-12 README.md11-46
Basic Terminal Application Structure:
Typical Application Lifecycle:
See dev/examples/hello_world.gleam and dev/examples/snake.gleam for complete examples.
Sources: README.md50-78 README.md81-93
Refresh this wiki