44 lines
1.3 KiB
TypeScript
44 lines
1.3 KiB
TypeScript
/**
|
|
* Detects whether a string contains meaningful Markdown formatting.
|
|
* Strips URLs and nostr: references first to avoid false positives.
|
|
*/
|
|
export function containsMarkdown(content: string): boolean {
|
|
// Remove URLs and nostr: references to avoid false positives
|
|
const cleaned = content
|
|
.replace(/https?:\/\/[^\s)>\]]+/g, '')
|
|
.replace(/nostr:[a-z0-9]+/g, '')
|
|
|
|
// Strong signals — any single one triggers markdown
|
|
const strongPatterns = [
|
|
/^```/m, // code fence
|
|
/\|[\s]*:?-+:?[\s]*\|/, // table separator |---|
|
|
/!\[[^\]]*\]\(/ // image  {
|
|
if (pattern.test(cleaned)) return true
|
|
}
|
|
|
|
// Medium signals — need 2+ different types
|
|
const mediumPatterns = [
|
|
/^#{1,6}\s+\S/m, // ATX heading (# text), not #hashtag
|
|
/\*\*[^*\n]+\*\*/, // bold **text**
|
|
/__[^_\n]+__/, // bold __text__
|
|
/\[[^\]]+\]\([^)]+\)/, // link [text](url)
|
|
/^>\s+\S/m, // blockquote > text
|
|
/^[-*]\s+\S/m, // unordered list - item or * item
|
|
/^\d+\.\s+\S/m, // ordered list 1. item
|
|
/^---$/m, // horizontal rule
|
|
/~~[^~\n]+~~/ // strikethrough ~~text~~
|
|
]
|
|
|
|
let matchCount = 0
|
|
for (const pattern of mediumPatterns) {
|
|
if (pattern.test(cleaned)) {
|
|
matchCount++
|
|
if (matchCount >= 2) return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|