Skip to content
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ ink --plain README.md

Headings, bold, italic, strikethrough, links, blockquotes, lists, task lists, tables, footnotes, horizontal rules — all rendered with proper styling and colors.

Tables follow their column alignment (`:--`, `:-:`, `--:`), wide CJK and emoji cells included. Superscript `x^2^` (or `x<sup>2</sup>`) and subscript `H<sub>2</sub>O` come out as `x²` and `H₂O` (as `^(…)`/`_(…)` when a character has no Unicode form). Single and double tildes, `~x~` and `~~x~~`, strike through as on GitHub. `||spoilers||` stay hidden until you select or copy them, and definition lists (`Term` then `: definition`) get a bold term with its definitions indented underneath.

### Syntax-highlighted code blocks

Language-aware highlighting for every major language. Code blocks get clean borders with the language label shown at the top.
Expand All @@ -164,7 +166,11 @@ Flowcharts, sequence diagrams, pie charts, and Gantt charts rendered as ASCII ar

### GitHub-style admonitions

`[!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]`, and `[!CAUTION]` blocks render with distinct colors and icons.
`[!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]`, and `[!CAUTION]` blocks render with distinct colors and icons. A custom title (`> [!NOTE] Read this first`) replaces the type name, and Obsidian callouts (`> [!info]- Title`, `[!bug]`, `[!example]`, …) map onto the same five colours, fold markers ignored.

### Frontmatter

YAML (`---`), TOML (`+++`) and JSON (`{`) frontmatter is hidden by default. With `--frontmatter` (or `frontmatter = true`) it shows as a small key/value box at the top of the document — lists comma-joined, nested values as written — instead of being read as markdown.

### Wikilinks

Expand Down Expand Up @@ -243,7 +249,7 @@ On the Linux console, a non-UTF-8 locale, a legacy console font or a screen read

### Math and emoji

Inline `$E=mc^2$` and block `$$...$$` math render in code style, and `:emoji:` shortcodes resolve to their glyph (`:rocket:` → 🚀).
Inline `$E=mc^2$`, block `$$...$$` and ```` ```math ```` render as Unicode text: `$x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}$` reads `x = (-b ± √(b² - 4ac))/2a`. Greek letters, operators, `\mathbb{R}` → ℝ, super- and subscripts, fractions, roots, and matrix, `cases` and `aligned` environments (drawn on several lines with brackets) are covered; anything else is shown as written. A lone `$` in prose (`costs $5 and $10`) stays a dollar sign. With `--ascii` the LaTeX source is shown instead. `:emoji:` shortcodes resolve to their glyph (`:rocket:` → 🚀).

### Presentation mode

Expand All @@ -257,6 +263,8 @@ ink --slides deck.md

Point `ink --plain` at a long document on an interactive terminal and it pages the output through `$PAGER` (default `less -R`) — a drop-in markdown replacement for `cat`/`less`. Piped or redirected output prints straight through, so it stays friendly for scripts, fzf previews, and git. Use `--no-pager` to always print directly. When stdout is not a terminal, `ink file.md` behaves like `ink --plain file.md`, as `bat` and `glow` do.

`--line-range` works like `bat`'s: `ink --line-range 40:80 README.md` renders only those lines of the markdown *source* (1-based, inclusive; also `40:`, `:80`, a single line, or several `--line-range`s). A range that cuts through a fenced code block keeps the block intact and highlighted. It always prints plain output.

Color follows `--color <auto|always|never>` (default `auto`). In `auto` mode, `--plain` and `ink diff` emit color and OSC 8 hyperlinks only when stdout is a terminal, so redirects, pipes, and `git` textconv get clean text. Precedence: an explicit `--color` wins; then `NO_COLOR` turns color off; then `CLICOLOR_FORCE=1` or `FORCE_COLOR` turns it on; then `TERM=dumb` turns it off; then the terminal check. Tools that display ANSI from a pipe need `--color=always`:

```bash
Expand Down Expand Up @@ -361,7 +369,7 @@ spacing = "normal"
# Show table of contents on startup
toc = false

# Show YAML/TOML frontmatter
# Show YAML/TOML/JSON frontmatter as a metadata box at the top
frontmatter = false

# Behavior
Expand Down Expand Up @@ -428,7 +436,9 @@ Options:
--image-protocol <P> auto | kitty | iterm2 | sixel | halfblocks
--list-themes List available themes and exit
--no-pager Never page --plain output, even on a TTY
--frontmatter Show YAML/TOML frontmatter
--frontmatter Show YAML/TOML/JSON frontmatter as a metadata box
--line-range <START:END> Render only these source lines (N, START:, :END;
repeatable; implies --plain)
--spacing <MODE> Line spacing: compact, normal, relaxed
--no-mouse Don't capture the mouse (overrides mouse_capture in config)
--ascii Draw borders, bullets and markers in plain ASCII
Expand Down
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ <h3>Open links by keyboard</h3>
</div>
<div class="feature">
<h3>Math &amp; emoji</h3>
<p>Renders <code>$math$</code> in code style and resolves <code>:emoji:</code> shortcodes.</p>
<p>Renders <code>$math$</code> as Unicode text (Greek, fractions, roots, matrices) and resolves <code>:emoji:</code> shortcodes.</p>
</div>
<div class="feature">
<h3>Admonitions</h3>
Expand Down
13 changes: 8 additions & 5 deletions docs/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ ink is a feature-rich terminal markdown reader built in Rust. It renders markdow
- Bold, italic, strikethrough, inline code
- Links with OSC 8 hyperlink support (Cmd+Click / Ctrl+Click in supported terminals)
- Syntax-highlighted code blocks using syntect (supports all major programming languages)
- Tables with auto-sized columns and word wrapping
- Tables with auto-sized columns, word wrapping and column alignment (:--, :-:, --:)
- Blockquotes with nested support and depth-based coloring
- Ordered and unordered lists with nesting
- Task lists with checkbox rendering
- Footnotes
- Superscript ^x^ and <sup>x</sup>, subscript <sub>x</sub> (Unicode forms, ^(x)/_(x) fallback); ~x~ and ~~x~~ are strikethrough as on GitHub; ||spoilers||, definition lists (Term / : definition)
- Horizontal rules
- Inline/block math ($...$, $$...$$) rendered in code style; :emoji: shortcodes resolved to glyphs
- HTML blocks shown as text; common inline HTML (<br>, <sub>, <sup>, <kbd>) handled rather than dropped
- YAML and TOML frontmatter stripping (optional display)
- Inline/block math ($...$, $$...$$, ```math) rendered as Unicode text: Greek letters, operators, \mathbb/\mathcal letters, super/subscripts, \frac as a/b, \sqrt, matrix/cases/aligned environments on several lines; unknown commands kept as written; ASCII mode shows the LaTeX source. A lone $ in prose stays a dollar sign. :emoji: shortcodes resolved to glyphs
- HTML blocks shown as text; common inline HTML (<br>, <sub>, <sup>, <kbd>) handled rather than dropped, <sub>/<sup> as Unicode sub/superscripts
- YAML, TOML and JSON frontmatter: stripped by default; --frontmatter shows it as a key/value box

### Images
- Real pixel rendering via terminal graphics protocols (Kitty graphics, iTerm2 inline, Sixel), auto-detected at startup; --image-protocol auto|kitty|iterm2|sixel|halfblocks
Expand All @@ -37,7 +38,8 @@ ink is a feature-rich terminal markdown reader built in Rust. It renders markdow
- Unknown diagram types get fallback rendering

### Admonitions
- GitHub-style admonition blocks: [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION]
- GitHub-style admonition blocks: [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION], with custom titles ([!NOTE] Title)
- Obsidian callouts ([!info], [!bug], [!example], ...; +/- fold markers ignored) mapped onto the same colours
- Each type has a distinct color and icon

### Wikilinks
Expand Down Expand Up @@ -86,6 +88,7 @@ ink is a feature-rich terminal markdown reader built in Rust. It renders markdow
- --color auto|always|never (default auto): color and OSC 8 links only when stdout is a terminal; precedence is --color, then NO_COLOR, then CLICOLOR_FORCE/FORCE_COLOR, then TERM=dumb, then the TTY check. Use --color=always for fzf previews or less -R; git textconv (`ink --plain`) gets clean text
- `ink -` reads the document from stdin
- Pager mode: on an interactive terminal, --plain pages long output through $PAGER (default less -R), like bat; --no-pager disables it
- --line-range START:END (also START:, :END, N; repeatable): render only those 1-based source lines; a cut code fence is reopened/closed; implies --plain
- Document outline: `ink outline <file>` shows heading structure
- Document stats: `ink stats <file>` shows word count, reading time, element counts
- Markdown diff: `ink diff <file_a> <file_b>` compares two files with a real Myers line diff
Expand Down
9 changes: 5 additions & 4 deletions docs/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@ ink is a command-line tool that renders markdown files in the terminal with rich

## What it does

- Renders markdown with syntax highlighting, tables, blockquotes, lists, task lists, footnotes, and horizontal rules
- Renders markdown with syntax highlighting, tables (with column alignment), blockquotes, lists, task lists, footnotes, definition lists, superscript (^x^, <sup>) and subscript (<sub>), spoilers, and horizontal rules
- Displays inline images using terminal graphics protocols (Kitty, iTerm2, Sixel) where supported, falling back to Unicode half-block characters (any true-color terminal); --image-protocol selects the renderer
- Renders mermaid diagrams (flowcharts, sequence diagrams, pie charts, Gantt charts) as ASCII art
- Supports GitHub-style admonitions: NOTE, TIP, IMPORTANT, WARNING, CAUTION
- Supports GitHub-style admonitions (NOTE, TIP, IMPORTANT, WARNING, CAUTION) with custom titles, and Obsidian callouts
- YAML, TOML and JSON frontmatter: hidden by default, shown as a key/value box with --frontmatter
- Supports wikilink syntax: [[page]] and [[page|display text]]
- Renders inline/block math ($...$, $$...$$) in code style and resolves :emoji: shortcodes
- Renders inline/block math ($...$, $$...$$, ```math) as Unicode text (Greek, operators, super/subscripts, fractions, roots, matrices, cases) and resolves :emoji: shortcodes
- Interactive TUI with multi-tab support, in-document search (n/N to cycle matches), keyboard link opening (press f), a help overlay (?), and a table of contents sidebar
- Text selection and clipboard copy: v/V for character- and line-wise visual selection (y copies), mouse drag with double-click word and triple-click line, c to copy a code block's raw source, Y to copy the current section's markdown; delivered over OSC 52 (works across SSH/tmux) and a native helper (pbcopy/wl-copy/xclip/xsel/clip.exe)
- File browser mode for exploring directories of markdown files
- 8 built-in color themes with auto dark/light detection, live theme picker that saves to config, plus custom TOML themes
- Presentation mode (split on --- separators), navigate with arrows/space
- Watch mode for live-reloading on file changes
- Pager mode: `ink --plain` on a TTY pages long output through $PAGER (like bat)
- Pager mode: `ink --plain` on a TTY pages long output through $PAGER (like bat); --line-range START:END renders only those source lines
- Honors NO_COLOR and downgrades to 256-color on non-truecolor terminals
- Document statistics, outline view, and a real (Myers) markdown diff
- Vim-style keybindings (j/k/G/Ctrl+f/Ctrl+b), also emacs preset, fully customizable
Expand Down
20 changes: 11 additions & 9 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2266,19 +2266,21 @@ fn build_tab(
} else {
None
};
let (_, stripped) = if args.frontmatter {
(None, source.to_string())
} else {
frontmatter::strip_frontmatter(&source)
};
// Frontmatter is stripped, or (with --frontmatter) swapped for a fenced
// block the layout draws as a metadata box.
let stripped = frontmatter::prepare(&source, args.frontmatter);

// Pre-process wikilinks before parsing
let content = crate::wikilink::process_wikilinks(&stripped);
// Frontmatter comes off the top whole, and wikilinks never add or remove
// a line; if either ever stops holding, the mapping is unknown.
// Source line = content line + offset. Only the head of the document
// changes: stripping removes whole lines, and the metadata box usually
// keeps the line count. If the parsed text has MORE lines than the source
// (JSON frontmatter gains fence lines), or wikilinks ever change the line
// count, the mapping is unknown and no line is passed to the editor.
let line_offset = source
.strip_suffix(stripped.as_str())
.map(|head| head.matches('\n').count())
.matches('\n')
.count()
.checked_sub(stripped.matches('\n').count())
.filter(|_| content.matches('\n').count() == stripped.matches('\n').count());

let arena = Arena::new();
Expand Down
23 changes: 19 additions & 4 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,18 @@ pub struct Cli {
#[arg(long)]
pub no_pager: bool,

/// Show YAML/TOML frontmatter
/// Show YAML/TOML/JSON frontmatter as a metadata box at the top
/// (hidden by default)
#[arg(long)]
pub frontmatter: bool,

/// Render only these SOURCE lines of the markdown (1-based, inclusive):
/// `START:END`, `START:`, `:END` or `N`; repeatable. A fenced code block
/// cut by a range is reopened/closed so it still highlights. Implies
/// --plain
#[arg(long, value_name = "START:END", value_parser = parse_line_range)]
pub line_range: Vec<crate::parser::line_range::LineRange>,

/// Line spacing [default: normal]
#[arg(long, value_enum)]
pub spacing: Option<Spacing>,
Expand Down Expand Up @@ -120,6 +128,11 @@ fn parse_width(s: &str) -> Result<WidthArg, String> {
}
}

/// `--line-range` parser.
fn parse_line_range(s: &str) -> Result<crate::parser::line_range::LineRange, String> {
crate::parser::line_range::LineRange::parse(s)
}

/// `--image-protocol` parser (keeps the aliases `ProtocolChoice::parse` accepts).
fn parse_protocol(s: &str) -> Result<crate::graphics::ProtocolChoice, String> {
crate::graphics::ProtocolChoice::parse(s)
Expand Down Expand Up @@ -357,7 +370,8 @@ pub fn run() -> Result<()> {
slides: cli.slides,
// The interactive reader needs a terminal: with stdout piped or
// redirected (`ink file.md | cat`), render plain output instead.
plain: cli.plain || !stdout_tty,
// `--line-range` has no reader equivalent: it renders plain.
plain: cli.plain || !stdout_tty || !cli.line_range.is_empty(),
watch: cli.watch,
toc,
images: if cli.no_images {
Expand Down Expand Up @@ -438,8 +452,9 @@ pub fn run() -> Result<()> {
let sources = read_all_inputs(&args)?;
let mut rendered = String::new();
for source in &sources {
let source = crate::parser::line_range::select(source, &cli.line_range);
rendered.push_str(&render::plain::render_plain_with_color(
source, &args, color,
&source, &args, color,
)?);
}
emit_plain(&rendered, cli.no_pager);
Expand Down Expand Up @@ -671,7 +686,7 @@ pub(crate) const STARTER_CONFIG: &str = r#"# ink configuration
# Show table of contents on startup
# toc = false

# Show YAML/TOML frontmatter as a code block at the top of the document
# Show YAML/TOML/JSON frontmatter as a metadata box at the top of the document
# frontmatter = false

[behavior]
Expand Down
Loading
Loading