Actions Status MetaCPAN Release

NAME

mdee - em·dee, Markdown Easy on the Eyes

SYNOPSIS

mdee [ options ] file ...

 -h  --help             show help
     --version          show version
 -d  --debug            debug level (repeatable)
 -x  --[no-]trace       trace mode (set -x)
 -n  --dryrun           dry-run mode
 -s  --style=#          output style (nup/pager/cat/filter/raw)
 -f  --filter           shortcut for --style=filter
 -p  --plain            shortcut for --style=pager
     --[no-]fold        line folding (default: on)
     --[no-]table       table formatting (default: on)
     --[no-]nup         nup paged output (default: on)
     --[no-]rule        use Unicode rules for tables (default: on)
 -w  --width=#          fold width (default: 80)
 -t  --theme=#[,#,...]  color theme(s) (default: hashed)
 -m  --mode=#           light or dark (default: light)
 -B  --base-color=#     override base color of theme
                        (e.g., Ivory, #780043, (120,0,67))
     --list-themes      list available themes
     --show=#           set field visibility (e.g., italic=1)
 -C  --pane=#           number of columns
 -R  --row=#            number of rows
 -G  --grid=#           grid layout (e.g., 2x3)
 -P  --page=#           page height in lines
 -S  --pane-width=#     pane width (default: 85)
--bs --border-style=#   border style
     --[no-]pager[=#]   pager command

VERSION

Version 0.15

DESCRIPTION

em·dee (command: mdee) is a terminal-based multi-column Markdown text viewer with syntax highlighting, particularly useful for reading documents generated by LLMs.

It displays Markdown text as-is with minimal formatting: syntax highlighting, line folding for long list items, and table column alignment. Markup characters (**, `, #, etc.) are preserved in the output. By default, closing hashes are appended to h3-h6 headers (via the built-in hashed theme) to make heading levels visually distinct. Link URLs are removed from the display, showing only the link text (e.g., [text](url) becomes [text]). On terminals supporting OSC 8 hyperlinks, the link text remains clickable.

It does not render Markdown into formatted output or reflow paragraphs. For full Markdown rendering, many other viewers are available. Combine them with nup(1) for similar paged output (e.g., nup glow README.md).

The pipeline combines greple(1) for colorization and nup(1) for multi-column paged output.

Supported elements: headers (h1-h6), bold, italic, strikethrough, inline code, code blocks, HTML comments, tables, and list items.

Multi-column Layout and Pagination

By default, mdee calculates the number of display columns by dividing the terminal width by the pane width (default 85 characters). This determines how the output is laid out and paginated.

When two or more columns fit, nup(1) arranges output in multi-column layout with page-by-page pagination — content is split into terminal-height pages viewed through a pager.

When only one column fits, mdee still uses nup for formatting (borders, document layout) but disables page-by-page splitting, so the content scrolls continuously in the pager. This avoids wasted space from page breaks on narrow terminals while maintaining the same visual appearance.

To force page-by-page pagination even in single-column layout, specify --nup explicitly. The --pane-width (-S) option adjusts the column width used for this calculation, and --pane (-C) sets the number of columns directly.

Use -p (--style=pager) for a simpler view without nup formatting — highlighted output is piped directly through a pager. Use -f (--style=filter) to write highlighted output to stdout without a pager, suitable for piping into other commands.

INSTALLATION

Homebrew

Use tecolicom/tap:

brew tap tecolicom/tap
brew install app-mdee

CPAN

cpanm -n App::mdee

OPTIONS

General Options

Processing Options

Theme Options

em·dee supports color themes for customizing syntax highlighting. Themes define colors for various Markdown elements (headers, code blocks, bold text, etc.).

Highlight Options

Layout Options (passed to nup)

Pager Options

CONFIGURATION

User configuration is loaded from:

${XDG_CONFIG_HOME:-~/.config}/mdee/config.sh

This is a shell script that can set defaults and override colors:

# ~/.config/mdee/config.sh
default[mode]='dark'             # set default mode
default[theme]='warm'            # set default theme(s)
default[style]='pager'           # set default style
default[width]=100               # set default fold width
default[base_color]='DarkCyan'   # set default base color

The default associative array supports the following keys:

Overriding theme colors and patterns

Config.sh can modify theme arrays and patterns directly, using the same mechanism as theme files:

# Change base color for both modes
theme_light[base]='<DarkCyan>=y25'
theme_dark[base]='<DarkCyan>=y80'

# Append to both light and dark using declare -n
for _array in theme_light theme_dark; do
    declare -n _theme=$_array
    _theme[h3]+=';sub{s/(?<!#)$/ ###/r}'
done

# Modify matching patterns
pattern[link]='...'

Since ${base} references are expanded after loading, changing the base color automatically affects all derived colors (h1, h2, bold, etc.).

Use -d to dump current theme and pattern values in sourceable format.

Color specification format

Color specifications use Term::ANSIColor::Concise format. The FG/BG notation specifies foreground and background colors (e.g., L25DE/${base} means gray foreground on base-colored background). The ${base} string is expanded to the base color value after loading.

EXAMPLES

mdee README.md              # view markdown file
mdee -C2 document.md        # 2-column view
mdee -G2x2 manual.md        # 2x2 grid (4-up)
mdee -w60 narrow.md         # narrower text width
mdee --no-pager file.md     # without pager
mdee --no-nup file.md       # output to stdout without nup
mdee --no-fold file.md      # disable line folding
mdee --no-table file.md     # disable table formatting

# Output styles
mdee -s pager file.md       # fold + table, output to pager
mdee -s cat file.md         # fold + table, output to stdout
mdee -s filter file.md      # table only, no fold/nup
mdee -s raw file.md         # highlight only

# Style shortcuts
mdee -p file.md             # same as --style=pager
cat file.md | mdee -f       # highlight stdin (filter mode)
mdee -f file.md             # highlight only (no paging)

# Override individual settings
mdee -f --fold file.md      # filter + fold
mdee -p --no-fold file.md   # pager without fold

# Theme examples
mdee --mode=dark file.md               # use dark mode
mdee --mode=light file.md              # use light mode
mdee -B Ivory file.md                  # override base color
mdee --mode=dark -B '#780043' file.md  # dark mode with burgundy
mdee --theme=warm file.md              # warm (Coral) base color
mdee --theme=warm,hashed file.md      # warm + closing hashes
mdee --list-themes                     # list available themes

DEPENDENCIES

This command requires the following:

IMPLEMENTATION

em·dee is implemented as a Bash script that orchestrates multiple specialized tools into a unified pipeline. The architecture follows Unix philosophy: each tool does one thing well, and they communicate through standard streams.

The overall data flow is:

Input File
    |
    v
[greple] --- Syntax Highlighting
    |
    v
[ansifold] --- Text Folding (optional)
    |
    v
[ansicolumn] --- Table Formatting (optional)
    |
    v
[nup] --- Paged Output (nup style)
    |         or
[pager] --- Pager Output (pager style)
    |
    v
Terminal

Pipeline Architecture

em·dee dynamically constructs a pipeline based on enabled options. Each stage is defined as a Bash function (e.g., run_greple, run_fold). The --dryrun option displays the function-based pipeline without execution.

Processing Stages

The pipeline consists of four configurable stages. Each stage can be enabled or disabled independently using --[no-]fold, --[no-]table, and --[no-]nup options.

Syntax Highlighting

The first stage uses greple(1) with the -G (grep mode) and --ci=G (capture index) options to apply different colors to each captured group in regular expressions.

Supported Markdown elements:

Code block detection follows the CommonMark specification:

Text Folding

The second stage wraps long lines in list items using ansifold(1) via Greple::tee. It preserves ANSI escape sequences and maintains proper indentation for nested lists.

Recognized list markers include *, -, 1., 1), #., and #). The # marker is Pandoc's auto-numbered list syntax.

The folding width is controlled by --width option (default: 80).

Table Formatting

The third stage formats Markdown tables using ansicolumn(1). Tables are detected by the pattern ^(\|.+\|\n){3,} and formatted with aligned columns while preserving ANSI colors.

Output Stage

The final stage uses nup(1) to provide multi-column paged output. Layout options (--pane, --row, --grid, --page) are passed directly to nup.

Theme System

em·dee implements a theme system where themes are transformations applied to the built-in default theme. Multiple themes can be chained via --theme=NAME1,NAME2,....

Theme Structure

The built-in default theme is defined as theme_light and theme_dark associative arrays. Dark inherits undefined keys from light immediately after declaration. Theme files can modify these arrays and the pattern[] array directly:

# theme/warm.sh — change colors
theme_light[base]='<Coral>=y25'
theme_dark[base]='<Coral>=y80'

# modify matching patterns
pattern[link]='...'

Base Color Expansion

The ${base} placeholder is expanded to the effective base color after theme loading. The base color is determined by the --base-color option (default: RoyalBlue) with automatic luminance adjustment based on mode (=y25 for light, =y80 for dark).

Color Specifications

Colors are specified using Term::ANSIColor::Concise format. The --cm option maps colors to captured groups. For example, L00DE/${base} specifies gray foreground on base-colored background.

The color specification supports modifiers:

Terminal Mode Detection

em·dee uses Getopt::EX::termcolor to detect terminal background luminance. If luminance is below 50%, dark mode is automatically selected.

LIMITATIONS

HTML Comments

Only HTML comments starting at the beginning of a line are highlighted. Inline comments are not matched to avoid conflicts with inline code containing comment-like text (e.g., `<!-->`).

Emphasis

Emphasis patterns (bold and italic) do not span multiple lines. Multi-line emphasis text is not supported.

Link patterns do not span multiple lines. The link text and URL must be on the same line.

Links inside other highlighted elements (such as headings or bold text) are not processed.

Reference-style links ([text][ref] with [ref]: url elsewhere) are not supported.

Links are converted to OSC 8 terminal hyperlinks for clickable URLs:

This requires terminal support. Compatible terminals include iTerm2, Kitty, WezTerm, Ghostty, and recent versions of GNOME Terminal. Apple's default Terminal.app does not support OSC 8.

When using less as pager, version 566 or later is required with -R option.

For OSC 8 specification, see: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda

Less Environment Variables

When less is used as pager (either directly via --style=pager or through nup), the following environment variables affect behavior. mdee sets defaults for these when they are not already defined:

If you already have these variables set in your environment, mdee does not override them.

SEE ALSO

nup(1), greple(1), ansifold(1), ansicolumn(1)

AUTHOR

Kazumasa Utashiro

LICENSE

Copyright 2026 Kazumasa Utashiro.

This software is released under the MIT License. https://opensource.org/licenses/MIT