view release on metacpan or search on metacpan
theme_light[base]='<DarkCyan>=y25'
theme_dark[base]='<DarkCyan>=y80'
# config.sh or theme file: enable hashed headings via md module config
md_config+=(hashed.h3=1 hashed.h4=1 hashed.h5=1 hashed.h6=1)
```
#### Theme Listing
- `--list-themes`: Shows preview samples for all available themes. Field names are theme keys excluding `base`.
- Color specs use [Term::ANSIColor::Concise](https://metacpan.org/pod/Term::ANSIColor::Concise): `L00`-`L25` (gray scale), `${base}` (placeholder), `+l10`/`-l10` (lightness adjust), `D`/`I`/`U`/`E` (bold/italic/underline/erase), `FG/BG`.
### User Configuration
Config file: `${XDG_CONFIG_HOME:-~/.config}/mdee/config.sh`
The config file is sourced at global scope (not inside a function), so `declare -A` creates global variables. This allows custom themes to be defined in config.sh.
The `default` associative array supports:
| Key | Option | Example |
_key=${_entry%%=*} _val=${_entry#*=}
if [[ -v theme_light[$_key] ]]; then
theme_light[$_key]="$_val"
theme_dark[$_key]="$_val"
else
md_config+=("$_entry")
fi
done
```
Color labels (h1, bold, etc.) go to md_config and are handled by the md module's `Getopt::EX::Config` (pre-declared with `undef` default). Priority: default colors â config params â `--cm`.
Note: `--cm` is passed to the md module (before `--` in greple invocation), so it only works for md module labels (lowercase: `h1`, `bold`, etc.). Theme keys `FILE`/`FILE_FORMAT` are passed to greple's own `--cm` (after `--`), so they can only be set...
## Implementation Notes
### Pipeline Architecture
mdee constructs a pipeline dynamically:
```mermaid
flowchart LR
A[Input File] --> B[greple -Mmd]
- The `\e[m` reset at end enables **cumulative coloring**: when `apply_color` wraps placeholder with outer color (e.g., heading), it re-inserts the outer color after the reset
- Each step is a `Step` object: `Step(sub{})` (always active) or `Step(label => sub{})` (controllable via `--show`)
- Pipeline order determined by `build_pipeline()` based on `heading_markup`
#### Pipeline Architecture
Steps are organized into three groups:
```perl
my @protect_steps = qw(code_blocks comments image_links images links);
my @inline_steps = qw(inline_code horizontal_rules bold_italic bold italic strike);
my @final_steps = qw(blockquotes);
```
The `headings` step always calls `restore()` â color â `protect()`,
enabling cumulative coloring regardless of position. Its position
in the pipeline is controlled by `heading_markup`:
**`build_pipeline()` logic:**
- `heading_markup=0` (default): `@protect_steps`, **headings**, `@inline_steps`, `@final_steps`
- `heading_markup=1` or `all`: `@protect_steps`, `@inline_steps`, **headings**, `@final_steps`
- `heading_markup=bold:italic`: `@protect_steps`, bold, italic, **headings**, inline_code, horizontal_rules, strike, `@final_steps`
When `heading_markup` is a colon-separated step list, specified steps
are moved before headings while remaining inline steps stay after.
This allows selective markup inside headings (e.g., bold but not code).
Links are always processed before headings (in `@protect_steps`), so
OSC 8 hyperlinks in headings remain clickable regardless of
`heading_markup`.
The `colorize()` loop calls `$step->active` and `$step->run`:
```perl
for my $name (build_pipeline()) {
my $step = $colorize{$name};
| Theme Key | Module Label | Description |
|-----------|-------------|-------------|
| `code_mark` | `code_mark` | Fenced code block delimiters (```` ``` ````, `~~~`) |
| `code_tick` | `code_tick` | Inline code backticks (`` ` ``) |
| `code_info` | `code_info` | Fenced code block info string |
| `code_block` | `code_block` | Fenced code block body (with `;E`) |
| `code_inline` | `code_inline` | Inline code body (without `;E`) |
| `emphasis_mark` | `emphasis_mark` | Emphasis markers (`**`, `*`, `__`, `_`, `~~`) |
`emphasis_mark` is the default color for all emphasis markers. Per-type overrides
(`bold_mark`, `italic_mark`, `strike_mark`) can be defined via `--cm` and take
priority when present. This follows the same pattern as `code_tick` / `code_inline`.
```bash
# Light mode
[code_mark]='L20'
[code_tick]='L15/L23'
[code_info]='${base_name}=y70'
[code_block]='/L23;E'
[code_inline]='L00/L23'
[emphasis_mark]='L18'
for k in "${show_fields[@]}"; do
show[$k]=$val
done
fi
}
```
- `%!`: Hash type with callback
- Callback receives `($optname, $arg)` - parse key=value from `$arg`
- `all` is special: sets all fields to the given value
- Order matters: `--show all= --show bold` disables all, then enables bold
- Individual key=value is automatically handled by getoptlong.sh
In `run_greple()`, show values are passed to the md module:
```bash
for name in "${!show[@]}"; do
[[ $name == all ]] && continue
md_opts+=(--show "${name}=${show[$name]}")
done
```
The md module's `active()` function checks show flags and skips regex substitutions entirely for disabled fields.
### Emphasis Patterns (CommonMark)
Bold/italic/strike patterns use `$SKIP_CODE` as first alternative to skip code spans. Named captures `(?<m>...)` (markers) and `(?<t>...)` (content) with `\g{m}` backreference prevent mixing (`**...__`).
- `(?<![\\\w])` / `(?!\w)`: Word boundaries for `_`/`__` (prevents `foo_bar_baz`)
- `(?<!\*)` / `(?!\*)`: Distinguishes `*italic*` from `**bold**`
- `mark_color($type, $text)`: Uses `${type}_mark` if in colormap, falls back to `emphasis_mark`
- `/p` flag with `${^MATCH}`: Safe alternative to `$&` (used by `$SKIP_CODE`)
### OSC 8 Hyperlinks
Links are converted to OSC 8 terminal hyperlinks. Three types (in processing order): `image_link` (`[](url)`), `image` (``), `link` (`[text](url)`). Each is colored, wrapped in OSC 8, and protected. Disable with `config(osc8=0)`...
URL encoding: `uri_escape_utf8($url, "^\\x20-\\x7e")` â escapes only non-ASCII, preserving `:`, `/`, etc.
#### Code Span Pattern and `(*SKIP)(*FAIL)` Protection
- Invocation: `. getoptlong.sh OPTS "$@"`
## 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 (Bold/Italic)
Emphasis patterns do not span multiple lines. Multi-line bold or italic text is not supported.
`***bold italic***` and `___bold italic___` are supported as a combined pattern (processed before bold and italic, result is `protect()`ed). Other nested forms (e.g., `**bold _italic_**`) are not supported.
### Links
Link patterns do not span multiple lines. The link text and URL must be on the same line.
Link text matching uses `` (?:`[^`\n]*+`|\\.|[^`\\\n\]]++)+ `` to handle:
- `]` inside backtick-quoted text (e.g., `` [`init [CONFIGS...]`](#url) ``) â deviates from CommonMark spec (which terminates `]` even inside code spans) but matches GitHub rendering
- Backslash-escaped `\]` (e.g., `[foo\]bar](#url)`) â per CommonMark spec, `\]` does not terminate link text
By default, inline code and emphasis inside headings are not processed
(headings get uniform color). Links are always processed as OSC 8
hyperlinks. With `--heading-markup` (`--hm`), all inline formatting
is visible within headings via cumulative coloring. Specific steps
can be selected: `--hm=bold:italic` processes only bold and italic
inside headings.
Reference-style links (`[text][ref]` with `[ref]: url` elsewhere) are not supported.
### Indented Line Folding (TODO)
Lines starting with whitespace are not currently folded. Adding `^\h+.*\n` to the fold target would enable autoindent-aware wrapping, but the following issues must be resolved first:
1. **Pandoc non-pipe tables**: Simple tables, grid tables, and multiline tables use lines starting with spaces/dashes. These would be incorrectly folded. Pandoc table detection and exclusion is needed before enabling indented line folding.
1.05 2026-02-27T12:34:03Z
- Add --[no-]page-number option to show page number on border
- Update theme colors for light/dark themes
- Fix formfeed fillup in single-column mode with multiple files
- Require App::ansicolumn 1.5702
1.04 2026-02-26T02:55:15Z
- Add --config option for setting config parameters from command line
- Support color labels (h1, bold, etc.) as config params in md module
- Add file label theming (file, file_format theme keys)
- Add fatchap theme for decorative h1 heading borders
- Require App::ansicolumn 1.56
1.03 2026-02-24T16:19:41Z
- Add --[no-]trim option for table cell whitespace trimming (default: on)
- Require App::ansicolumn 1.55 for --table-remove, --item-format, --padding
1.02 2026-02-24T04:15:14Z
0.20 2026-02-19T11:57:33Z
- Migrate text folding to md module (foldlist config parameter)
- Rename nup_opts to pass_nup for consistency with pass_md
- Require App::Greple::md 0.9904
0.19 2026-02-19T04:49:23Z
- Add --colormap option to pass colormap specs to md module
- Extend --heading-markup to accept step names (e.g., bold:italic)
- Use greple --filter option, require App::Greple 10.04
- Require App::Greple::md 0.9902
0.18 2026-02-18T09:02:05Z
- Update deps: App::Greple::md 0.9901, App::Greple 10.03
- Update documentation for md module color improvements
0.17 2026-02-17T15:21:03Z
- Move CONFIGURATION section out of OPTIONS into its own top-level section
- Add dependencies table to CLAUDE.md
0.13 2026-02-07T04:10:31Z
- Add --[no-]rule option for Unicode table borders (â â ⤠⼠â)
- Remove unused ansi_color import from fix_table_script
0.12 2026-02-06T11:02:50Z
- Add backtick protection to bold/italic/strike patterns (avoid matching inside inline code)
- Add backslash escape handling to strike pattern
- Set LESS and LESSANSIENDCHARS defaults for pager support
0.11 2026-02-06T02:08:25Z
- Add -x/--trace option for shell trace mode (set -x)
- Support Pandoc-style list markers (#. #) 1)) in fold/autoindent
- Exclude tables from fold processing to preserve formatting
- Use File::Share for share directory discovery
- Consolidate all patterns into patterns_default array for centralized management
- Fix link patterns to not span multiple lines
- Refactor theme: dark theme inherits undefined keys from light theme
- Add LIMITATIONS section to documentation
- Require Text::ANSI::Fold 2.32 for OSC 8 hyperlink support
0.02 2026-01-18T02:51:12Z
- Replace --enable/--disable with --show option for field visibility control
- Rename underline to italic field, enable by default
- Support both *text* and _text_ for italic
- Support both **text** and __text__ for bold
- Add backslash escape handling for emphasis patterns
- Use word boundaries for underscore emphasis (CommonMark compliant)
- Add h6 color definition
- Add --color=always to greple for consistent output in non-terminal environments
- Require App::Greple::tee 1.04
- Add tee module execution tests
0.01 2026-01-17T16:39:57Z
- Initial release
- Multi-column Markdown viewer with syntax highlighting
- Support for headers, bold, strikethrough, inline code, code blocks,
HTML comments, tables, and list items
- Light/dark theme with automatic terminal detection
- Filter mode (-f) for highlight-only output
- --enable/--disable options for field control
- CommonMark-compliant code block detection (``` and ~~~)
Revision history for Perl extension App-Greple-md
0.9905 2026-02-20T05:32:13Z
- Introduce Step class for OO pipeline architecture
- Extract $CODE pattern as shared base for inline_code and $SKIP_CODE
- Use $SKIP_CODE with (*SKIP)(*FAIL) for emphasis code span protection
- Use /p flag with ${^MATCH} instead of $& in bold/italic/strike
- Add inline_code controllability via --show code_inline=
- Document --show controllable vs always-active elements
- Add tests for multi-backtick space stripping and code span protection
0.9904 2026-02-19T11:43:43Z
- Display inline code as `content´ with configurable tick markers
- Collapse multi-backtick delimiters to single pair, strip spaces
- Add tick_open/tick_close config parameters
- Prevent link/image patterns from matching inside code spans
- Add --fold option for text folding via ansifold (-Mtee)
- Add colorize/foldlist config flags for stage control
- Add code_tick label for independent inline code backtick coloring
- Simplify h5/h6 colors to use same base color as h4
- Change dark code_info color to L10 (gray, matching code_mark)
- Use define directives for fold exclude/match patterns
0.9902 2026-02-19T04:39:29Z
- Refactor colorize into pipeline architecture with build_pipeline()
- Extend --heading-markup to accept step names (e.g., bold:italic)
- Use greple --filter option, require App::Greple 10.04
- Fix deal_with option spec: use underscore for correct Config key matching
- Fix test failure on CPAN Testers: use Runner submodule to find greple
0.9901 2026-02-18T08:16:52Z
- Fix heading color preservation with cumulative coloring
- Restore protected regions before applying heading colors
- Reorder processing: emphasis before headings
- Prevent placeholder ESC+[ from matching as markdown link
- Improve color scheme for better heading compatibility
- bold: D (weight only), link/image: I (italic only)
- code_inline: add explicit foreground (L00/L23, L25/L05)
- code_info: use base color without hue rotation
- h1/h2: move E to end of spec for correct EL position
- Manage protect/restore and OSC 8 markers via variables
- Rewrite POD with categorized labels and module options
- Add short options: -m (mode), -B (base_color)
0.99 2026-02-17T14:44:56Z
- Initial release
--[no-]nup nup paged output (default: on)
--[no-]rule use Unicode rules for tables (default: on)
-V --cat-v visualize whitespace via cat-v
-w --width=# fold width (default: 80)
-t --theme=#[,#,...] color theme(s) (default: hashed,nomark)
-m --mode=# light or dark (default: light)
-B --base-color=# override base color of theme
(e.g., Ivory, #780043, (120,0,67))
--config=KEY=VALUE set config parameter (e.g., h1=RD, FILE=...)
--cm --colormap=L=SPEC override color for element (e.g., h1=RD)
--hm --heading-markup=# enable markup in headings (all/bold/...)
--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-]page-number page number on border (default: on)
--[no-]pager[=#] pager command
are available. Combine them with [nup(1)](https://metacpan.org/pod/App%3A%3Anup) for similar paged output
(e.g., `nup glow README.md`).
The pipeline combines [greple(1)](https://metacpan.org/pod/App%3A%3AGreple) for colorization and
[nup(1)](https://metacpan.org/pod/App%3A%3Anup) for multi-column paged output.
The following elements are highlighted. Elements marked with
`--show` can be individually disabled via the `--show` option.
Others are always processed for structural integrity.
--show bold (**bold**, __bold__)
--show italic (*italic*, _italic_)
--show strike (~~strike~~)
--show code_inline (`code`)
--show header, h1-h6 (# heading)
--show horizontal_rule (---, ***, ___)
--show blockquote (> quote)
always code_mark (``` or ~~~)
code_block
code_info
always comment (<!-- ... -->)
Set the fold width for text wrapping. Default is calculated from
`--pane-width` minus 5 (margin for borders and padding), which
gives 80 when pane-width is 85. Only effective when `--fold` is
enabled.
## Theme Options
**em·dee** supports color themes for customizing syntax highlighting.
Themes define colors for various Markdown elements (headers, code blocks,
bold text, etc.).
- **-t** _NAME_, **--theme**=_NAME_\[,_NAME_,...\]
Select color themes. The default is `hashed,nomark`, which
appends closing hashes to h3-h6 headers and hides markup
characters for a clean reading experience.
Themes specified with `--theme` are added to the default, not
replacing it. Multiple themes can be specified as comma-separated
names or by repeating the option. Each theme is applied in order
Built-in themes:
- `hashed` (default)
Append closing hashes to h3-h6 headers.
- `nomark` (default)
Hide emphasis markers (`**`, `*`, `__`, `_`, `~~`),
inline code backticks, and link brackets (`[`, `]`).
Content text keeps its formatting (bold, italic,
strikethrough, code, clickable links) but the surrounding
markers are not displayed.
- `warm`
Change base color to Coral.
- `fatchap`
Add decorative border lines (`#`) above and below h1 headings,
- **--config**=_KEY_=_VALUE_
Set config parameters from the command line. Parameters are
passed to the [App::Greple::md](https://metacpan.org/pod/App%3A%3AGreple%3A%3Amd) module via
[Getopt::EX::Config](https://metacpan.org/pod/Getopt%3A%3AEX%3A%3AConfig). Theme keys recognized by mdee (`base`,
`FILE`, `FILE_FORMAT`) are consumed locally and applied to both
light and dark themes; all other parameters are forwarded to the
md module.
Color labels (`h1`, `bold`, `italic`, etc.) are accepted as
config parameters and override default colors. The `${base}`
placeholder can be used and will be expanded at runtime.
mdee --config h1=RD # red h1
mdee --config bold=GD --config italic=YI # green bold, yellow italic
mdee --config h1='L25D/R;E' # custom h1 with background
mdee --config FILE='L25D/R;E' # red file label
mdee --config FILE_FORMAT=%s: # simple format
mdee --config hashed.h3=1 # enable h3 closing hashes
Theme keys consumed by mdee:
- `base` - Base color (equivalent to `--base-color`)
- `FILE` - Color spec for file label (default: `L25D/${base};E` for light, `L00D/${base};E` for dark). This is passed to greple's `FILE` label and cannot be set via `--cm`.
- `FILE_FORMAT` - Format string for file label passed to greple's `--format FILE=` (default: `\n %s\n\n`). Cannot be set via `--cm`.
`table_trim`, `rule`, `osc8`, `tick_open`, `tick_close`, etc.
## Highlight Options
- **--colormap**=_LABEL_=_SPEC_, **--cm**=_LABEL_=_SPEC_
Override the color for a specific element. _LABEL_ is one of the
color labels listed below. _SPEC_ follows
[Term::ANSIColor::Concise](https://metacpan.org/pod/Term%3A%3AANSIColor%3A%3AConcise) format.
--colormap h1=RD # red bold headings
--colormap code_block=/L20;E # dark background for code blocks
--colormap bold='${base}D' # base color bold
Available labels:
h1 - h6 Heading levels 1 through 6
bold Bold (**text** or __text__)
italic Italic (*text* or _text_)
strike Strikethrough (~~text~~)
emphasis_mark Emphasis markers (**, *, __, _, ~~)
bold_mark Bold markers only (overrides emphasis_mark)
italic_mark Italic markers only (overrides emphasis_mark)
strike_mark Strike markers only (overrides emphasis_mark)
link Inline links [text](url)
image Images 
image_link Image links [](url)
link_mark Link/image brackets (overrides emphasis_mark)
blockquote Blockquote marker (>)
horizontal_rule Horizontal rules (---, ***, ___)
comment HTML comments (<!-- ... -->)
code_mark Code block delimiters (``` and ~~~)
code_inline Inline code body
This option can be specified multiple times. Color labels can
also be set via `--config`. When both are
specified, `--cm` takes priority over `--config`.
- **--heading-markup**=_STEPS_, **--hm** _STEPS_
Control inline markup processing inside headings. By default,
headings are rendered with uniform heading color without processing
bold, italic, strikethrough, or inline code inside them. Links
are always processed as OSC 8 hyperlinks regardless of this option.
Use `all` or `1` to enable all inline formatting within headings
using cumulative coloring. To select specific steps, list them
separated by colons.
Available steps: `inline_code`, `horizontal_rules`, `bold`,
`italic`, `strike`.
mdee --hm all file.md # all markup
mdee --hm bold file.md # bold only
mdee --hm bold:italic file.md # bold and italic
- **--show**=_FIELD_\[=_VALUE_\],...
Control field visibility for highlighting. Empty value or `0` disables
the field; any other value (including `1`) enables it.
--show italic # enable italic
--show bold=0 # disable bold
--show all # enable all fields
--show all= --show bold # disable all, then enable only bold
Multiple fields can be specified with commas or by repeating the option.
The special field `all` affects all fields and is processed first.
See ["DESCRIPTION"](#description) for the list of available fields and
elements that are always active.
All controllable fields are enabled by default.
## Layout Options (passed to nup)
mechanism as theme files:
# Change base color for both modes
theme_light[base]='<DarkCyan>=y25'
theme_dark[base]='<DarkCyan>=y80'
# Enable md module features
md_config+=(hashed.h3=1 hashed.h4=1 hashed.h5=1 hashed.h6=1)
Changing the base color automatically affects all derived colors
(h1, h2, bold, etc.) because the md module expands `${base}`
references.
Use `-d` to dump current theme values in sourceable format.
**Color specification format**
Color specifications use [Term::ANSIColor::Concise](https://metacpan.org/pod/Term%3A%3AANSIColor%3A%3AConcise) format.
The `FG/BG` notation specifies foreground and background colors
(e.g., `L25D/${base};E` means gray foreground on base-colored background).
The `${base}` string is expanded to the base color value after loading.
#### Syntax Highlighting
The first stage uses [greple(1)](https://metacpan.org/pod/App%3A%3AGreple) with the `--filter`
option, which sets `--all --need=0 --exit=0` to run as a filter
without requiring pattern arguments. Syntax highlighting is handled
entirely by the `-Mmd` module.
Supported Markdown elements:
- Headers (`# h1` through `###### h6`)
- Bold text (`**bold**` or `__bold__`)
- Italic text (`*italic*` or `_italic_`)
- Inline code (`` `code` ``)
- Code blocks (fenced with ```` ``` ```` or `~~~`)
- HTML comments (`<!-- comment -->`)
Code block detection follows the CommonMark specification:
- Opening fence: 0-3 spaces indentation, then 3+ backticks or tildes
- Closing fence: 0-3 spaces indentation, same character, same or more count
- Backticks and tildes cannot be mixed (```` ``` ```` must close with ```` ``` ````)
to the md module via `md_config[]`:
# theme/warm.sh â change base color
theme_light[base]='<Coral>=y25'
theme_dark[base]='<Coral>=y80'
# theme/hashed.sh â enable closing hashes
md_config+=(hashed.h3=1 hashed.h4=1 hashed.h5=1 hashed.h6=1)
The `md_config[]` entries are passed as config parameters to the
[App::Greple::md](https://metacpan.org/pod/App%3A%3AGreple%3A%3Amd) module. Color labels (`h1`, `bold`, etc.)
can also be set as config parameters:
# theme file or config.sh â override color label
md_config+=(h1='L25D/R;E' bold=RD)
#### Base Color Expansion
The `${base}` placeholder in color values is expanded by the md
module. The base color is determined by `--base-color` option
(default: RoyalBlue) with automatic luminance adjustment based on
mode (`=y25` for light, `=y80` for dark).
### Color Specifications
# 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.
`***bold italic***` and `___bold italic___` are supported.
Other nested forms (e.g., `**bold _italic_**`) are not.
## Links
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.
## Table Alignment
Column alignment assumes that cell content in the source is not
pre-padded. If the source table is already formatted with aligned
spacing (e.g., `| c |`), the embedded spaces become part of
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
# HTML comments: protect from further processing
s/(^<!--(?![->]).*?-->)/protect(main::color('comment', $1))/gme;
# (links, headings, emphasis in later tasks)
$_ = restore($_);
$_;
}
```
Note: The `protect/restore` mechanism ensures inline code like `` `**bold**` `` is not processed as bold. NUL bytes are safe as placeholders since they don't appear in normal text.
**Step 2: Add colors to __DATA__**
Add to `option default`:
```
--cm inline_code=L15/L23 \
--cm comment=CM+r60 \
```
Note: `comment` color in mdee is `${base}+r60` (base color + reddish). For standalone default, use a hardcoded approximation. mdee will override via `--cm`.
**Step 3: Test inline code protection**
Create test: a line with `` `**not bold**` `` should show inline code color, NOT bold.
Run: `echo '`**not bold**` but **this is bold**' | greple -Mmd -Ilib 2>/dev/null`
Expected: First part colored as inline code, second part not yet colored (bold not implemented yet)
**Step 4: Commit**
```bash
git add lib/App/Greple/md.pm
git commit -m "feat: inline code and comment colorization with protection"
```
---
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
```bash
git add lib/App/Greple/md.pm
git commit -m "feat: heading colorization with cumulative coloring over links"
```
---
### Task 6: Emphasis Patterns (Bold, Italic, Strike)
Add bold, italic, and strikethrough patterns. These are processed after headings.
**Files:**
- Modify: `lib/App/Greple/md.pm`
**Step 1: Add emphasis patterns to colorize()**
After heading processing:
```perl
# Bold: **text** and __text__
s/(?<![\\`])\*\*.*?(?<!\\)\*\*/main::color('bold', $&)/ge;
s/(?<![\\`\w])__.*?(?<!\\)__(?!\w)/main::color('bold', $&)/ge;
# Italic: _text_ and *text*
s/(?<![\\`\w])_(?:(?!_).)+(?<!\\)_(?!\w)/main::color('italic', $&)/ge;
s/(?<![\\`\*])\*(?:(?!\*).)+(?<!\\)\*(?!\*)/main::color('italic', $&)/ge;
# Strikethrough: ~~text~~
s/(?<![\\`])~~.+?(?<!\\)~~/main::color('strike', $&)/ge;
```
Note: These patterns are identical to current mdee patterns. The `(?<![\\`])` lookbehind provides backslash and backtick protection. The protect/restore mechanism handles inline code, so backtick protection in the regex is belt-and-suspenders safety.
**Step 2: Add emphasis colors to __DATA__**
```
--cm bold=<RoyalBlue>=y25;D \
--cm italic=I \
--cm strike=X \
```
**Step 3: Test emphasis inside headings**
Run:
```bash
printf '## This is **bold** in heading\n**standalone bold**\n`**not bold**`\n' \
| greple -Mmd -Ilib 2>/dev/null | cat -v
```
Expected: Bold inside heading gets both heading and bold styling. Inline code content is not bolded.
**Step 4: Commit**
```bash
git add lib/App/Greple/md.pm
git commit -m "feat: bold, italic, and strikethrough patterns"
```
---
### Task 7: Blockquote and Horizontal Rule
Add remaining elements.
**Files:**
- Modify: `lib/App/Greple/md.pm`
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
**Step 1: Add blockquote and horizontal rule to colorize()**
```perl
# Blockquote marker: > at start of line
s/^(>+\h?)(.*)$/main::color('blockquote', $1) . $2/me;
# Horizontal rule: 3+ of -, *, or _ (possibly with spaces)
s/^([ ]{0,3}(?:[-*_][ ]*){3,})$/main::color('horizontal_rule', $1)/me;
```
Note: For blockquotes, only the `>` marker is colored (not the content), matching typical Markdown viewer behavior. Content may contain other elements (bold, links) which are already processed.
**Step 2: Add colors to __DATA__**
```
--cm blockquote=<RoyalBlue>=y25;D \
--cm horizontal_rule=L15 \
```
**Step 3: Test**
Run: `printf '> quoted **bold** text\n---\n' | greple -Mmd -Ilib 2>/dev/null | cat -v`
Expected: `>` marker colored, bold within quote colored, `---` colored as horizontal rule.
**Step 4: Commit**
```bash
git add lib/App/Greple/md.pm
git commit -m "feat: blockquote and horizontal rule patterns"
```
---
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
--cm comment=L15 \
--cm link=CU \
--cm image=CU \
--cm image_link=CU \
--cm h1=L25DE/<RoyalBlue>=y25 \
--cm h2=L25DE/<RoyalBlue>=y25+y20 \
--cm h3=L25DN/<RoyalBlue>=y25+y30 \
--cm h4=<RoyalBlue>=y25;UD \
--cm h5=<RoyalBlue>=y25;U \
--cm h6=<RoyalBlue>=y25 \
--cm bold=<RoyalBlue>=y25;D \
--cm italic=I \
--cm strike=X \
--cm blockquote=<RoyalBlue>=y25;D \
--cm horizontal_rule=L15 \
-E '(?!)' \
--print &__PACKAGE__::colorize
option --md-dark \
--cm code_fence=L10 \
--cm code_lang=L12 \
--cm code_body=/L05;E \
--cm inline_code=L12/L05 \
--cm h1=L00DE/<RoyalBlue>=y80 \
--cm h2=L00DE/<RoyalBlue>=y80-y15 \
--cm h3=L00DN/<RoyalBlue>=y80-y25 \
--cm h4=<RoyalBlue>=y80;UD \
--cm h5=<RoyalBlue>=y80;U \
--cm h6=<RoyalBlue>=y80 \
--cm bold=<RoyalBlue>=y80;D \
--cm blockquote=<RoyalBlue>=y80;D
```
Note: Dark mode only overrides colors that differ from light. Labels like `italic`, `strike`, `link`, `image`, `image_link`, `horizontal_rule` are the same in both modes, so they only appear in `option default`.
**Step 2: Test light mode**
Run: `greple -Mmd -Ilib /Users/utashiro/Git/tecolicom/App-mdee/t/test.md 2>/dev/null | head -30`
Expected: Colored output with light mode colors
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
- Create: `t/01_colorize.t`
- Create: `t/test.md` (small test markdown file)
**Step 1: Create test markdown file**
```markdown
# Heading 1
## Heading 2 with [link](https://example.com)
Normal text with **bold** and *italic*.
`inline code with **not bold**`
```bash
code block
```
> blockquote
---
~~strikethrough~~
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
my $test_md = 't/test.md';
plan skip_all => "greple not found" unless `which greple`;
my $out = `greple -Mmd -Ilib $test_md 2>/dev/null`;
ok(length($out) > 0, "greple -Mmd produces output");
# Output should contain ANSI escape sequences (colored)
like($out, qr/\e\[/, "output contains ANSI color sequences");
# Test that code blocks are protected
my $code_test = '`**not bold**`';
my $code_out = `echo '$code_test' | greple -Mmd -Ilib 2>/dev/null`;
# The **not bold** should NOT have bold ANSI codes applied separately
# Test dark mode
my $dark_out = `greple '-Mmd::config(mode=dark)' -Ilib $test_md 2>/dev/null`;
ok(length($dark_out) > 0, "dark mode produces output");
done_testing;
```
**Step 3: Run tests**
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
**Files:**
- No new files
**Step 1: Full visual test with light mode**
Run: `greple -Mmd -Ilib /Users/utashiro/Git/tecolicom/App-mdee/t/test.md 2>/dev/null | less -R`
Verify:
- [ ] Headers h1-h6 are colored with decreasing prominence
- [ ] Bold text is bold with base color
- [ ] Italic text is italic
- [ ] Strikethrough has strikethrough styling
- [ ] Inline code has background color
- [ ] Code blocks have background color, fence and language colored differently
- [ ] Links show as `[text]` with clickable OSC 8 (on supported terminals)
- [ ] Images show as `![alt]` with clickable OSC 8
- [ ] Blockquote `>` marker is colored
- [ ] Horizontal rules are colored
- [ ] HTML comments are colored
- [ ] Links inside headings have both heading color AND link color
- [ ] Bold inside inline code is NOT processed as bold
**Step 2: Full visual test with dark mode**
Run: `greple '-Mmd::config(mode=dark)' -Ilib /Users/utashiro/Git/tecolicom/App-mdee/t/test.md 2>/dev/null | less -R`
Verify same elements with dark-appropriate colors.
**Step 3: Test --cm override**
Run: `greple -Mmd --cm h1=RD -Ilib /Users/utashiro/Git/tecolicom/App-mdee/t/test.md 2>/dev/null | head -5`
Expected: h1 heading should be red+bold instead of default blue
**Step 4: Fix any issues found and commit**
```bash
git add -A
git commit -m "fix: address integration test findings"
```
---
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
# Nup mode
./script/mdee t/test.md
# Dark mode
./script/mdee --mode=dark -f t/test.md
# Theme override
./script/mdee --no-theme -f t/test.md
# Show field visibility
./script/mdee --show all= --show bold -f t/test.md
# List themes
./script/mdee --list-themes
```
**Step 3: Commit**
```bash
cd /Users/utashiro/Git/tecolicom/App-mdee
git add script/mdee
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
---
## Key Design Decisions
### Color Label Mapping (mdee â module)
| mdee theme key | Module --cm label(s) | Notes |
|---|---|---|
| `h1`-`h6` | `h1`-`h6` | Direct mapping |
| `bold` | `bold` | Direct |
| `italic` | `italic` | Direct |
| `strike` | `strike` | Direct |
| `comment` | `comment` | Direct |
| `code_block` | `code_fence`, `code_lang`, `code_body` | Split into 3 labels |
| `inline_code` | `inline_code` | Module uses single label |
| `link` | `link` | mdee's `sub{...}` func spec â module's simple color |
| `image` | `image` | Same as link |
| `image_link` | `image_link` | Same as link |
### Protection Mechanism
docs/plans/2026-02-16-greple-md-module-design.md view on Meta::CPAN
### `__DATA__` Section
Static defaults defined in the module's `__DATA__` section:
```
option default \
--all --need=0 \
--cm h1=... --cm h2=... --cm h3=... \
--cm h4=... --cm h5=... --cm h6=... \
--cm bold=... --cm italic=... --cm strike=... \
--cm code_block=... --cm inline_code=... \
--cm link=... --cm image=... --cm image_link=... \
--cm blockquote=... --cm horizontal_rule=... \
--cm comment=... \
-E '(?!)' \
--print &__PACKAGE__::colorize
```
- `--cm LABEL=spec`: Default colors for each element (light mode defaults)
- `-E '(?!)'`: Dummy pattern (never matches, but required for greple to invoke the print function)
docs/plans/2026-02-16-greple-md-module-design.md view on Meta::CPAN
4. Applies h2 color to the entire line via `main::color('h2', ...)`
5. Result: heading color wraps the line, link color and OSC 8 are preserved inside
### Pattern Definitions
Patterns are defined as Perl regexes within the `colorize()` function (or as module-level constants). They correspond to current mdee patterns:
| Label | Description | Current mdee source |
|-------|-------------|-------------------|
| h1-h6 | Headers | `patterns_default` h1-h6 |
| bold | `**text**`, `__text__` | `patterns_default` bold |
| italic | `*text*`, `_text_` | `patterns_default` italic |
| strike | `~~text~~` | `patterns_default` strike |
| code_block | Fenced code blocks | `patterns_default` code_block |
| inline_code | `` `code` `` | `patterns_default` inline_code |
| link | `[text](url)` | `patterns_default` link |
| image | `` | `patterns_default` image |
| image_link | `[](url)` | `patterns_default` image_link |
| blockquote | `> text` | `patterns_default` blockquote |
| horizontal_rule | `---`, `***`, `___` | `patterns_default` horizontal_rule |
| comment | `<!-- ... -->` | `patterns_default` comment |
lib/App/Greple/md.pm view on Meta::CPAN
greple -Mmd --foldlist -- file.md
greple -Mmd -- --fold file.md
=head1 DESCRIPTION
B<App::Greple::md> is a L<greple|App::Greple> module for viewing
Markdown files in the terminal with syntax highlighting.
It colorizes headings, bold, italic, strikethrough, inline code,
fenced code blocks, HTML comments, blockquotes, horizontal rules,
links, and images. Tables are formatted with aligned columns and
optional Unicode box-drawing borders. Long lines in list items can
be folded with proper indentation. Links become clickable via OSC 8
terminal hyperlinks in supported terminals.
Nested elements are handled with cumulative coloring: for example,
a link inside a heading retains both its link color and the heading
background color.
lib/App/Greple/md.pm view on Meta::CPAN
greple -Mmd --mode=dark --cm h1=RD -- file.md
=head2 B<-m> I<MODE>, B<--mode>=I<MODE>
Set color mode. Available modes are C<light> (default) and C<dark>.
greple -Mmd -m dark -- file.md
=head2 B<-B> I<COLOR>, B<--base-color>=I<COLOR>
Override the base color used for headings, bold, links, and other
elements. Accepts a named color (e.g., C<Crimson>, C<DarkCyan>) or a
L<Term::ANSIColor::Concise> color spec.
greple -Mmd -B Crimson -- file.md
=head2 B<--[no-]colorize>
Enable or disable syntax highlighting. Enabled by default.
When disabled, no color is applied to Markdown elements.
lib/App/Greple/md.pm view on Meta::CPAN
greple -Mmd --no-rule -- file.md
=head2 B<--colormap> I<LABEL>=I<SPEC>, B<--cm> I<LABEL>=I<SPEC>
Override the color for a specific element. I<LABEL> is one of
the color labels listed in L</COLOR LABELS>. I<SPEC> follows
L<Term::ANSIColor::Concise> format and supports C<sub{...}>
function specs via L<Getopt::EX::Colormap>.
greple -Mmd --cm h1=RD -- file.md
greple -Mmd --cm bold='${base}D' -- file.md
=head2 B<--heading-markup>[=I<STEPS>], B<--hm>[=I<STEPS>]
Control inline markup processing inside headings. By default,
headings are rendered with uniform heading color without processing
bold, italic, strikethrough, or inline code inside them. Links
are always processed as OSC 8 hyperlinks regardless of this option.
Without an argument, all inline formatting becomes visible within
headings using cumulative coloring. With an argument, only the
specified steps are processed inside headings. Steps are separated
by colons.
Available steps: C<inline_code>, C<horizontal_rules>, C<bold>,
C<italic>, C<strike>.
greple -Mmd --hm -- file.md # all markup
greple -Mmd --hm=bold -- file.md # bold only
greple -Mmd --hm=bold:italic -- file.md # bold and italic
=head2 B<--hashed> I<LEVEL>=I<VALUE>
Append closing hashes to headings. For example, C<### Title>
becomes C<### Title ###>. Set per heading level:
greple -Mmd --hashed h3=1 --hashed h4=1 -- file.md
=head2 B<--show> I<LABEL>[=I<VALUE>]
Control which elements are highlighted. This is useful for
focusing on specific elements or disabling unwanted highlighting.
greple -Mmd --show bold=0 -- file.md # disable bold
greple -Mmd --show all= --show h1 -- file.md # only h1
C<--show LABEL=0> or C<--show LABEL=> disables the label.
C<--show LABEL> or C<--show LABEL=1> enables it.
C<all> is a special key that sets all labels at once.
Controllable labels: C<bold>, C<italic>, C<strike>, C<code_inline>,
C<header> (h1-h6), C<horizontal_rule>, C<blockquote>.
The following elements are always processed and cannot be disabled:
C<comment>, C<code_block> (C<code_mark>, C<code_info>),
C<link>, C<image>, C<image_link>.
Use C<--cm LABEL=> to remove their color without disabling processing.
=head1 CONFIGURATION
Module parameters can also be set using the C<config()> function
lib/App/Greple/md.pm view on Meta::CPAN
h1 L25D/${base};E L00D/${base};E
h2 L25D/${base}+y20;E L00D/${base}-y15;E
h3 L25DN/${base}+y30 L00DN/${base}-y25
h4 ${base}UD ${base}UD
h5 ${base}U ${base}U
h6 ${base} ${base}
=head2 Inline Formatting
LABEL LIGHT DARK
bold D
italic I
strike X
emphasis_mark L18 L10
bold_mark - -
italic_mark - -
strike_mark - -
Emphasis markers (C<**>, C<*>, C<__>, C<_>, C<~~>) are colored with
C<emphasis_mark>, separately from the content text. C<bold_mark>,
C<italic_mark>, C<strike_mark> are undefined by default and fall back
to C<emphasis_mark>. Define them via C<--cm> to override per type:
greple -Mmd --cm emphasis_mark=R -- file.md # all markers red
greple -Mmd --cm bold_mark=G -- file.md # bold markers green
=head2 Code
LABEL LIGHT DARK
code_mark L20 L10
code_tick L15/L23 L15/L05
code_info ${base_name}=y70 L10
code_block /L23;E /L05;E
code_inline L00/L23 L25/L05
lib/App/Greple/md.pm view on Meta::CPAN
=cut
use URI::Escape;
use Getopt::EX::Config;
use Getopt::EX::Colormap;
my @color_labels = qw(
code_mark code_tick code_info code_block code_inline
comment link image image_link
h1 h2 h3 h4 h5 h6
bold italic strike emphasis_mark
bold_mark italic_mark strike_mark link_mark
blockquote horizontal_rule
);
my $config = Getopt::EX::Config->new(
mode => '', # light / dark
osc8 => 1, # OSC 8 hyperlinks
base_color => '', # override base color
colorize => 1, # syntax highlighting
foldlist => 0, # text folding
foldwidth => 80, # fold width
lib/App/Greple/md.pm view on Meta::CPAN
comment => '${base}+r60',
link => 'I',
image => 'I',
image_link => 'I',
h1 => 'L25D/${base};E',
h2 => 'L25D/${base}+y20;E',
h3 => 'L25DN/${base}+y30',
h4 => '${base}UD',
h5 => '${base}U',
h6 => '${base}',
bold => 'D',
italic => 'I',
strike => 'X',
emphasis_mark => 'L18',
blockquote => '${base}D',
horizontal_rule => 'L15',
);
my %dark_overrides = (
code_mark => 'L10',
code_tick => 'L15/L05',
lib/App/Greple/md.pm view on Meta::CPAN
if $hashed->{"h$n"} && $line !~ /\#$/;
protect(md_color("h$n", restore($line)));
}mge;
}
}),
horizontal_rules => Step(horizontal_rule => sub {
s/^([ ]{0,3}(?:[-*_][ ]*){3,})$/protect(md_color('horizontal_rule', $1))/mge;
}),
bold_italic => Step(bold => sub {
s{$SKIP_CODE|(?<!\\)(?<m>\*\*\*)(?<t>.*?)(?<!\\)\g{m}}{
protect(mark_color('bold', $+{m}) . md_color('bold', md_color('italic', $+{t})) . mark_color('bold', $+{m}))
}gep;
s{$SKIP_CODE|(?<![\\\w])(?<m>___)(?<t>.*?)(?<!\\)\g{m}(?!\w)}{
protect(mark_color('bold', $+{m}) . md_color('bold', md_color('italic', $+{t})) . mark_color('bold', $+{m}))
}gep;
}),
bold => Step(bold => sub {
s{$SKIP_CODE|(?<!\\)(?<m>\*\*)(?<t>.*?)(?<!\\)\g{m}}{
mark_color('bold', $+{m}) . md_color('bold', $+{t}) . mark_color('bold', $+{m})
}gep;
s{$SKIP_CODE|(?<![\\\w])(?<m>__)(?<t>.*?)(?<!\\)\g{m}(?!\w)}{
mark_color('bold', $+{m}) . md_color('bold', $+{t}) . mark_color('bold', $+{m})
}gep;
}),
italic => Step(italic => sub {
s{$SKIP_CODE|(?<![\\\w])(?<m>_)(?<t>(?:(?!_).)+)(?<!\\)\g{m}(?!\w)}{
mark_color('italic', $+{m}) . md_color('italic', $+{t}) . mark_color('italic', $+{m})
}gep;
s{$SKIP_CODE|(?<![\\*])(?<m>\*)(?<t>(?:(?!\*).)+)(?<!\\)\g{m}(?!\*)}{
mark_color('italic', $+{m}) . md_color('italic', $+{t}) . mark_color('italic', $+{m})
}gep;
lib/App/Greple/md.pm view on Meta::CPAN
);
#
# Pipeline configuration
#
# Always before headings (protection + links)
my @protect_steps = qw(code_blocks comments image_links images links);
# Inline steps controlled by heading_markup
my @inline_steps = qw(inline_code horizontal_rules bold_italic bold italic strike);
# Always last
my @final_steps = qw(blockquotes);
sub build_pipeline {
my $hm = $config->{heading_markup};
# heading_markup disabled: headings before all inline steps
if (!$hm) {
return (@protect_steps, 'headings', @inline_steps, @final_steps);
}
# "all" or "1": all inline steps before headings
my %before;
if ($hm eq '1' || $hm =~ /^all$/i) {
%before = map { $_ => 1 } @inline_steps;
} else {
# "bold:italic" â collect word tokens, filter to valid inline steps
my %valid = map { $_ => 1 } @inline_steps;
%before = map { $_ => 1 } grep { $valid{$_} } ($hm =~ /(\w+)/g);
}
my @before_h = grep { $before{$_} } @inline_steps;
my @after_h = grep { !$before{$_} } @inline_steps;
return (@protect_steps, @before_h, 'headings', @after_h, @final_steps);
}
lib/App/mdee.pm view on Meta::CPAN
--[no-]nup nup paged output (default: on)
--[no-]rule use Unicode rules for tables (default: on)
-V --cat-v visualize whitespace via cat-v
-w --width=# fold width (default: 80)
-t --theme=#[,#,...] color theme(s) (default: hashed,nomark)
-m --mode=# light or dark (default: light)
-B --base-color=# override base color of theme
(e.g., Ivory, #780043, (120,0,67))
--config=KEY=VALUE set config parameter (e.g., h1=RD, FILE=...)
--cm --colormap=L=SPEC override color for element (e.g., h1=RD)
--hm --heading-markup=# enable markup in headings (all/bold/...)
--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-]page-number page number on border (default: on)
--[no-]pager[=#] pager command
lib/App/mdee.pm view on Meta::CPAN
are available. Combine them with L<nup(1)|App::nup> for similar paged output
(e.g., C<nup glow README.md>).
The pipeline combines L<greple(1)|App::Greple> for colorization and
L<nup(1)|App::nup> for multi-column paged output.
The following elements are highlighted. Elements marked with
C<--show> can be individually disabled via the C<--show> option.
Others are always processed for structural integrity.
--show bold (**bold**, __bold__)
--show italic (*italic*, _italic_)
--show strike (~~strike~~)
--show code_inline (`code`)
--show header, h1-h6 (# heading)
--show horizontal_rule (---, ***, ___)
--show blockquote (> quote)
always code_mark (``` or ~~~)
code_block
code_info
always comment (<!-- ... -->)
lib/App/mdee.pm view on Meta::CPAN
C<--pane-width> minus 5 (margin for borders and padding), which
gives 80 when pane-width is 85. Only effective when C<--fold> is
enabled.
=back
=head2 Theme Options
B<em·dee> supports color themes for customizing syntax highlighting.
Themes define colors for various Markdown elements (headers, code blocks,
bold text, etc.).
=over 4
=item B<-t> I<NAME>, B<--theme>=I<NAME>[,I<NAME>,...]
Select color themes. The default is C<hashed,nomark>, which
appends closing hashes to h3-h6 headers and hides markup
characters for a clean reading experience.
Themes specified with C<--theme> are added to the default, not
lib/App/mdee.pm view on Meta::CPAN
=over 4
=item C<hashed> (default)
Append closing hashes to h3-h6 headers.
=item C<nomark> (default)
Hide emphasis markers (C<**>, C<*>, C<__>, C<_>, C<~~>),
inline code backticks, and link brackets (C<[>, C<]>).
Content text keeps its formatting (bold, italic,
strikethrough, code, clickable links) but the surrounding
markers are not displayed.
=item C<warm>
Change base color to Coral.
=item C<fatchap>
Add decorative border lines (C<#>) above and below h1 headings,
lib/App/mdee.pm view on Meta::CPAN
=item B<--config>=I<KEY>=I<VALUE>
Set config parameters from the command line. Parameters are
passed to the L<App::Greple::md> module via
L<Getopt::EX::Config>. Theme keys recognized by mdee (C<base>,
C<FILE>, C<FILE_FORMAT>) are consumed locally and applied to both
light and dark themes; all other parameters are forwarded to the
md module.
Color labels (C<h1>, C<bold>, C<italic>, etc.) are accepted as
config parameters and override default colors. The C<${base}>
placeholder can be used and will be expanded at runtime.
mdee --config h1=RD # red h1
mdee --config bold=GD --config italic=YI # green bold, yellow italic
mdee --config h1='L25D/R;E' # custom h1 with background
mdee --config FILE='L25D/R;E' # red file label
mdee --config FILE_FORMAT=%s: # simple format
mdee --config hashed.h3=1 # enable h3 closing hashes
Theme keys consumed by mdee:
=over 4
=item C<base> - Base color (equivalent to C<--base-color>)
lib/App/mdee.pm view on Meta::CPAN
=head2 Highlight Options
=over 4
=item B<--colormap>=I<LABEL>=I<SPEC>, B<--cm>=I<LABEL>=I<SPEC>
Override the color for a specific element. I<LABEL> is one of the
color labels listed below. I<SPEC> follows
L<Term::ANSIColor::Concise> format.
--colormap h1=RD # red bold headings
--colormap code_block=/L20;E # dark background for code blocks
--colormap bold='${base}D' # base color bold
Available labels:
h1 - h6 Heading levels 1 through 6
bold Bold (**text** or __text__)
italic Italic (*text* or _text_)
strike Strikethrough (~~text~~)
emphasis_mark Emphasis markers (**, *, __, _, ~~)
bold_mark Bold markers only (overrides emphasis_mark)
italic_mark Italic markers only (overrides emphasis_mark)
strike_mark Strike markers only (overrides emphasis_mark)
link Inline links [text](url)
image Images 
image_link Image links [](url)
link_mark Link/image brackets (overrides emphasis_mark)
blockquote Blockquote marker (>)
horizontal_rule Horizontal rules (---, ***, ___)
comment HTML comments (<!-- ... -->)
code_mark Code block delimiters (``` and ~~~)
lib/App/mdee.pm view on Meta::CPAN
code_inline Inline code body
This option can be specified multiple times. Color labels can
also be set via C<--config>. When both are
specified, C<--cm> takes priority over C<--config>.
=item B<--heading-markup>=I<STEPS>, B<--hm> I<STEPS>
Control inline markup processing inside headings. By default,
headings are rendered with uniform heading color without processing
bold, italic, strikethrough, or inline code inside them. Links
are always processed as OSC 8 hyperlinks regardless of this option.
Use C<all> or C<1> to enable all inline formatting within headings
using cumulative coloring. To select specific steps, list them
separated by colons.
Available steps: C<inline_code>, C<horizontal_rules>, C<bold>,
C<italic>, C<strike>.
mdee --hm all file.md # all markup
mdee --hm bold file.md # bold only
mdee --hm bold:italic file.md # bold and italic
=item B<--show>=I<FIELD>[=I<VALUE>],...
Control field visibility for highlighting. Empty value or C<0> disables
the field; any other value (including C<1>) enables it.
--show italic # enable italic
--show bold=0 # disable bold
--show all # enable all fields
--show all= --show bold # disable all, then enable only bold
Multiple fields can be specified with commas or by repeating the option.
The special field C<all> affects all fields and is processed first.
See L</DESCRIPTION> for the list of available fields and
elements that are always active.
All controllable fields are enabled by default.
=back
lib/App/mdee.pm view on Meta::CPAN
mechanism as theme files:
# Change base color for both modes
theme_light[base]='<DarkCyan>=y25'
theme_dark[base]='<DarkCyan>=y80'
# Enable md module features
md_config+=(hashed.h3=1 hashed.h4=1 hashed.h5=1 hashed.h6=1)
Changing the base color automatically affects all derived colors
(h1, h2, bold, etc.) because the md module expands C<${base}>
references.
Use C<-d> to dump current theme values in sourceable format.
B<Color specification format>
Color specifications use L<Term::ANSIColor::Concise> format.
The C<FG/BG> notation specifies foreground and background colors
(e.g., C<L25D/${base};E> means gray foreground on base-colored background).
The C<${base}> string is expanded to the base color value after loading.
lib/App/mdee.pm view on Meta::CPAN
option, which sets C<--all --need=0 --exit=0> to run as a filter
without requiring pattern arguments. Syntax highlighting is handled
entirely by the C<-Mmd> module.
Supported Markdown elements:
=over 4
=item * Headers (C<# h1> through C<###### h6>)
=item * Bold text (C<**bold**> or C<__bold__>)
=item * Italic text (C<*italic*> or C<_italic_>)
=item * Inline code (C<`code`>)
=item * Code blocks (fenced with C<```> or C<~~~>)
=item * HTML comments (C<< <!-- comment --> >>)
=back
lib/App/mdee.pm view on Meta::CPAN
to the md module via C<md_config[]>:
# theme/warm.sh â change base color
theme_light[base]='<Coral>=y25'
theme_dark[base]='<Coral>=y80'
# theme/hashed.sh â enable closing hashes
md_config+=(hashed.h3=1 hashed.h4=1 hashed.h5=1 hashed.h6=1)
The C<md_config[]> entries are passed as config parameters to the
L<App::Greple::md> module. Color labels (C<h1>, C<bold>, etc.)
can also be set as config parameters:
# theme file or config.sh â override color label
md_config+=(h1='L25D/R;E' bold=RD)
=head4 Base Color Expansion
The C<${base}> placeholder in color values is expanded by the md
module. The base color is determined by C<--base-color> option
(default: RoyalBlue) with automatic luminance adjustment based on
mode (C<=y25> for light, C<=y80> for dark).
=head3 Color Specifications
lib/App/mdee.pm view on Meta::CPAN
=head1 LIMITATIONS
=head2 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., C<< `<!-->` >>).
=head2 Emphasis
Emphasis patterns (bold and italic) do not span multiple lines.
Multi-line emphasis text is not supported.
C<***bold italic***> and C<___bold italic___> are supported.
Other nested forms (e.g., C<< **bold _italic_** >>) are not.
=head2 Links
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 (C<[text][ref]> with C<[ref]: url> elsewhere)
are not supported.
=head2 Table Alignment
Column alignment assumes that cell content in the source is not
pre-padded. If the source table is already formatted with aligned
spacing (e.g., C<| c |>), the embedded spaces become part of
script/mdee view on Meta::CPAN
--[no-]nup nup paged output (default: on)
--[no-]rule use Unicode rules for tables (default: on)
-V --cat-v visualize whitespace via cat-v
-w --width=# fold width (default: 80)
-t --theme=#[,#,...] color theme(s) (default: hashed,nomark)
-m --mode=# light or dark (default: light)
-B --base-color=# override base color of theme
(e.g., Ivory, #780043, (120,0,67))
--config=KEY=VALUE set config parameter (e.g., h1=RD, FILE=...)
--cm --colormap=L=SPEC override color for element (e.g., h1=RD)
--hm --heading-markup=# enable markup in headings (all/bold/...)
--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-]page-number page number on border (default: on)
--[no-]pager[=#] pager command
script/mdee view on Meta::CPAN
)
##############################################################################
# Theme base colors
##############################################################################
declare -A theme_light=([base]='<RoyalBlue>=y25' [FILE]='L25D/L10E' [FILE_FORMAT]='\n %s\n\n')
declare -A theme_dark=([base]='<RoyalBlue>=y80' [FILE]='L00D/L15E' [FILE_FORMAT]='\n %s\n\n')
declare -a md_config=()
declare -a show_fields=(
bold strike italic code_inline
header h1 h2 h3 h4 h5 h6 horizontal_rule blockquote
)
# Patterns (name-pattern pairs)
# Highlighting and folding patterns are handled by App::Greple::md module.
declare -a patterns_default=()
# Build pattern associative array from patterns_default
# (built before theme loading so themes can modify patterns)
declare -A pattern
script/mdee view on Meta::CPAN
are available. Combine them with L<nup(1)|App::nup> for similar paged output
(e.g., C<nup glow README.md>).
The pipeline combines L<greple(1)|App::Greple> for colorization and
L<nup(1)|App::nup> for multi-column paged output.
The following elements are highlighted. Elements marked with
C<--show> can be individually disabled via the C<--show> option.
Others are always processed for structural integrity.
--show bold (**bold**, __bold__)
--show italic (*italic*, _italic_)
--show strike (~~strike~~)
--show code_inline (`code`)
--show header, h1-h6 (# heading)
--show horizontal_rule (---, ***, ___)
--show blockquote (> quote)
always code_mark (``` or ~~~)
code_block
code_info
always comment (<!-- ... -->)
script/mdee view on Meta::CPAN
C<--pane-width> minus 5 (margin for borders and padding), which
gives 80 when pane-width is 85. Only effective when C<--fold> is
enabled.
=back
=head2 Theme Options
B<em·dee> supports color themes for customizing syntax highlighting.
Themes define colors for various Markdown elements (headers, code blocks,
bold text, etc.).
=over 4
=item B<-t> I<NAME>, B<--theme>=I<NAME>[,I<NAME>,...]
Select color themes. The default is C<hashed,nomark>, which
appends closing hashes to h3-h6 headers and hides markup
characters for a clean reading experience.
Themes specified with C<--theme> are added to the default, not
script/mdee view on Meta::CPAN
=over 4
=item C<hashed> (default)
Append closing hashes to h3-h6 headers.
=item C<nomark> (default)
Hide emphasis markers (C<**>, C<*>, C<__>, C<_>, C<~~>),
inline code backticks, and link brackets (C<[>, C<]>).
Content text keeps its formatting (bold, italic,
strikethrough, code, clickable links) but the surrounding
markers are not displayed.
=item C<warm>
Change base color to Coral.
=item C<fatchap>
Add decorative border lines (C<#>) above and below h1 headings,
script/mdee view on Meta::CPAN
=item B<--config>=I<KEY>=I<VALUE>
Set config parameters from the command line. Parameters are
passed to the L<App::Greple::md> module via
L<Getopt::EX::Config>. Theme keys recognized by mdee (C<base>,
C<FILE>, C<FILE_FORMAT>) are consumed locally and applied to both
light and dark themes; all other parameters are forwarded to the
md module.
Color labels (C<h1>, C<bold>, C<italic>, etc.) are accepted as
config parameters and override default colors. The C<${base}>
placeholder can be used and will be expanded at runtime.
mdee --config h1=RD # red h1
mdee --config bold=GD --config italic=YI # green bold, yellow italic
mdee --config h1='L25D/R;E' # custom h1 with background
mdee --config FILE='L25D/R;E' # red file label
mdee --config FILE_FORMAT=%s: # simple format
mdee --config hashed.h3=1 # enable h3 closing hashes
Theme keys consumed by mdee:
=over 4
=item C<base> - Base color (equivalent to C<--base-color>)
script/mdee view on Meta::CPAN
=head2 Highlight Options
=over 4
=item B<--colormap>=I<LABEL>=I<SPEC>, B<--cm>=I<LABEL>=I<SPEC>
Override the color for a specific element. I<LABEL> is one of the
color labels listed below. I<SPEC> follows
L<Term::ANSIColor::Concise> format.
--colormap h1=RD # red bold headings
--colormap code_block=/L20;E # dark background for code blocks
--colormap bold='${base}D' # base color bold
Available labels:
h1 - h6 Heading levels 1 through 6
bold Bold (**text** or __text__)
italic Italic (*text* or _text_)
strike Strikethrough (~~text~~)
emphasis_mark Emphasis markers (**, *, __, _, ~~)
bold_mark Bold markers only (overrides emphasis_mark)
italic_mark Italic markers only (overrides emphasis_mark)
strike_mark Strike markers only (overrides emphasis_mark)
link Inline links [text](url)
image Images 
image_link Image links [](url)
link_mark Link/image brackets (overrides emphasis_mark)
blockquote Blockquote marker (>)
horizontal_rule Horizontal rules (---, ***, ___)
comment HTML comments (<!-- ... -->)
code_mark Code block delimiters (``` and ~~~)
script/mdee view on Meta::CPAN
code_inline Inline code body
This option can be specified multiple times. Color labels can
also be set via C<--config>. When both are
specified, C<--cm> takes priority over C<--config>.
=item B<--heading-markup>=I<STEPS>, B<--hm> I<STEPS>
Control inline markup processing inside headings. By default,
headings are rendered with uniform heading color without processing
bold, italic, strikethrough, or inline code inside them. Links
are always processed as OSC 8 hyperlinks regardless of this option.
Use C<all> or C<1> to enable all inline formatting within headings
using cumulative coloring. To select specific steps, list them
separated by colons.
Available steps: C<inline_code>, C<horizontal_rules>, C<bold>,
C<italic>, C<strike>.
mdee --hm all file.md # all markup
mdee --hm bold file.md # bold only
mdee --hm bold:italic file.md # bold and italic
=item B<--show>=I<FIELD>[=I<VALUE>],...
Control field visibility for highlighting. Empty value or C<0> disables
the field; any other value (including C<1>) enables it.
--show italic # enable italic
--show bold=0 # disable bold
--show all # enable all fields
--show all= --show bold # disable all, then enable only bold
Multiple fields can be specified with commas or by repeating the option.
The special field C<all> affects all fields and is processed first.
See L</DESCRIPTION> for the list of available fields and
elements that are always active.
All controllable fields are enabled by default.
=back
script/mdee view on Meta::CPAN
mechanism as theme files:
# Change base color for both modes
theme_light[base]='<DarkCyan>=y25'
theme_dark[base]='<DarkCyan>=y80'
# Enable md module features
md_config+=(hashed.h3=1 hashed.h4=1 hashed.h5=1 hashed.h6=1)
Changing the base color automatically affects all derived colors
(h1, h2, bold, etc.) because the md module expands C<${base}>
references.
Use C<-d> to dump current theme values in sourceable format.
B<Color specification format>
Color specifications use L<Term::ANSIColor::Concise> format.
The C<FG/BG> notation specifies foreground and background colors
(e.g., C<L25D/${base};E> means gray foreground on base-colored background).
The C<${base}> string is expanded to the base color value after loading.
script/mdee view on Meta::CPAN
option, which sets C<--all --need=0 --exit=0> to run as a filter
without requiring pattern arguments. Syntax highlighting is handled
entirely by the C<-Mmd> module.
Supported Markdown elements:
=over 4
=item * Headers (C<# h1> through C<###### h6>)
=item * Bold text (C<**bold**> or C<__bold__>)
=item * Italic text (C<*italic*> or C<_italic_>)
=item * Inline code (C<`code`>)
=item * Code blocks (fenced with C<```> or C<~~~>)
=item * HTML comments (C<< <!-- comment --> >>)
=back
script/mdee view on Meta::CPAN
to the md module via C<md_config[]>:
# theme/warm.sh â change base color
theme_light[base]='<Coral>=y25'
theme_dark[base]='<Coral>=y80'
# theme/hashed.sh â enable closing hashes
md_config+=(hashed.h3=1 hashed.h4=1 hashed.h5=1 hashed.h6=1)
The C<md_config[]> entries are passed as config parameters to the
L<App::Greple::md> module. Color labels (C<h1>, C<bold>, etc.)
can also be set as config parameters:
# theme file or config.sh â override color label
md_config+=(h1='L25D/R;E' bold=RD)
=head4 Base Color Expansion
The C<${base}> placeholder in color values is expanded by the md
module. The base color is determined by C<--base-color> option
(default: RoyalBlue) with automatic luminance adjustment based on
mode (C<=y25> for light, C<=y80> for dark).
=head3 Color Specifications
script/mdee view on Meta::CPAN
=head1 LIMITATIONS
=head2 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., C<< `<!-->` >>).
=head2 Emphasis
Emphasis patterns (bold and italic) do not span multiple lines.
Multi-line emphasis text is not supported.
C<***bold italic***> and C<___bold italic___> are supported.
Other nested forms (e.g., C<< **bold _italic_** >>) are not.
=head2 Links
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 (C<[text][ref]> with C<[ref]: url> elsewhere)
are not supported.
=head2 Table Alignment
Column alignment assumes that cell content in the source is not
pre-padded. If the source table is already formatted with aligned
spacing (e.g., C<| c |>), the embedded spaces become part of
t/01_mdee.t view on Meta::CPAN
# Test: show option (verify actual output behavior)
subtest 'show option' => sub {
# Helper: check if text has ANSI color directly applied
# Markers and content may be colored separately (emphasis_mark),
# so match ANSI before either the marker or the content.
sub has_ansi_around {
my ($out, $text) = @_;
return $out =~ /\e\[[0-9;]*m\Q$text\E/;
}
sub has_bold_coloring {
my ($out) = @_;
# Markers (**) are colored with emphasis_mark, content with bold
return $out =~ /\e\[[0-9;]*m\*\*.*bold text.*\*\*/;
}
sub has_italic_coloring {
my ($out) = @_;
return $out =~ /\e\[[0-9;]*m_.*italic text.*_/;
}
# Default: bold should be colored (--no-theme to test with markers visible)
my $default = `$mdee -f --no-theme $test_md 2>&1`;
ok(has_bold_coloring($default), 'default has bold formatting');
# --show bold=0: bold should NOT be colored
my $no_bold = `$mdee -f --no-theme --show bold=0 $test_md 2>&1`;
ok(!has_bold_coloring($no_bold), '--show bold=0 disables bold');
# --show italic=0: italic should NOT be colored
my $no_italic = `$mdee -f --no-theme --show italic=0 $test_md 2>&1`;
ok(!has_italic_coloring($no_italic), '--show italic=0 disables italic');
# --show all= disables all formatting
my $all_off = `$mdee -f --no-theme '--show=all=' $test_md 2>&1`;
ok(!has_bold_coloring($all_off), '--show all= disables bold');
# --show all= --show bold: only bold colored
my $only_bold = `$mdee -f --no-theme '--show=all=' --show=bold $test_md 2>&1`;
ok(has_bold_coloring($only_bold), '--show all= --show bold enables bold');
ok(!has_italic_coloring($only_bold), '--show all= --show bold disables italic');
# unknown field should error
my $unknown = `$mdee --dryrun --show unknown $test_md 2>&1`;
like($unknown, qr/unknown field/, '--show unknown produces error');
};
# Test: config file defaults
subtest 'config file defaults' => sub {
use File::Temp qw(tempdir);
my $tmpdir = tempdir(CLEANUP => 1);
t/02_colorize.t view on Meta::CPAN
# Empty ;sub{} suffix should not break colorization
my $no_sub = run("-Mmd --cm 'h3=RD' -- $test_md")->stdout;
my ($h3_plain) = map { $strip->($_) } grep { /Heading 3/ } split /\n/, $no_sub;
unlike($h3_plain, qr/### Heading 3 ###/, "no sub{} means no closing hashes");
# Multi-backtick code span: spaces should be stripped (CommonMark)
my ($multi_bt) = map { $strip->($_) } grep { /Multi-backtick/ } split /\n/, $out;
like($multi_bt, qr/\`\`\*\*\`\N{ACUTE ACCENT}/, "multi-backtick strips spaces around content");
unlike($multi_bt, qr/\` \`\*\*\`\N{ACUTE ACCENT} \`/, "multi-backtick does not preserve inner spaces");
# Code span protection: bold/strike not processed inside code spans
my ($code_prot) = grep { /inline code with/ } split /\n/, $out;
unlike($code_prot, qr/\e\[1m/, "bold not applied inside inline code");
# Word boundary: underscore emphasis not applied inside words (CommonMark)
my ($wb_line) = map { $strip->($_) } grep { /Word boundaries/ } split /\n/, $out;
like($wb_line, qr/abc_def_ghi/, "_ not applied inside word abc_def_ghi");
like($wb_line, qr/foo__bar__baz/, "__ not applied inside word foo__bar__baz");
like($wb_line, qr/x___y___z/, "___ not applied inside word x___y___z");
}
done_testing;
# Markdown Syntax Demo
## Emphasis
**bold text** and *italic text* and ~~strikethrough~~.
__bold underscore__ and _italic underscore_.
***bold italic*** and ___bold italic underscore___.
Mixed: **bold with *italic* inside** is not fully supported.
## Code
Inline `code` and multi-backtick `` `literal` `` example.
```bash
echo "fenced code block"
ls -la
```
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
## Blockquotes
> This is a blockquote with **bold** and `code`.
>
> > Nested quote with *italic*.
## Table
| Command | Description | Version |
| - | - | - |
| greple | Pattern matching | 10.04 |
| ansifold | Text folding | 2.26 |
| ansicolumn | Column formatting | 1.44 |
| nup | Multi-column output | 0.22 |
## Lists
- First item with `inline code` and a description long enough to demonstrate line folding behavior when the terminal width is narrower than the text content
- Second item with **bold** and *italic* and ***bold italic*** combined in a single line that also extends beyond the typical display width to show proper indentation after wrapping
- Third item with a [link](https://example.com) and `code` and ~~strikethrough~~ mixed together in a line that should definitely wrap at some point
- Nested item with a longer description to verify that nested list indentation is preserved correctly when ansifold wraps the text at the configured width
- Another nested item
1. Numbered list item with enough text to demonstrate that numbered list markers are handled correctly during the folding process and indentation is maintained
2. With **emphasis** and [links](https://example.com) and `inline code` all in one long line
3. Third numbered item
## Definition Lists
---
## Combined Formatting
> **Important:** Markers are hidden by default. Use `--no-theme` to show them.
>
> ***Bold italic*** in a blockquote with [a link](https://example.com).
## Escaped Characters
\**not bold\** and \~~not strike\~~.
## Heading 2 with [link](https://example.com)
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
Normal text with **bold** and *italic* and ~~strikethrough~~.
Underscore bold: __double underscore__ and italic: _single underscore_.
`inline code with **not bold**`
More text with `code` inside.
Multi-backtick: `` `**` `` and ``` ``code`` ```.
```bash
echo "code block"
```
~~~python
Four-space indented fence becomes content (CommonMark rule):
```markdown
- List item with code block:
```bash
echo "indented code"
```
```
> blockquote with **bold** text
> nested > quote
---
***
[simple link](https://example.com)

[](https://example.com)
<!-- HTML comment -->
<!--
multi-line comment
-->
## Heading with [link](https://example.com) inside
**bold with `code` inside**
Escaped: \**not bold\** and \`not code\`
Word boundaries: abc_def_ghi and foo__bar__baz and x___y___z.
## Table Example
|Name|Description|Status|
|-|-|-|
|greple|Pattern matching tool|active|
|ansifold|ANSI-aware text folding|active|
|ansicolumn|Column formatting with ANSI support|active|
## Aligned Table
| Left | Center | Right | Default |
|:-----|:------:|------:|---------|
| a | b | c | d |
| long | x | y | z |
## List Example
- First item with `inline code`
- Second item with **bold text** and _italic_
- Third item with a longer description that might wrap to multiple lines when displayed in a narrow terminal window
## Definition List Example
greple
: Pattern matching and highlighting tool with extensive regex support for syntax highlighting
ansifold
: ANSI-aware text folding utility that wraps long lines while preserving escape sequences and maintaining proper indentation
Term with blank line
: Definition after a blank line with `inline code` and **bold text** that might wrap to multiple lines