atagen

malice

public
Star0
Watch0
df3c0f2b3da49cedd2da20209bd76e39695179bc
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git

Blame: web/src/lib/code.ts

df3c0f2b3da4atagen2026-10-22 13:07 UTC1
export type CodeSegment =
df3c0f2b3da4atagen2026-10-22 13:07 UTC2
  | { kind: 'text'; text: string }
df3c0f2b3da4atagen2026-10-22 13:07 UTC3
  | { kind: 'inline'; text: string }
df3c0f2b3da4atagen2026-10-22 13:07 UTC4
  | { kind: 'block'; text: string; lang: string | null };
df3c0f2b3da4atagen2026-10-22 13:07 UTC5
df3c0f2b3da4atagen2026-10-22 13:07 UTC6
const FENCE = /```([^\s`]*)[^\S\n]*\n?([\s\S]*?)\n?```/g;
df3c0f2b3da4atagen2026-10-22 13:07 UTC7
const INLINE = /`([^`\n]+)`/g;
df3c0f2b3da4atagen2026-10-22 13:07 UTC8
df3c0f2b3da4atagen2026-10-22 13:07 UTC9
function splitInline(text: string, out: CodeSegment[]): void {
df3c0f2b3da4atagen2026-10-22 13:07 UTC10
  let last = 0;
df3c0f2b3da4atagen2026-10-22 13:07 UTC11
  for (const m of text.matchAll(INLINE)) {
df3c0f2b3da4atagen2026-10-22 13:07 UTC12
    if (m.index > last) out.push({ kind: 'text', text: text.slice(last, m.index) });
df3c0f2b3da4atagen2026-10-22 13:07 UTC13
    out.push({ kind: 'inline', text: m[1] });
df3c0f2b3da4atagen2026-10-22 13:07 UTC14
    last = m.index + m[0].length;
df3c0f2b3da4atagen2026-10-22 13:07 UTC15
  }
df3c0f2b3da4atagen2026-10-22 13:07 UTC16
  if (last < text.length) out.push({ kind: 'text', text: text.slice(last) });
df3c0f2b3da4atagen2026-10-22 13:07 UTC17
}
df3c0f2b3da4atagen2026-10-22 13:07 UTC18
df3c0f2b3da4atagen2026-10-22 13:07 UTC19
export function parseCode(text: string): CodeSegment[] {
df3c0f2b3da4atagen2026-10-22 13:07 UTC20
  const out: CodeSegment[] = [];
df3c0f2b3da4atagen2026-10-22 13:07 UTC21
  if (!text.includes('`')) {
df3c0f2b3da4atagen2026-10-22 13:07 UTC22
    if (text) out.push({ kind: 'text', text });
df3c0f2b3da4atagen2026-10-22 13:07 UTC23
    return out;
df3c0f2b3da4atagen2026-10-22 13:07 UTC24
  }
df3c0f2b3da4atagen2026-10-22 13:07 UTC25
  let last = 0;
df3c0f2b3da4atagen2026-10-22 13:07 UTC26
  for (const m of text.matchAll(FENCE)) {
df3c0f2b3da4atagen2026-10-22 13:07 UTC27
    splitInline(text.slice(last, m.index), out);
df3c0f2b3da4atagen2026-10-22 13:07 UTC28
    out.push({ kind: 'block', text: m[2], lang: m[1] || null });
df3c0f2b3da4atagen2026-10-22 13:07 UTC29
    last = m.index + m[0].length;
df3c0f2b3da4atagen2026-10-22 13:07 UTC30
    if (text[last] === '\n') last += 1;
df3c0f2b3da4atagen2026-10-22 13:07 UTC31
  }
df3c0f2b3da4atagen2026-10-22 13:07 UTC32
  splitInline(text.slice(last), out);
df3c0f2b3da4atagen2026-10-22 13:07 UTC33
  return out;
df3c0f2b3da4atagen2026-10-22 13:07 UTC34
}
df3c0f2b3da4atagen2026-10-22 13:07 UTC35
df3c0f2b3da4atagen2026-10-22 13:07 UTC36
export function hasCode(segments: CodeSegment[]): boolean {
df3c0f2b3da4atagen2026-10-22 13:07 UTC37
  return segments.some((s) => s.kind !== 'text');
df3c0f2b3da4atagen2026-10-22 13:07 UTC38
}
df3c0f2b3da4atagen2026-10-22 13:07 UTC39
df3c0f2b3da4atagen2026-10-22 13:07 UTC40
function escapeHtml(text: string): string {
df3c0f2b3da4atagen2026-10-22 13:07 UTC41
  return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
df3c0f2b3da4atagen2026-10-22 13:07 UTC42
}
df3c0f2b3da4atagen2026-10-22 13:07 UTC43
df3c0f2b3da4atagen2026-10-22 13:07 UTC44
/** Matrix `formatted_body` for a plain body, or null when it has no code to format. */
df3c0f2b3da4atagen2026-10-22 13:07 UTC45
export function codeToHtml(body: string): string | null {
df3c0f2b3da4atagen2026-10-22 13:07 UTC46
  const segments = parseCode(body);
df3c0f2b3da4atagen2026-10-22 13:07 UTC47
  if (!hasCode(segments)) return null;
df3c0f2b3da4atagen2026-10-22 13:07 UTC48
  return segments
df3c0f2b3da4atagen2026-10-22 13:07 UTC49
    .map((s) => {
df3c0f2b3da4atagen2026-10-22 13:07 UTC50
      if (s.kind === 'inline') return `<code>${escapeHtml(s.text)}</code>`;
df3c0f2b3da4atagen2026-10-22 13:07 UTC51
      if (s.kind === 'block') {
df3c0f2b3da4atagen2026-10-22 13:07 UTC52
        const cls = s.lang ? ` class="language-${escapeHtml(s.lang)}"` : '';
df3c0f2b3da4atagen2026-10-22 13:07 UTC53
        return `<pre><code${cls}>${escapeHtml(s.text)}</code></pre>`;
df3c0f2b3da4atagen2026-10-22 13:07 UTC54
      }
df3c0f2b3da4atagen2026-10-22 13:07 UTC55
      return escapeHtml(s.text).replace(/\n/g, '<br>');
df3c0f2b3da4atagen2026-10-22 13:07 UTC56
    })
df3c0f2b3da4atagen2026-10-22 13:07 UTC57
    .join('');
df3c0f2b3da4atagen2026-10-22 13:07 UTC58
}