/**
* Strip rich-text formatting (HTML formatting tags + markdown markers) from a
* string and return plain text. For compact single-line previews such as the AI
* chat-history list, where formatting must NOT render.
*
* Only a whitelist of known formatting tags is removed, so legitimate text such
* as "make a " or "5 < 10" is preserved.
*/
export const stripRichText = (input?: string | null): string => {
if (!input) return "";
let text = input;
// 1. Remove the HTML formatting tags the compact composer can emit — bold, italic,
// underline, strikethrough, link, ordered/unordered lists, blockquote, inline code
// and code-block, plus the structural wrappers these produce. Whitelisted (not a
// blanket strip) so genuine angle-bracket text like "make a " survives.
// Replaced with a space so adjacent list items / blocks don't get word-joined; the
// whitespace is collapsed in step 3.
text = text.replace(
/<\/?(?:b|strong|i|em|u|ins|s|strike|del|a|ol|ul|li|blockquote|code|pre|p|br|div|span|font|h[1-6])\b[^>]*>/gi,
" "
);
// 2. Strip markdown markers (keep inner text), covering the same set of formats in
// case the text carries markdown instead of / alongside HTML.
text = text
.replace(/```[a-z]*\s*([\s\S]*?)```/gi, "$1") // ```code block```
.replace(/(\*\*\*|___)(.+?)\1/g, "$2") // ***bold italic***
.replace(/(\*\*|__)(.+?)\1/g, "$2") // **bold** / __bold/underline__
// *italic* / _italic_ — boundary-aware (capture the leading boundary instead of a
// lookbehind, which Hermes/RN doesn't reliably support) so snake_case identifiers
// like "my_var_name" and "a * b" arithmetic are left untouched.
.replace(/(^|[^A-Za-z0-9*])\*\s*(.+?)\s*\*(?![A-Za-z0-9*])/g, "$1$2") // *italic*
.replace(/(^|[^A-Za-z0-9_])_\s*(.+?)\s*_(?![A-Za-z0-9_])/g, "$1$2") // _italic_
.replace(/~~(.+?)~~/g, "$1") // ~~strikethrough~~
.replace(/`([^`]+)`/g, "$1") // `inline code`
.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1"); // [label](url) -> label
// 3. Collapse leftover whitespace into a single clean line.
return text.replace(/\s+/g, " ").trim();
};