store side art as one ANSI file, render it with PHP (cached)
Deploy / deploy (push) Successful in 3s
Deploy / deploy (push) Successful in 3s
This commit is contained in:
+101
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* ANSI art (a .ans file you can `cat` in a terminal) to HTML for the side
|
||||
* panels. Understands foreground colors: truecolor (38;2;r;g;b), 256-color
|
||||
* (38;5;n), the 16 basic ones, and resets; everything else is ignored.
|
||||
* $mirror flips it left-right, swapping /\ () <> [] {} so shapes still read.
|
||||
*/
|
||||
|
||||
function ansi_color(array $p, ?string $cur): ?string
|
||||
{
|
||||
static $basic = ['000000', 'aa0000', '00aa00', 'aa5500', '0000aa', 'aa00aa', '00aaaa', 'aaaaaa',
|
||||
'555555', 'ff5555', '55ff55', 'ffff55', '5555ff', 'ff55ff', '55ffff', 'ffffff'];
|
||||
for ($i = 0; $i < count($p); $i++) {
|
||||
$n = (int) $p[$i];
|
||||
if ($n === 0 || $n === 39) {
|
||||
$cur = null;
|
||||
} elseif ($n >= 30 && $n <= 37) {
|
||||
$cur = $basic[$n - 30];
|
||||
} elseif ($n >= 90 && $n <= 97) {
|
||||
$cur = $basic[$n - 82];
|
||||
} elseif ($n === 38 && ($p[$i + 1] ?? '') === '2') {
|
||||
$cur = sprintf('%02x%02x%02x', $p[$i + 2] ?? 0, $p[$i + 3] ?? 0, $p[$i + 4] ?? 0);
|
||||
$i += 4;
|
||||
} elseif ($n === 38 && ($p[$i + 1] ?? '') === '5') {
|
||||
$c = (int) ($p[$i + 2] ?? 0);
|
||||
$i += 2;
|
||||
if ($c < 16) {
|
||||
$cur = $basic[$c];
|
||||
} elseif ($c < 232) {
|
||||
$c -= 16;
|
||||
$lvl = static fn(int $v): int => $v ? 55 + 40 * $v : 0;
|
||||
$cur = sprintf('%02x%02x%02x', $lvl(intdiv($c, 36)), $lvl(intdiv($c, 6) % 6), $lvl($c % 6));
|
||||
} else {
|
||||
$cur = str_repeat(sprintf('%02x', 8 + 10 * ($c - 232)), 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $cur;
|
||||
}
|
||||
|
||||
/** ansi_html(), cached on disk until the .ans file changes (parsing costs ~30 ms a side). */
|
||||
function ansi_html_cached(string $file, bool $mirror = false): string
|
||||
{
|
||||
$dir = (defined('DATA_DIR') ? DATA_DIR : sys_get_temp_dir()) . '/art-cache';
|
||||
$base = $dir . '/' . md5($file . '|' . (int) $mirror);
|
||||
$key = $base . '-' . filemtime($file) . '.html';
|
||||
if (is_file($key)) {
|
||||
return (string) file_get_contents($key);
|
||||
}
|
||||
$html = ansi_html($file, $mirror);
|
||||
@mkdir($dir, 0755, true);
|
||||
array_map('unlink', glob($base . '-*.html') ?: []); // every deploy bumps the mtime
|
||||
$tmp = $key . '.' . getmypid();
|
||||
if (@file_put_contents($tmp, $html) !== false) {
|
||||
rename($tmp, $key);
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
function ansi_html(string $file, bool $mirror = false): string
|
||||
{
|
||||
static $flip = ['/' => '\\', '\\' => '/', '(' => ')', ')' => '(', '<' => '>', '>' => '<',
|
||||
'[' => ']', ']' => '[', '{' => '}', '}' => '{'];
|
||||
$lines = [];
|
||||
foreach (explode("\n", rtrim((string) file_get_contents($file), "\n")) as $line) {
|
||||
// split into [char, color] cells
|
||||
$cells = [];
|
||||
$cur = null;
|
||||
foreach (preg_split('/(\e\[[0-9;]*m)/', $line, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $part) {
|
||||
if ($part[0] === "\e") {
|
||||
$cur = ansi_color(explode(';', substr($part, 2, -1)), $cur);
|
||||
continue;
|
||||
}
|
||||
foreach (mb_str_split($part) as $ch) {
|
||||
$cells[] = [$ch, $cur];
|
||||
}
|
||||
}
|
||||
$lines[] = $cells;
|
||||
}
|
||||
|
||||
$width = max(array_map('count', $lines) ?: [0]);
|
||||
$out = [];
|
||||
foreach ($lines as $cells) {
|
||||
$cells = array_pad($cells, $width, [' ', null]);
|
||||
if ($mirror) {
|
||||
$cells = array_map(static fn($c) => [$flip[$c[0]] ?? $c[0], $c[1]], array_reverse($cells));
|
||||
}
|
||||
$html = '';
|
||||
$open = null;
|
||||
foreach ($cells as [$ch, $color]) {
|
||||
// blanks have no color of their own; let them ride along in the open run
|
||||
if ($ch !== ' ' && $color !== $open) {
|
||||
$html .= ($open !== null ? '</i>' : '') . ($color !== null ? "<i style=\"color:#$color\">" : '');
|
||||
$open = $color;
|
||||
}
|
||||
$html .= htmlspecialchars($ch);
|
||||
}
|
||||
$out[] = $html . ($open !== null ? '</i>' : '');
|
||||
}
|
||||
return implode("\n", $out);
|
||||
}
|
||||
Reference in New Issue
Block a user