diff --git a/css/style.css b/css/style.css index 8d5e504..6e34da7 100644 --- a/css/style.css +++ b/css/style.css @@ -213,12 +213,13 @@ button { pointer-events: none; user-select: none; } -.side i { font-style: normal; } +/* the art comes as one layer per color, stacked */ +.side { display: grid; } +.side > span { grid-area: 1 / 1; direction: ltr; unicode-bidi: isolate; } .side-left { left: 0; } -/* rtl only so the right panel clips on its inner edge; the art itself stays - ltr, or the bidi algorithm reorders runs of punctuation */ +/* rtl only so the right panel clips on its inner edge; the layers stay ltr + (above), or the bidi algorithm reorders runs of punctuation */ .side-right { right: 0; direction: rtl; } -.side-right > span { direction: ltr; unicode-bidi: isolate; } /* the column's shadow: the art fades out under it instead of stopping dead beside the text. Eased falloff, as wide as about a third of the margin. */ diff --git a/inc/art.php b/inc/art.php index 0c9211b..6806374 100644 --- a/inc/art.php +++ b/inc/art.php @@ -43,7 +43,7 @@ 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'; + $key = $base . '-' . filemtime($file) . '-layers.html'; // bump the suffix when the markup changes if (is_file($key)) { return (string) file_get_contents($key); } @@ -79,23 +79,37 @@ function ansi_html(string $file, bool $mirror = false): string } $width = max(array_map('count', $lines) ?: [0]); - $out = []; - foreach ($lines as $cells) { + 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 ? '' : '') . ($color !== null ? "" : ''); - $open = $color; - } - $html .= htmlspecialchars($ch); - } - $out[] = $html . ($open !== null ? '' : ''); } - return implode("\n", $out); + unset($cells); + + // One full-size text layer per color, holding only that color's + // characters; CSS stacks them. A few dozen elements instead of a span per + // color run (thousands), which is what made the art slow to lay out. + $colors = []; + foreach ($lines as $cells) { + foreach ($cells as [$ch, $color]) { + if ($ch !== ' ') { + $colors[$color ?? ''] = true; + } + } + } + $html = ''; + foreach (array_keys($colors) as $color) { + $rows = []; + foreach ($lines as $cells) { + $row = ''; + foreach ($cells as [$ch, $c]) { + $row .= ($ch !== ' ' && ($c ?? '') === $color) ? $ch : ' '; + } + $rows[] = $row; + } + $style = $color !== '' ? " style=\"color:#$color\"" : ''; + $html .= "" . htmlspecialchars(implode("\n", $rows)) . ''; + } + return $html; } diff --git a/inc/footer.php b/inc/footer.php index c60db93..aeaa67d 100644 --- a/inc/footer.php +++ b/inc/footer.php @@ -12,6 +12,6 @@ $build = preg_match('/[0-9a-f]{7,40}/i', $build, $m) ? $m[0] : ($build ?: 'dev')
- +