Skip to content

Instantly share code, notes, and snippets.

@beeksiwaais
Created June 5, 2025 16:54
Show Gist options
  • Select an option

  • Save beeksiwaais/81d2a45a0987a4c2451013aa5a49f4fc to your computer and use it in GitHub Desktop.

Select an option

Save beeksiwaais/81d2a45a0987a4c2451013aa5a49f4fc to your computer and use it in GitHub Desktop.
A perl script to transform markdown to italic and bold in the terminal without buffering
#!/usr/bin/env perl
# Ollama Markdown to Terminal Colors Converter - Perl Version
# Usage: ollama run llama "your prompt" | ./ollama-markdown-colorizer.pl
use strict;
use warnings;
use Term::ReadKey;
# Disable buffering for immediate output
$| = 1;
STDOUT->autoflush(1);
# ANSI color codes
my $BOLD = "\033[1m";
my $ITALIC = "\033[3m";
my $CYAN = "\033[0;36m";
my $MAGENTA = "\033[0;35m";
my $BLUE = "\033[0;34m";
my $WHITE = "\033[1;37m";
my $DIM = "\033[2m";
my $GRAY = "\033[0;90m";
my $RESET = "\033[0m";
# State variables
my $bold_open = 0;
my $italic_open = 0;
my $code_open = 0;
my $in_code_block = 0;
my $line_buffer = "";
my $at_line_start = 1;
my $pending_char = "";
my $waiting_for_next = 0;
# Function to apply current formatting state
sub apply_state {
my $output = "";
if ($in_code_block) {
$output .= $DIM;
} else {
$output .= $CYAN if $code_open;
$output .= $BOLD if $bold_open;
$output .= $ITALIC if $italic_open;
}
print $output;
}
# Function to toggle bold formatting
sub toggle_bold {
if (!$bold_open) {
print $RESET . $BOLD;
$bold_open = 1;
apply_state();
} else {
print $RESET;
$bold_open = 0;
apply_state();
}
}
# Function to toggle italic formatting
sub toggle_italic {
if (!$italic_open) {
print $RESET . $ITALIC;
$italic_open = 1;
apply_state();
} else {
print $RESET;
$italic_open = 0;
apply_state();
}
}
# Function to toggle code formatting
sub toggle_code {
if (!$code_open) {
print $RESET . $CYAN;
$code_open = 1;
} else {
print $RESET;
$code_open = 0;
apply_state();
}
}
# Function to process a character (with potential lookahead)
sub process_char {
my $char = shift;
# Handle pending character from previous lookahead
if ($waiting_for_next) {
$waiting_for_next = 0;
if ($pending_char eq "*") {
if ($char eq "*") {
# This is ** (bold)
toggle_bold();
return; # Consume both characters
} else {
# Previous was single * (italic)
toggle_italic();
# Continue processing current character
}
} elsif ($pending_char eq "`") {
# Process the backtick we were holding
toggle_code();
# Continue processing current character
}
$pending_char = "";
}
# Add to line buffer for line-level processing
$line_buffer .= $char;
# Handle newlines - check for headers and code blocks
if ($char eq "\n") {
# Remove the newline from buffer for checking
my $check_line = $line_buffer;
chomp($check_line);
# Check if this line is a code block delimiter
if ($check_line =~ /^```[a-zA-Z]*$/) {
if (!$in_code_block) {
$in_code_block = 1;
print $RESET . $GRAY . "┌─ Code Block ─" . $RESET . "\n";
print $DIM;
} else {
$in_code_block = 0;
print $RESET . $GRAY . "└─────────────" . $RESET . "\n";
apply_state();
}
$line_buffer = "";
$at_line_start = 1;
return;
}
# Check for headers at start of line
if ($at_line_start && $check_line =~ /^(#{1,6})\s+(.*)$/) {
my $header_level = length($1);
my $header_text = $2;
if ($header_level == 1) {
print $RESET . $BOLD . $MAGENTA;
} elsif ($header_level == 2) {
print $RESET . $BOLD . $BLUE;
} elsif ($header_level == 3) {
print $RESET . $BOLD . $CYAN;
} else {
print $RESET . $BOLD . $WHITE;
}
# Output the header text and reset
print $header_text . $RESET . "\n";
$line_buffer = "";
$at_line_start = 1;
apply_state();
return;
}
print $char;
$line_buffer = "";
$at_line_start = 1;
return;
}
# Track if we're at line start (ignoring whitespace)
if ($char !~ /[ \t]/) {
$at_line_start = 0;
}
# Skip markdown processing if in code block
if ($in_code_block) {
print $char;
return;
}
# Process markdown characters that need lookahead
if ($char eq "*") {
# Need to wait for next character to distinguish * from **
$pending_char = "*";
$waiting_for_next = 1;
return; # Don't output yet
} elsif ($char eq "`") {
# Inline code - immediate toggle
toggle_code();
return;
} else {
# Regular character
print $char;
}
}
# Main processing function
sub main_loop {
# Set up unbuffered input
ReadMode('raw');
my $char;
while (defined($char = ReadKey(0))) {
process_char($char);
}
# Handle any pending character at end of stream
if ($waiting_for_next) {
if ($pending_char eq "*") {
toggle_italic();
} elsif ($pending_char eq "`") {
toggle_code();
}
}
print $RESET;
ReadMode('restore');
}
# Main execution
if (@ARGV == 2) {
my ($model, $prompt) = @ARGV;
open(my $fh, '-|', 'ollama', 'run', $model, $prompt)
or die "Cannot run ollama: $!";
ReadMode('raw');
while (read($fh, my $char, 1)) {
process_char($char);
}
close($fh);
# Handle any pending character at end of stream
if ($waiting_for_next) {
if ($pending_char eq "*") {
toggle_italic();
} elsif ($pending_char eq "`") {
toggle_code();
}
}
print $RESET;
ReadMode('restore');
} elsif (@ARGV == 0) {
main_loop();
} else {
print "Usage: $0 [model] [prompt]\n";
print " Or: ollama run model 'prompt' | $0\n";
print "\n";
print "Examples:\n";
print " $0 llama3 'Explain markdown formatting'\n";
print " ollama run llama3 'Write some **bold** and *italic* text' | $0\n";
exit 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment