ultraviolet v1.0.1
Ultraviolet
Crystal port of Charm's Ultraviolet terminal UI library. The upstream Go implementation is tracked as a git submodule at ultraviolet_go/ for behavioral reference.
Status: behavior-complete against Go 524a660. See parity plan.
Installation
Add to shard.yml:
dependencies:
ultraviolet:
github: dsisnero/ultraviolet
Then:
shards install
Quick Start
require "ultraviolet"
env = ENV.map { |key, value| "#{key}=#{value}" }
term = Ultraviolet::Terminal.new(STDIN, STDOUT, env)
term.start
term.enter_alt_screen
"Hello, World!".each_char_with_index do |char, idx|
term.set_cell(idx, 0, Ultraviolet::Cell.new(char.to_s, 1))
end
term.display
See examples/helloworld.cr for a complete interactive application.
Features
Terminal Screen & Input
- Full-window and inline rendering modes (
TerminalScreen) - Raw terminal mode with signal handling (
Terminal) - Event-driven input: keyboard, mouse, paste, focus/blur, resize (
EventDecoder) - Kitty keyboard protocol, modifyOtherKeys, Win32 input, bracketed paste
- Legacy key encoding support via
LegacyKeyEncoding
High-Performance Renderer
The TerminalRenderer minimizes terminal output using cell-based diffing and ANSI optimizations:
- REP (repeat character), ECH (erase character), ICH/DCH (insert/delete character)
- SD/SU (scroll down/up), IL/DL (insert/delete line)
- CHA/HPA/VPA (cursor positioning), CHT/CBT (tab movement)
- Color profile downsampling (TrueColor → 256 → 16 → ASCII)
- Backspace/tab optimization for cursor movement
- Per-terminal capability detection
Cell-Based Buffer System
The Buffer / RenderBuffer / ScreenBuffer hierarchy provides:
- Cell-level read/write with wide character support (emoji, CJK)
- Touch tracking for efficient dirty-region rendering
- Clone, fill, clear area operations
- Insert/delete line/cell with bounds checking
ANSI Styled Strings
StyledString renders ANSI-escaped text with:
- SGR attribute parsing (bold, faint, italic, blink, reverse, conceal, strikethrough)
- TrueColor / 256 / 16 color foreground, background, underline
- Hyperlink (OSC 8) parsing
- Text wrapping, truncation with tail, placement functions
Screen Window System
Window provides nested, resizable viewports:
- Parent-child hierarchy with relative positioning
- MoveTo, MoveBy, Resize, Clone, CloneArea
- Custom width methods for CJK/emoji/grapheme support
Layout System
Layout functions for constraint-based positioning:
Percent,Fixed,RatioconstraintsSplitVertical,SplitHorizontal- 9 rect placement helpers: Center, TopLeft, TopRight, BottomLeft, etc.
Cross-Platform Console I/O
Console provides a unified interface across platforms:
- Unix: TTY detection, raw/restore terminal state,
Winsizequeries - Windows:
WinConconsole handle wrapper - Environment variable access via
Environ
Poll-Based Event Reading
Platform-optimized poll readers in poll.cr:
- BSD/macOS:
kqueueReadervia Kqueue - Linux:
epollReadervia Epoll - Windows:
conReadervia Windows Console API - Fallback:
fallbackReaderfor non-TTY file descriptors
Border Drawing
Border provides configurable box-drawing:
- Built-in styles: Normal, Rounded, Double, Thick, Hidden, Block, Markdown, ASCII
- Style/Link application without mutating base border
Drawrenders border into anyScreenregion
Module Overview
| Module | Go Source | Crystal Source |
|---|---|---|
| Terminal | terminal.go | terminal.cr |
| TerminalScreen | terminal_screen.go | terminal_screen.cr |
| TerminalRenderer | terminal_renderer.go | terminal_renderer.cr |
| TerminalReader | terminal_reader.go | terminal_reader.cr |
| EventScanner | terminal_reader.go (eventScanner) | event_scanner.cr |
| EventDecoder | decoder.go | decoder.cr |
| Buffer | buffer.go | buffer.cr |
| Cell/Style | cell.go | cell.cr, style.cr |
| StyledString | styled.go | styled.cr |
| Console | console.go | console.cr |
| Border | border.go | border.cr |
| Window | window.go | window.cr |
| Layout | layout/layout.go | layout.cr |
| Poll | poll*.go | poll*.cr |
| Winch | winch.go | winch.cr |
| TabStops | tabstop.go | tabstop.cr |
| Options | terminal.go (Options) | options.cr |
| Key types | key.go, key_table.go | key.cr, key_table.cr |
| Mouse | mouse.go | mouse.cr |
| Event types | event.go | event.cr |
| TTY | tty*.go | tty.cr, tty_state.cr |
Documentation
- Parity Plan — feature status and remaining work
- Tutorial — building a terminal application with Ultraviolet
- Examples — runnable Crystal examples
- Parity Manifests — Go-vs-Crystal tracking
Development
Setup
git submodule update --init --recursive
shards install
Parity Checks
# Check port inventory completeness
./scripts/check_port_inventory.sh . plans/inventory/go_port_inventory.tsv ultraviolet_go go
# Check source API parity
./scripts/check_source_parity.sh . plans/inventory/go_source_parity.tsv ultraviolet_go go
# Check test parity
./scripts/check_test_parity.sh . plans/inventory/go_test_parity.tsv ultraviolet_go go
# Adversarial verification
./scripts/verify_parity_adversarial.sh . ultraviolet_go go 'crystal spec' 'go test ./...'
Quality Gates
crystal tool format --check src spec
ameba src spec
crystal spec
Updating the Upstream Submodule
git -C ultraviolet_go fetch --tags
git -C ultraviolet_go checkout <tag-or-sha>
git add ultraviolet_go
git commit -m "Update ultraviolet_go submodule"
Then regenerate manifests:
PORT_FORCE_OVERWRITE=1 ./scripts/generate_source_parity_manifest.sh . '' ultraviolet_go go
PORT_FORCE_OVERWRITE=1 ./scripts/generate_test_parity_manifest.sh . '' ultraviolet_go go
Contributing
- Fork it (https://github.com/dsisnero/ultraviolet/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Port from Go source of truth, matching behavior exactly
- Write Crystal specs matching Go test expectations
- Run quality gates:
crystal spec && crystal tool format --check src spec && ameba src spec - Run parity checks:
./scripts/check_*.sh - Commit and push
- Create a Pull Request
License
MIT — see LICENSE.
Contributors
- Dom — creator and maintainer
ultraviolet
- 0
- 0
- 0
- 3
- 6
- 12 days ago
- February 3, 2026
MIT License
Wed, 27 May 2026 04:08:05 GMT