Skip to content

Instantly share code, notes, and snippets.

@jpic
Created September 24, 2025 12:35
Show Gist options
  • Select an option

  • Save jpic/179ff6f2256fbe46c9e418f0880ee6ab to your computer and use it in GitHub Desktop.

Select an option

Save jpic/179ff6f2256fbe46c9e418f0880ee6ab to your computer and use it in GitHub Desktop.
Simple ansi to html
import re
# 256-color to hex mapping (simplified xterm-256color palette)
def ansi_256_to_hex(n):
try:
n = int(n)
if 0 <= n <= 7: # Standard colors
colors = ['#000000', '#FF0000', '#00FF00', '#FFFF00', '#0000FF', '#FF00FF', '#00FFFF', '#FFFFFF']
return colors[n]
elif 8 <= n <= 15: # Bright colors (approximated to standard)
colors = ['#000000', '#FF0000', '#00FF00', '#FFFF00', '#0000FF', '#FF00FF', '#00FFFF', '#FFFFFF']
return colors[n - 8]
elif 16 <= n <= 231: # 6x6x6 RGB cube
n -= 16
r = (n // 36) * 51
g = ((n // 6) % 6) * 51
b = (n % 6) * 51
return f'#{r:02x}{g:02x}{b:02x}'
elif 232 <= n <= 255: # Grayscale (24 steps from #080808 to #EEEEEE)
v = 8 + (n - 232) * 10
return f'#{v:02x}{v:02x}{v:02x}'
return '#000000' # Fallback for invalid indices
except (ValueError, IndexError):
return '#000000'
# Convert ANSI-colored text to HTML
def ansi_to_html(ansi_text):
# Current state
current_style = {'fg': None, 'bg': None, 'bold': False}
output = []
# Split text by ANSI escape sequences
parts = re.split(r'(\033\[[0-9;]*[m])', ansi_text)
for part in parts:
if not part:
continue
# Handle ANSI escape codes
if part.startswith('\033['):
code = part[2:-1] # Remove \033[ and m
if code == '0': # Reset
current_style = {'fg': None, 'bg': None, 'bold': False}
elif code == '1': # Bold on
current_style['bold'] = True
elif code == '22': # Bold off
current_style['bold'] = False
elif code.startswith('38;5;'): # 256-color foreground
n = code[5:]
current_style['fg'] = ansi_256_to_hex(n)
elif code.startswith('48;5;'): # 256-color background
n = code[5:]
current_style['bg'] = ansi_256_to_hex(n)
# Ignore other codes
continue
# Handle text content
# Escape HTML special characters
part = (part.replace('&', '&amp;')
.replace('<', '&lt;')
.replace('>', '&gt;')
.replace('"', '&quot;')
.replace('\n', '<br>'))
# Build CSS style
styles = []
if current_style['fg']:
styles.append(f'color: {current_style["fg"]}')
if current_style['bg']:
styles.append(f'background: {current_style["bg"]}')
if current_style['bold']:
styles.append('font-weight: bold')
style_attr = f' style="{";".join(styles)}"' if styles else ''
output.append(f'<span{style_attr}>{part}</span>')
# Wrap in HTML document
html = f"""<!DOCTYPE html>
<html>
<head>
<title>ANSI to HTML</title>
<style>
body {{ font-family: monospace; white-space: pre; background: #000000; color: #FFFFFF; }}
</style>
</head>
<body>
{"".join(output)}
</body>
</html>"""
return html
# Example usage with your pip install output
ansi_output = """
24/09 2025 14:33:24 jpic@jpic ~/.local/lib/python3.13/site-packages/pygments
$ pip install -U pygments --user
Requirement already satisfied: pygments in /home/jpic/.local/lib/python3.13/site-packages (2.19.1)
Collecting pygments
Downloading pygments-2.19.2-py3-none-any.whl.metadata (2.5 kB)
Downloading pygments-2.19.2-py3-none-any.whl (1.2 MB)
\033[38;5;33m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m 1.2/1.2 MB 6.7 MB/s 0:00:00
Installing collected packages: pygments
Attempting uninstall: pygments
Found existing installation: Pygments 2.19.1
Uninstalling Pygments-2.19.1:
Successfully uninstalled Pygments-2.19.1
Successfully installed pygments-2.19.2
"""
# Convert to HTML and save
html_output = ansi_to_html(ansi_output)
with open("output.html", "w") as f:
f.write(html_output)
print("HTML output saved to output.html")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment