Nothing has been indexed for 1.4.2 yet. The tag is recorded, its shard.yml has not been read, so the manifest and dependency list below are empty because they are unknown rather than because they are absent.

Installation

# Add this to your shard.yml
dependencies:
  crysterm:
    github: crystallabs/crysterm
    version: ~> 1.4.2

Then run:

shards install

shard.yml

No shard.yml has been indexed for 1.4.2. You can read it on the repository.

Dependencies

Unknown: the shard.yml for this version has not been read yet.

README

This README is the one indexed from the repository at its latest ref, not from the tag for this version.

Crysterm is a console/terminal toolkit (TUI), inspired by Qt, Blessed, and Blessed-contrib.

It is implemented in Crystal. Apps using Crysterm can be written using AI.

Apple silicon hardware is recommended for development, for quickest iterations.

Main features:

Widgets

More than 90 concrete widgets with Qt-modeled APIs and behavior. Some demoed below:

Sources: tests/misc/widgets.cr and tests/misc/widgets2.cr. See also the full MainWindow chrome (menus, toolbars, docks, tabs) in tests/misc/qt_widgets.cr, per-widget demos under tests/widget/, and the directory with widgets in src/widget/.

Layouts

As in Qt, widgets are best positioned by layout engines rather than by absolute coordinates. There are 12 layouts supported.

Sources: tests/misc/layouts.cr and tests/misc/layouts2.cr. See also per-layout demos under tests/layout/, and the directory with layouts in src/layout/.

CSS and QSS styling

A complete CSS engine is used for styling -- paddings, margins, borders (including per-side and radius), colors, shadows, opacity, transitions, and pixel measures (when possible) translated to cells via the terminal's real cell geometry.

It also reads Qt QSS dialect directly — unmodified desktop Qt themes can be used to style apps.

Six independent windows below run the same scene, each loaded with a different theme from data/css/.

Source: tests/misc/themes.cr

For the minimal author-your-own-stylesheet form — one CSS string styling a whole app, including :focus restyling — see examples/css/css.cr.

Borders, Shadow, and Light

The full presentation of borders, shadows, and light can be seen in README-borders.md.

Source: tests/misc/styling.cr, tests/misc/styling2.cr, and other numbered styling files in tests/misc/

Rich text — Markdown with GFM

TextDocument (a QTextDocument work-alike) supports Markdown (CommonMark + GitHub-Featured Markdown), HTML, and Crysterm's native tags, and TextEdit/TextBrowser render it: headings, bold/italic/strikethrough, inline code and links, fenced code blocks, blockquotes, GFM tables laid out as real box-drawing tables, GFM task lists, and GFM alert admonitions — colored through the stylesheet (TextEdit::heading { color: … }), editable with full undo, navigable links.

Below is an example of a Claude-style session that is streaming a reply in Markdown (note: this is not Claude, it is Crysterm's Claude-like example):

Source: examples/claude/claude.cr

Syntax highlighting

SyntaxHighlighter mirrors Qt's QSyntaxHighlighter. Syntax formats overlay the text without touching content, undo or export; multi-line constructs carry state between blocks, and multiple highlighters can stack on one document.

Source: tests/misc/syntax.cr

Full Unicode

Unicode-native end to end: grapheme clusters (ZWJ emoji, combining marks), wide CJK cells, ambiguous-width resolution probed from the live terminal, East Asian width, and glyph chrome that auto-upgrades on modern-font terminals.

Icons and glyph chrome exist in up to three renditions, and detection is automatic:

  • ASCII — plain 7-bit fallback characters, for terminals/fonts that would otherwise show tofu.
  • Monochrome (GNU Unifont) — the deterministic bitmap face used by the built-in capture facility: captures are rasterized in-process with Unifont, so they look the same everywhere, independent of any real terminal or font.
  • Full-color — in a live terminal, emoji glyphs are drawn by the terminal's own color emoji font.

All Crysterm captures shown in this document are the middle variant; run the examples in a modern terminal to see the full-color renditions. For example, the "Save" icon (Glyphs::Role::IconSave) in all three:

ASCIICapture (Unifont)Terminal
s💾

Source: examples/text/editor/editor.cr

Native TrueColor

Colors are natively stored as full 24-bit RGB and reduced to the terminal's real capability only at output time (16.7M → 256 → 16 → 8 colors, automatically). Alpha compositing blends colors per channel in RGB space; translucent widgets, soft shadows, and smooth gradients are supported.

Source: tests/misc/truecolor.cr

Hardware and artificial cursors

Cursor shape (block / underline / bar), blink, and color are settable per window and per widget — each focused widget can present its own cursor.

When the terminal can't style its hardware cursor (or a custom glyph/style is requested), Crysterm transparently composites an artificial cursor into the cell buffer instead.

Source: tests/misc/cursors.cr

Border junctions with color blending

Adjacent and overlapping borders can merge into shared junction glyphs/chars (├ ┬ ┼ …). When the touching borders differ in color, the difference can be ignored or blended (for the smoothest seam), or the merge can be skipped.

Source: tests/misc/junctions.cr

Parseable tags in strings

Any widget content can carry inline tags (when parse_tags: true), for in-band colors ({red-fg}, {#57c7ff-bg}), attributes ({bold}, {underline}, {inverse}, {blink}), and alignment ({center}, {right}, and {|} for left/right split).

Source: tests/misc/tags.cr

Loadable fonts

BitmapFont loads GNU Unifont .hex and ttystudio .json fonts — BigText renders text with them in the terminal, and the capture pipeline rasterizes screenshots through them. Terminus and Unifont faces ship in data/font/.

Source: tests/misc/fonts.cr

Rich media — images, animated images, and video

One Media widget, twenty-one backends, selected automatically per terminal and per content (--media-backend=auto) or forced by config/CLI/env/code:

  • In-band pixels — Kitty graphics, iTerm2 inline images, Sixel, ReGIS
  • Sub-cell glyphs — octant (2×4), braille (2×4), sextant (2×3), quadrant (2×2), half (1×2), block, ASCII
  • ANSI cells — TrueColor, 256, 16, 8 colors
  • External — w3m overlay, überzug, Tektronix 4014

Formats: PNG, APNG, GIF (stills and animations), JPEG (iTerm pass-through), ANSI/BBS art (.ans, .nfo, …), and http(s):// sources. Fit modes: stretch, contain (aspect-preserving), cover, none.

Sources: tests/misc/media_graphics.cr, tests/misc/media_glyph.cr, tests/misc/media_ansi.cr

APNG and GIF animations play in every backend, with per-frame delays honored and multiple widgets optionally driven in lockstep from one shared timer — here the same GIF in four backends at once:

Source: tests/misc/animated.cr — see also tests/misc/netscape.cr

Videos play through the same widget via ffmpeg (Widget::Video): mp4, mkv, webm, mov, avi, mpeg, ts, 3gp and more, with eager or constant-memory streaming decode (media.video_decode=auto|eager|stream):

Source: tests/misc/video.cr

Terminal feature auto-detection

At startup Crysterm probes the live terminal — truecolor (DECRQSS), graphics protocols (Kitty/iTerm/Sixel), Unicode and ambiguous width, palette and default colors, cursor styling, kitty keyboard protocol, in-band resize, pixel mouse, cell pixel geometry — and automatically picks the best supported settings.

E.g. the same gauges render as real pixels on a Kitty-graphics terminal and downgrade to braille sub-cells on a plain terminal. All autodetected, no user action required.

Source: tests/misc/detect.cr — probe internals: Tput#probe!, Tput::Features, Tput::Emulator.

Everything in the toolkit tunable

Every internal setting in the toolkit has a default value and can be overriden. Unless left at (pre-set or auto-detected) defaults, values can be specified in config file (~/.config/crysterm/config.yml), env (CRYSTERM_*), CLI flag, or code — currently ~70 total low-level settings available in any Crysterm program out of the box.

crystal run app.cr -- --crysterm-dump-config=pretty  # every option + provenance
crystal run app.cr -- --media-backend=sixel          # force a graphics backend
CRYSTERM_MEDIA_BACKEND=kitty crystal run app.cr

Crysterm reads these from a copy of ARGV without consuming anything, and its two framework flags are namespaced --crysterm-config / --crysterm-dump-config, so an app's own --config stays its own.

Multiple screens

One process can drive several completely separate terminal screens.

Below, a value assigned on the left screen updates the right one, running inside a single program but on different TTYs.

Source: examples/screen/multiple/multiple.cr

Full mouse support

All the protocols — X10, SGR (1006), URxvt (1015), SGR-Pixels (1016, sub-cell pixel coordinates), and GPM on the Linux console — with hover, enter/leave, capture, double/triple-click counting, wheel scrolling, focus reporting (1004), and a GUI pointer shape over hovered widgets (OSC 22).

Real drag & drop with MIME-typed payloads (text/uri-list, …), Move/Copy/Link actions negotiated by modifiers, and the same sessions driven by keyboard. The editor demo above opens its menu and scrolls by mouse.

Paste buffer, kill-ring, undo/redo

Text widgets share a process-wide Emacs kill-ring (C-k/C-u/C-w kill, C-y yank, consecutive kills merge — kill in one field, yank in another), a full undo/redo stack on the document model (C-z/M-z, grouped edit blocks, format-preserving), GUI clipboard keys (C-c/C-x/ C-v), bracketed paste (DEC 2004), and the system clipboard over OSC 52 — copy/paste that works through SSH and tmux.

Reactive programming

An opt-in app-state layer — the toolkit itself never requires it, and plain property assignment always works. When you want it: a fine-grained reactive system (SolidJS-style): Reactive::Property, Reactive.computed, effects with automatic dependency tracking, Reactive.bind scoped to any event handler's lifetime, ObservableList for collection views, batching, and a reactive_property macro for widget classes. Assign property.value = x — every bound widget repaints itself:

Source: tests/misc/reactive.cr — full tour: tests/reactive/reactive.cr

High-performance rendering

The renderer supports compositing and damage tracking, and automatically uses whichever is faster for the workload.

Output is a minimal cell-level diff against what the terminal already shows, with CSR/BCE scroll optimizations and optional DEC 2026 synchronized output. The FPS widget reports render/draw/flush times and terminal bandwidth live:

Source: tests/misc/quicktro.cr — see also tests/misc/concurrent_rendering.cr

Screen capture

Any window or single widget can record itself and produce a still PNG (encoded in-process, no external tools), any other format — APNG, GIF, MP4, WebM, JPEG, … — by piping frames to ffmpeg, and a textual .dump format, including live recording of a running UI at a chosen fps and compositing of in-band graphics (sixel/kitty/iterm) into the capture.

Setting CRYSTERM_SHOT / CRYSTERM_ANIM / CRYSTERM_DUMP makes any Crysterm program capture itself headlessly — every image on this page was produced that way.

Source: tests/misc/minesweeper.cr — the game from examples/games/minesweeper/minesweeper.cr, playing itself through the scripted input paths below

Scripting and automation

Scripted input goes through the exact code paths that real input uses — window.press 'x' / window.press Key::Down / window.type "hello" for keys, window.click x, y / window.wheel :up, x, y for the mouse (with window.emit some_event and window.dispatch_mouse ... as the general escape hatches).

The bundled harness (tests/widget/example.cr) builds on that with a scripting driver — d.key, d.type, d.click, d.act, dwell timing — used by ~200 widget demos to film themselves, and equally usable for in-app automation and testing.

Direct (inline) mode

Apps can also run on the command line without taking over the screen. Two flavors: Crysterm::Direct for styled printing into the normal scrollback and inline windows (Window.new inline: true, optionally auto_grow: true, max_height:).

The complete widget stack, popups and all, anchored at the shell cursor like fzf. For example, a completer on a command line:

Source: examples/direct/completer/completer.cr

(Note: the above feature is currently experimental.)

Remote control

Built with -Dremote, an app exposes its widget tree as a DOM over HTTP: JSON-RPC commands (setContent, addClass, focus, append, query, snapshot, …) addressed by full CSS selectors.

UI events are streamed out over Server-Sent Events — so app behavior can live in another process, written in any language.

The layout side works without the network, too: Window#load_layout builds the UI from HTML + CSS, queryable and updatable in-process:

Source: tests/misc/dom.cr — the bundled crysterm run app.html --handler "python3 app.py" CLI is in src/remote/bin/.

(Note: the above feature is not browser output in HTML; it is control of a native program/TUI from an external program, and is currently experimental.)

Ready-to-use example apps

Programs under examples/ are complete, usable applications, meant as templates for your own.

And more

  • Terminal widget — a real VT-emulating terminal inside your UI (ptys, colors, mouse), enabling multiplexers and embedded shells.
  • Charts — line/bar/pie/donut/sparkline graph widgets on a backend-agnostic canvas that upgrades from braille to real pixels.
  • Effects — animated widget effects (e.g. Matrix rain), marquees, gradients, shadows with true alpha.
  • Hyperlinks — OSC 8 clickable links tracked per cell.
  • Keyboard — kitty keyboard protocol and modifyOtherKeys, probed and enabled automatically.
  • GPM — mouse on the bare Linux console.
  • ANSI art — CP437 .ans/.nfo art decoded and rendered with sub-cell detail.
  • Docs with pictures — crystal docs embeds each widget's capture into its API documentation automatically.

Other contributed shards

Crysterm is supported by the event model in event_handler, color routines in term_colors, terminal handling in tput.cr, GPM mouse in gpm.cr, a terminfo library in unibilium.cr, and an animated PNG/GIF parser in pnggif.

tput.cr implements all the terminal routines, and does not use ncurses. For terminfo bindings it uses unibilium, but it also supports a built-in, standard mode which does not use terminfo at all. (A lot of modern software just hardcodes the sequences.) The other important module at Crysterm's core is event_handler. through which all app events and input are routed.

Hello world

require "crysterm"

# The two conventional short aliases — nothing is `include`d into your namespace.
alias CT = Crysterm
alias CW = CT::Widgets

# A `Window` is the surface your widgets live on.
window = CT::Window.new title: "hello"

# `tagged:` is `content:` with the {tags} parsed.
CW::Box.new \
  parent: window,
  top: :center, left: :center, width: 20, height: 5,
  tagged: "{center}'Hello {bold}world{/bold}!'\nPress q to quit.{/center}",
  style: CT::Style.new(fg: "yellow", bg: "blue", border: true)

# `q` / Ctrl-Q quit by default. Run the main loop:
window.exec

A step-by-step tutorial building up from this program is in GETTING-STARTED.md, and a ready-to-copy project skeleton is in examples/template/.

Examples

git clone https://github.com/crystallabs/crysterm
cd crysterm
shards

crystal tests/hellos/hello.cr          # the program above
crystal tests/hellos/hello2.cr         # the Qt shape: MainWindow + a layout
crystal tests/misc/qt_widgets.cr   # tour of the Qt-inspired widget set
crystal tests/misc/widgets.cr      # 6 representative widgets, animated (1/2)
crystal tests/misc/widgets2.cr     # 6 more representative widgets (2/2)

Larger, complete applications:

crystal examples/mutt/mutt.cr               # a Mutt-style mail client
crystal examples/pine/pine.cr               # a Pine/Alpine-style mail client
crystal examples/claude/claude.cr           # a Claude-style rich-text chat session
crystal examples/text/editor/editor.cr      # a working Unicode text editor
crystal examples/terminal/emulator/emulator.cr  # a minimal real terminal emulator
crystal examples/terminal/tid/tid.cr        # terminal identification, live
crystal examples/screen/multiple/multiple.cr    # two windows, one app
crystal examples/direct/completer/completer.cr  # inline (fzf-style) mode
crystal examples/games/minesweeper/minesweeper.cr
crystal examples/games/pong/pong.cr
crystal examples/games/commando/commando.cr
crystal examples/games/wumpus/wumpus.cr

(And many more under examples/ and tests/.)

Dependencies and versioning

Crysterm and associated shards (tput, event_handler, term_colors, etc.) follow semantic versioning, with releases tagged vX.Y.Z. Crysterm's shard.yml pins each of those dependencies to its major version (e.g. version: ~> 2.0), so a plain clone plus shards install resolves to released versions, with minor and patch updates picked up.

To develop against the current master HEADs instead, place shard.override.yml next to shard.yml:

# shard.override.yml — track master HEADs instead of releases
dependencies:
  tput:
    github: crystallabs/tput
    branch: master
  crystallabs-helpers:
    github: crystallabs/crystallabs-helpers.cr
    branch: master
  term_colors:
    github: crystallabs/term_colors
    branch: master
  event_handler:
    github: crystallabs/event_handler
    branch: master
  gpm:
    github: crystallabs/gpm.cr
    branch: master
  pnggif:
    github: crystallabs/pnggif
    branch: master
  superconf:
    github: crystallabs/superconf.cr
    branch: master
  unibilium:
    github: crystallabs/unibilium.cr
    branch: master
  unibilium-shim:
    github: crystallabs/unibilium-shim.cr
    branch: master

For working with local checkouts, path: ../tput.cr entries instead of branch: ... work the same way.

Testing

Run crystal spec as usual.

Documentation

For a hands-on, chapter-by-chapter tutorial — from an empty directory to a styled, interactive application — see GETTING-STARTED.md.

Run crystal docs as usual for plain API docs.

For the full docs — with every widget's screenshot or animation embedded into its class documentation — run the docs pipeline instead:

crystal tools/test.cr

Invoked as shown, it (re)captures any stale example outputs, embeds each widget's capture into its doc comment, and builds crystal docs with the images copied into the docs/ tree. Such improved documentation is available as usual in docs/index.html.

To narrow the run, name a target and/or an action, e.g. re-make just one widget's captures with crystal tools/test.cr -- tests/widget/button.cr, or only re-insert image links and rebuild docs with crystal tools/test.cr -- --doc-comments --docs.

Thanks