| 34 | // Handles: ~~strike~~, **bold**, *italic*, _italic_, `code`, |
| 35 | // , [link](url), bare https:// URLs, @mentions |
| 36 | function inlineFormat(text) { |
| 37 | const parts = []; |
| 38 | const re = |
| 39 | /(~~[^~]+~~|\*\*[^*]+\*\*|\*[^*\n]+\*|_[^_\n]+_|`[^`]+`|!\[[^\]]*\]\([^)]+\)|\[[^\]]+\]\([^)]+\)|https?:\/\/[^\s<>"')]+|@[\w-]+)/g; |
| 40 | let last = 0, |
| 41 | m, |
| 42 | k = 0; |
| 43 | |
| 44 | while ((m = re.exec(text)) !== null) { |
| 45 | if (m.index > last) |
| 46 | parts.push(<span key={k++}>{text.slice(last, m.index)}</span>); |
| 47 | const raw = m[0]; |
| 48 | |
| 49 | if (raw.startsWith("~~")) { |
| 50 | parts.push( |
| 51 | <s key={k++} style={{ color: "var(--text3)" }}> |
| 52 | {raw.slice(2, -2)} |
| 53 | </s>, |
| 54 | ); |
| 55 | } else if (raw.startsWith("**")) { |
| 56 | parts.push( |
| 57 | <strong key={k++} style={{ color: "var(--text)", fontWeight: 600 }}> |
| 58 | {raw.slice(2, -2)} |
| 59 | </strong>, |
| 60 | ); |
| 61 | } else if (raw.startsWith("*") || raw.startsWith("_")) { |
| 62 | parts.push( |
| 63 | <em key={k++} style={{ color: "var(--text2)", fontStyle: "italic" }}> |
| 64 | {raw.slice(1, -1)} |
| 65 | </em>, |
| 66 | ); |
| 67 | } else if (raw.startsWith("`")) { |
| 68 | parts.push( |
| 69 | <code |
| 70 | key={k++} |
| 71 | style={{ |
| 72 | fontSize: 11, |
| 73 | background: "var(--surface)", |
| 74 | border: "1px solid var(--border)", |
| 75 | borderRadius: 3, |
| 76 | padding: "1px 5px", |
| 77 | fontFamily: "monospace", |
| 78 | color: "var(--text)", |
| 79 | }} |
| 80 | > |
| 81 | {raw.slice(1, -1)} |
| 82 | </code>, |
| 83 | ); |
| 84 | } else if (raw.startsWith("![")) { |
| 85 | const mm = raw.match(/^!\[([^\]]*)\]\(([^)]+)\)$/); |
| 86 | if (mm) { |
| 87 | parts.push( |
| 88 | <ReleaseImage |
| 89 | key={k++} |
| 90 | src={mm[2]} |
| 91 | alt={mm[1]} |
| 92 | style={{ |
| 93 | maxWidth: "100%", |