termify
Version, currently 0.6.09 versions
github.com/nogginly/termify.cr
A terminal rendering support shard for Crystal applications
Installation
# Add this to your shard.yml
dependencies:
termify:
github: nogginly/termify.cr
version: ~> 0.6.0Then run:
shards installshard.yml
- Crystal
- no constraint declared
- License
- MPL-2.0
- Author
- nogginly
- Targets
hellofrom samples/hello.crmd2termfrom samples/md2term.crscrollcatfrom samples/scrollcat.cr
Dependencies
Runtime Dependencies
- tablo1.1.0github: hutou/tablo
- tartrazine*github: ralsina/tartrazine
Development Dependencies
README
This README is the one indexed from the repository at its latest ref, not from the tag for this version.
Termify.cr
A Crystal shard for rendering Markdown to terminal, with an emphasis on streaming for the most part.
AI Usage
See DISCLOSURE for how I used AI for this project.
Installation
-
Add the dependency to your
shard.yml:dependencies: termify: github: nogginly/termify.cr -
Run
shards install
Usage
require "termify"
Setup platform Terminal
term = Termify.terminal
# Enable VT console
term.setup_console
# Ensure we restore terminal console settings
at_exit { term.restore_console }
Terminal stands on its own. It needs nothing from the Markdown renderer, so a
plain CLI tool can use it, and the ScrollRegion below, without rendering any
Markdown at all.
Scrolling within part of the screen
ScrollRegion confines output to a fixed number of lines, so streaming text
scrolls within them while the rest of the screen stays put. Height is clamped
to 3..10.
region = Termify::ScrollRegion.new(term, 5)
region.start
# ... anything printed now scrolls within those 5 lines
region.stop
See scrollcat for a working example.
Rendering Markdown
Termify.render_markdown do |io|
io.puts "# Hello"
io.puts "_Hello_, **World!**"
end
Custom stylesheet
Here's an example of a custom stylesheet (from md2term sample):
STYLESHEET = Termify.markdown_stylesheet({
"h1" => {
bold: true,
line_prefix: "# ".colorize(:dark_gray).to_s,
newline_after: true
},
"h2" => {bold: true, line_prefix: "## ".colorize(:dark_gray).to_s, newline_after: true, newline_before: true},
"h3" => {bold: true, line_prefix: "### ".colorize(:dark_gray).to_s, newline_after: true, newline_before: true},
"h4" => {bold: true, fg: "white", line_prefix: "#### ".colorize(:dark_gray).to_s},
"h5" => {bold: true},
"h6" => {bold: true},
"code_block" => {
fg: :light_cyan, line_number_format: "%3d: ",
highlight_theme: "catppuccin-macchiato",
gutter_style: {dim: true},
},
"code_inline" => {fg: :red},
"html_tag" => {dim: true},
"block_html" => {dim: true},
"list_item" => {newline_after: true, newline_before: true},
"block_quote" => {line_prefix: "│ ", newline_after: true, newline_before: true, bg: "Grey7"},
})
Termify.render_markdown(STDOUT, STYLESHEET) do |md_io|
# send your Markdown to `md_io`
end
Progress while gathering
Most Markdown renders as it arrives. Two things cannot: a table needs every row before it can size its columns, and a code block needs its whole body before it can be syntax highlighted. When Markdown is streaming in slowly, output stops for as long as that takes, and a reader cannot tell if the app has locked up.
Pass an on_gather handler to hear about it so you can control what to show and when.
handler = ->(event : Termify::Markdown::GatherEvent) {
case event.phase
in .started? then spinner.start(event.kind)
in .progressed? then spinner.count = event.units # rows, or lines
in .finished? then spinner.stop
end
}
Termify.render_markdown(STDOUT, STYLESHEET, on_gather: handler) do |md_io|
# send your Markdown to `md_io`
end
Nothing is written between Started and Finished, so a spinner drawn on the
current line can be cleared before the finished block lands on it. A Started is
always followed by a Finished, including when a document ends mid-table, and a
handler that raises will not stop the render.
See md2term for a working spinner, which reads slowly
enough that you can watch it.
Credits
- Tablo, for the table rendering
- Tartrazine, for the code syntax highlighting
Development
See DEVELOPMENT
Contributions, by invitation!
With apologies, at this time contributions are by invitation only and limited to people I know and see often.
These are early days for Termify and I am busy with family and work.
At this time I want to work on this at a manageable pace.
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This release
- Version
0.6.0- Tagged
- May 31, 2026
- Commit
6e091e956c0e- Indexed
- yes
Dependents
Repository
github.com/nogginly/termify.cr
Metadata
- Created
- Aug 19, 2026
- Updated
- Sep 17, 2026
- Synced
- Sep 17, 2026
- Versions
- 9