|
1 | 1 | const SUPPORTED = typeof window !== 'undefined' && 'speechSynthesis' in window; |
2 | 2 |
|
| 3 | +/** Remove all HTML tags, repeating until no tags remain (prevents incomplete sanitization). */ |
| 4 | +function stripHtmlTags(text: string): string { |
| 5 | + const tagPattern = /<[^>]*>/g; |
| 6 | + let previous = text; |
| 7 | + let result = text.replace(tagPattern, ''); |
| 8 | + while (result !== previous) { |
| 9 | + previous = result; |
| 10 | + result = result.replace(tagPattern, ''); |
| 11 | + } |
| 12 | + return result; |
| 13 | +} |
| 14 | + |
3 | 15 | /** Strip markdown/HTML to plain text suitable for speech synthesis. */ |
4 | 16 | function stripToPlainText(markdown: string): string { |
5 | | - return markdown |
6 | | - // Remove code blocks (``` ... ```) |
7 | | - .replace(/```[\s\S]*?```/g, ' code block omitted ') |
8 | | - // Remove inline code |
9 | | - .replace(/`([^`]+)`/g, '$1') |
10 | | - // Remove images |
11 | | - .replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1') |
12 | | - // Remove links — keep text |
13 | | - .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') |
14 | | - // Remove headings markers |
15 | | - .replace(/^#{1,6}\s+/gm, '') |
16 | | - // Remove bold/italic markers |
17 | | - .replace(/\*{1,3}([^*]+)\*{1,3}/g, '$1') |
18 | | - .replace(/_{1,3}([^_]+)_{1,3}/g, '$1') |
19 | | - // Remove strikethrough |
20 | | - .replace(/~~([^~]+)~~/g, '$1') |
21 | | - // Remove blockquotes |
22 | | - .replace(/^>\s+/gm, '') |
23 | | - // Remove horizontal rules |
24 | | - .replace(/^[-*_]{3,}\s*$/gm, '') |
25 | | - // Remove HTML tags |
26 | | - .replace(/<[^>]+>/g, '') |
| 17 | + return stripHtmlTags( |
| 18 | + markdown |
| 19 | + // Remove code blocks (``` ... ```) |
| 20 | + .replace(/```[\s\S]*?```/g, ' code block omitted ') |
| 21 | + // Remove inline code |
| 22 | + .replace(/`([^`]+)`/g, '$1') |
| 23 | + // Remove images |
| 24 | + .replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1') |
| 25 | + // Remove links — keep text |
| 26 | + .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') |
| 27 | + // Remove headings markers |
| 28 | + .replace(/^#{1,6}\s+/gm, '') |
| 29 | + // Remove bold/italic markers |
| 30 | + .replace(/\*{1,3}([^*]+)\*{1,3}/g, '$1') |
| 31 | + .replace(/_{1,3}([^_]+)_{1,3}/g, '$1') |
| 32 | + // Remove strikethrough |
| 33 | + .replace(/~~([^~]+)~~/g, '$1') |
| 34 | + // Remove blockquotes |
| 35 | + .replace(/^>\s+/gm, '') |
| 36 | + // Remove horizontal rules |
| 37 | + .replace(/^[-*_]{3,}\s*$/gm, '') |
| 38 | + ) |
27 | 39 | // Collapse multiple newlines/spaces |
28 | 40 | .replace(/\n{2,}/g, '. ') |
29 | 41 | .replace(/\n/g, ' ') |
|
0 commit comments