przn
A terminal-based presentation tool written in Ruby. Renders Markdown slides with Kitty text sizing protocol support for beautifully scaled headings.
Installation
gem install przn
Usage
przn .md
To open the presentation directly at a specific slide, append @N (1-based):
przn your_slides.md @42
Out-of-range numbers are clamped to the last slide, so @9999 jumps to the end.
PDF export
przn --export your_slides.md
przn --export pdf your_slides.md
przn --export pdf -o output.pdf your_slides.md
Requires a TrueType font (with glyf outlines) for proper rendering. Prawn does not support CFF-based fonts (most .otf files). Fonts are auto-detected in this order: NotoSansJP TTF, HackGen, Arial Unicode.
Key bindings
| Key | Action |
|---|---|
→ ↓ l j Space |
Next slide |
← ↑ h k |
Previous slide |
g |
First slide |
G |
Last slide |
q Ctrl-C |
Quit |
Selecting and copying text
przn doesn't capture mouse events, so drag-to-select and the terminal's own copy shortcut (Kitty: Cmd+C on macOS, Ctrl+Shift+C on Linux) work normally on a slide. Mouse-tracking modes that may have leaked from a previously crashed program are explicitly disabled on entry, so drag selection is reliable.
Markdown format
przn's Markdown format is compatible with Rabbit's Markdown mode.
Slide splitting
Slides are separated by # (h1) headings.
# Slide 1
content
# Slide 2
more content
Text formatting
*emphasis*
**bold**
~~strikethrough~~
`inline code`
Long lines wrap at whitespace boundaries (not mid-word) for English-style text. A single word that's longer than the line — a URL, a class name — still wraps at the character it has to. CJK runs without inter-character whitespace fall back to per-character splitting.
Lists
* item 1
* item 2
* nested item
- also works as bullets
1. ordered
2. list
Code blocks
Fenced code blocks:
```ruby
puts "hello"
```
Indented code blocks (4 spaces) with optional kramdown IAL:
def hello
puts "world"
end
{: lang="ruby"}
Block quotes
> quoted text
> continues here
Tables
| Header 1 | Header 2 |
|----------|----------|
| cell 1 | cell 2 |
Definition lists
term
: definition
Text sizing
Uses Rabbit-compatible {::tag} notation. Supported size names: xx-small, x-small, small, large, x-large, xx-large, xxx-large, xxxx-large, and numeric 1-7.
{::tag name="x-large"}Big text{:/tag}
{::tag name="7"}Maximum size{:/tag}
An XML-style alternative is also accepted:
<size=x-large>Big text</size>
<size=7>Maximum size</size>
On Kitty-compatible terminals, sized text is rendered using the OSC 66 text sizing protocol. On other terminals, the markup is silently ignored.
Color
Named ANSI colors (red, green, yellow, blue, magenta, cyan, white, plus bright_* variants) and 6-digit hex. Use {::tag name="..."} (kramdown form) or the color attribute on <font> (see Font).
{::tag name="red"}warning{:/tag}
{::tag name="ff5555"}custom hex{:/tag}
<font color="red">warning</font>
<font color="ff5555">custom hex</font>
Font
HTML 4-style <font> tag with face, size, and color attributes. Any subset, in any order. The kramdown shape is also accepted.
<font face="Helvetica Neue">Title</font>
<font face="Menlo" size="3">code</font>
<font face="Menlo" size="3" color="red">flagged</font>
{::font name="Helvetica Neue"}Title{:/font}
face requires a terminal that honors the OSC 66 f= extension (e.g. Echoes). For PDF export, the family is registered with Prawn via fontconfig — families that can't be found fall through to the default font.
Alignment
{:.center}
centered text
{:.right}
right-aligned text
XML form (single-line, paragraph-level):
<center>centered <size=3>text</size></center>
<right>right-aligned</right>
Slide background
Set a per-slide background — solid color or linear gradient — via a self-closing block-level directive. Uses the Echoes OSC 7772 extension; other terminals ignore the escape sequence.
# Title
<bg color="#1a1a2e"/>
content...
# Second slide
<bg from="#1a1a2e" to="#16213e" angle="90"/>
content...
The previous slide's background is cleared on every navigation, and on przn exit, so your shell isn't left tinted.
Comments
{::comment}
This text is hidden from the presentation.
{:/comment}
Notes
Visible text {::note}(speaker note){:/note}
Visible text <note>(speaker note)</note>
Escaping <, >, &
To show literal markup characters that would otherwise be interpreted as a tag, use HTML-style entity references:
<note> renders as: <note>
2 < 3 renders as: 2 < 3
A & B renders as: A & B
A bare < not followed by a recognized tag name renders literally as well, so most accidental < characters are fine. The entities are only needed when you'd otherwise hit one of the tag patterns (<size=...>, <font ...>, <note>, <wait/>, <center>, <right>, <bg .../>).
Wait marker
Self-closing presentation flow marker, consumed at parse time:
{::wait/}
<wait/>
Theming
Pass a YAML file via --theme path/to/theme.yml. All keys are optional — anything you don't set falls back to the defaults baked in at default_theme.yml.
colors:
background: "000000"
foreground: "ffffff"
heading: # falls back to foreground
code_bg: "313244"
dim: "6c7086"
inline_code: "a6e3a1"
font:
family: # body text font; terminal: OSC 66 f=, PDF: Prawn font
size: 18 # base PDF font size in pt
bullet: "・" # unordered-list marker; also h2–h6 prefix
bullet_size: # OSC 66 scale (1–7) for the bullet glyph
heading_face: # font family for h1 (slide titles)
bg: # default slide background (Echoes OSC 7772)
color: # solid, e.g. "#1a1a2e"
from: # gradient endpoint
to: # gradient endpoint
angle: # gradient angle in degrees
Notes:
bullet/bullet_size—bulletis the character;bullet_sizeis the OSC 66 scale used to render it. When smaller than the body text scale, the bullet is rendered with fractional scaling and vertical centering so it still aligns with the body line.font.family— applied to body text (terminal: via OSC 66f=, requires Echoes; PDF: registered via fontconfig). Inline<font face="...">runs override it per-segment.heading_face— independent fromfont.family. h1 usesheading_faceif set, else falls back to the terminal's default font (it does not silently inheritfont.family). h2–h6 are body text. When the chosen face is proportional, every h1 OSC 66 sequence is emitted withh=2so a terminal that honors centered horizontal alignment (Echoes) keeps the title visually centered against its reserved cell block.bg— the deck-wide default background. A per-slide<bg .../>directive overrides it for that slide.
License
The gem is available as open source under the terms of the MIT License.