export interface TextHighlight { text: string; color: string; } export interface HighlightedTextSegment { text: string; color: string | null; } export function highlightTextSegments( value: string, highlights: readonly TextHighlight[], ): HighlightedTextSegment[] { const rules = highlights .filter((highlight) => highlight.text.length > 0) .toSorted((left, right) => right.text.length - left.text.length); if (!rules.length) return [{ text: value, color: null }]; const segments: HighlightedTextSegment[] = []; let cursor = 0; while (cursor < value.length) { let matchIndex = -1; let match: TextHighlight | undefined; for (const rule of rules) { const index = value.indexOf(rule.text, cursor); if (index !== -1 && (matchIndex === -1 || index < matchIndex)) { matchIndex = index; match = rule; } } if (!match || matchIndex === -1) { segments.push({ text: value.slice(cursor), color: null }); break; } if (matchIndex > cursor) { segments.push({ text: value.slice(cursor, matchIndex), color: null }); } segments.push({ text: match.text, color: match.color }); cursor = matchIndex + match.text.length; } return segments; }