Full document, spreadsheet, slideshow, and diagram tooling
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 67 lines 2.5 kB view raw
1/** 2 * Markdown paste detection for the docs editor. 3 * 4 * Conservatively detects whether pasted plain text is markdown. 5 * Requires 2+ signals or 1 strong signal (heading, code block, table) 6 * to avoid false positives on normal text. 7 * 8 * Also detects when clipboard HTML contains unrendered markdown link 9 * syntax, indicating the source didn't convert markdown to rich text. 10 */ 11 12const PATTERNS: readonly RegExp[] = [ 13 /^#{1,6}\s+\S/m, // ATX headings (strong) 14 /^\s*[-*+]\s+\S/m, // Unordered lists 15 /^\s*\d+\.\s+\S/m, // Ordered lists 16 /^\s*```/m, // Fenced code blocks (strong) 17 /^\s*>\s+\S/m, // Blockquotes 18 /\[.+?\]\(.+?\)/, // Links 19 /!\[.*?\]\(.+?\)/, // Images 20 /^\s*[-*_]{3,}\s*$/m, // Horizontal rules 21 /^\s*\|.+\|.+\|/m, // Tables 22 /^\s*- \[([ xX])\]/m, // Task lists 23]; 24 25// Strong solo signals — a single match is enough 26const HEADING_RE = /^#{1,6}\s+\S/m; 27const CODE_FENCE_RE = /^\s*```/m; 28const TABLE_WITH_SEPARATOR_RE = /^\s*\|.+\|.+\|\s*\n\s*\|[\s:|-]+\|/m; 29const MARKDOWN_LINK_RE = /\[.+?\]\(.+?\)/; 30const TASK_LIST_RE = /^\s*- \[([ xX])\]/m; 31 32export function looksLikeMarkdown(text: string): boolean { 33 if (!text || !text.trim()) return false; 34 35 let signals = 0; 36 for (const p of PATTERNS) { 37 if (p.test(text)) signals++; 38 } 39 40 if (signals >= 2) return true; 41 42 // Strong solo signals 43 if (HEADING_RE.test(text)) return true; 44 if (CODE_FENCE_RE.test(text)) return true; 45 if (TABLE_WITH_SEPARATOR_RE.test(text)) return true; 46 if (MARKDOWN_LINK_RE.test(text)) return true; 47 if (TASK_LIST_RE.test(text)) return true; 48 49 return false; 50} 51 52/** 53 * Check whether clipboard HTML contains unrendered markdown link syntax. 54 * When apps copy content, they may include both text/html and text/plain. 55 * If the HTML contains literal `[text](url)` strings (not rendered as <a> tags), 56 * the plain text markdown path should be preferred. 57 */ 58// Looser task list pattern for HTML context (no start-of-line anchor) 59const RAW_TASK_LIST_IN_HTML = /- \[([ xX])\]\s/; 60 61export function htmlContainsRawMarkdown(html: string): boolean { 62 if (!html) return false; 63 // Strip actual <a> tags and their content to avoid false positives, 64 // then check if raw markdown link syntax remains in the text content. 65 const stripped = html.replace(/<a\b[^>]*>.*?<\/a>/gi, ''); 66 return MARKDOWN_LINK_RE.test(stripped) || RAW_TASK_LIST_IN_HTML.test(stripped); 67}