atagen

malice

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

Blame: web/src/lib/inline.ts

705b1e1062c2atagen2026-10-23 09:05 UTC1
import type { Mention } from './mentions';
705b1e1062c2atagen2026-10-23 09:05 UTC2
import { linkSpans } from './urls';
705b1e1062c2atagen2026-10-23 09:05 UTC3
705b1e1062c2atagen2026-10-23 09:05 UTC4
export type InlineSegment =
705b1e1062c2atagen2026-10-23 09:05 UTC5
  | { kind: 'text'; text: string }
705b1e1062c2atagen2026-10-23 09:05 UTC6
  | { kind: 'mention'; text: string; user_id: string }
705b1e1062c2atagen2026-10-23 09:05 UTC7
  | { kind: 'link'; text: string; href: string };
705b1e1062c2atagen2026-10-23 09:05 UTC8
705b1e1062c2atagen2026-10-23 09:05 UTC9
interface Span {
705b1e1062c2atagen2026-10-23 09:05 UTC10
  start: number;
705b1e1062c2atagen2026-10-23 09:05 UTC11
  end: number;
705b1e1062c2atagen2026-10-23 09:05 UTC12
  segment: InlineSegment;
705b1e1062c2atagen2026-10-23 09:05 UTC13
}
705b1e1062c2atagen2026-10-23 09:05 UTC14
705b1e1062c2atagen2026-10-23 09:05 UTC15
const WORD_CHAR = /[\p{L}\p{N}_]/u;
705b1e1062c2atagen2026-10-23 09:05 UTC16
705b1e1062c2atagen2026-10-23 09:05 UTC17
function escapeRegExp(text: string): string {
705b1e1062c2atagen2026-10-23 09:05 UTC18
  return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
705b1e1062c2atagen2026-10-23 09:05 UTC19
}
705b1e1062c2atagen2026-10-23 09:05 UTC20
705b1e1062c2atagen2026-10-23 09:05 UTC21
function mentionSpans(text: string, mentions: Mention[], taken: Span[]): Span[] {
705b1e1062c2atagen2026-10-23 09:05 UTC22
  const byText = new Map(mentions.filter((m) => m.text).map((m) => [m.text, m.user_id]));
705b1e1062c2atagen2026-10-23 09:05 UTC23
  if (byText.size === 0) return [];
705b1e1062c2atagen2026-10-23 09:05 UTC24
  const names = [...byText.keys()].sort((a, b) => b.length - a.length);
705b1e1062c2atagen2026-10-23 09:05 UTC25
  const out: Span[] = [];
705b1e1062c2atagen2026-10-23 09:05 UTC26
  for (const m of text.matchAll(new RegExp(names.map(escapeRegExp).join('|'), 'g'))) {
705b1e1062c2atagen2026-10-23 09:05 UTC27
    const start = m.index;
705b1e1062c2atagen2026-10-23 09:05 UTC28
    const end = start + m[0].length;
705b1e1062c2atagen2026-10-23 09:05 UTC29
    if (WORD_CHAR.test(text[start - 1] ?? '') || WORD_CHAR.test(text[end] ?? '')) continue;
705b1e1062c2atagen2026-10-23 09:05 UTC30
    if (taken.some((s) => start < s.end && end > s.start)) continue;
705b1e1062c2atagen2026-10-23 09:05 UTC31
    out.push({ start, end, segment: { kind: 'mention', text: m[0], user_id: byText.get(m[0])! } });
705b1e1062c2atagen2026-10-23 09:05 UTC32
  }
705b1e1062c2atagen2026-10-23 09:05 UTC33
  return out;
705b1e1062c2atagen2026-10-23 09:05 UTC34
}
705b1e1062c2atagen2026-10-23 09:05 UTC35
705b1e1062c2atagen2026-10-23 09:05 UTC36
export function segmentInline(text: string, mentions: Mention[]): InlineSegment[] {
705b1e1062c2atagen2026-10-23 09:05 UTC37
  const links: Span[] = linkSpans(text).map((l) => ({
705b1e1062c2atagen2026-10-23 09:05 UTC38
    start: l.start,
705b1e1062c2atagen2026-10-23 09:05 UTC39
    end: l.end,
705b1e1062c2atagen2026-10-23 09:05 UTC40
    segment: { kind: 'link', text: text.slice(l.start, l.end), href: l.href }
705b1e1062c2atagen2026-10-23 09:05 UTC41
  }));
705b1e1062c2atagen2026-10-23 09:05 UTC42
  const spans = [...links, ...mentionSpans(text, mentions, links)].sort((a, b) => a.start - b.start);
705b1e1062c2atagen2026-10-23 09:05 UTC43
  const out: InlineSegment[] = [];
705b1e1062c2atagen2026-10-23 09:05 UTC44
  let last = 0;
705b1e1062c2atagen2026-10-23 09:05 UTC45
  for (const s of spans) {
705b1e1062c2atagen2026-10-23 09:05 UTC46
    if (s.start > last) out.push({ kind: 'text', text: text.slice(last, s.start) });
705b1e1062c2atagen2026-10-23 09:05 UTC47
    out.push(s.segment);
705b1e1062c2atagen2026-10-23 09:05 UTC48
    last = s.end;
705b1e1062c2atagen2026-10-23 09:05 UTC49
  }
705b1e1062c2atagen2026-10-23 09:05 UTC50
  if (last < text.length) out.push({ kind: 'text', text: text.slice(last) });
705b1e1062c2atagen2026-10-23 09:05 UTC51
  return out;
705b1e1062c2atagen2026-10-23 09:05 UTC52
}