kaneo (minimalist kanban) fork to experiment adding a tangled integration github.com/usekaneo/kaneo
at main 84 lines 1.9 kB view raw
1type ProseMirrorNodeJSON = { 2 type: string; 3 attrs?: Record<string, unknown>; 4 content?: ProseMirrorNodeJSON[]; 5 text?: string; 6}; 7 8const TASK_LINE_REGEX = /^\s*[-*]\s+\[( |x|X)\]\s*(.*)$/; 9const HEADING_REGEX = /^(#{1,6})\s+(.+)$/; 10 11function createParagraphNode(text: string): ProseMirrorNodeJSON { 12 const normalizedText = text.trim(); 13 if (!normalizedText) { 14 return { type: "paragraph" }; 15 } 16 17 return { 18 type: "paragraph", 19 content: [{ type: "text", text: normalizedText }], 20 }; 21} 22 23export function parseTaskListMarkdownToNodes( 24 rawText: string, 25): ProseMirrorNodeJSON[] | null { 26 if (!/(?:^|\n)\s*[-*]\s+\[(?: |x|X)\]\s+/.test(rawText)) { 27 return null; 28 } 29 30 const lines = rawText.replace(/\r\n/g, "\n").split("\n"); 31 const nodes: ProseMirrorNodeJSON[] = []; 32 let taskItems: ProseMirrorNodeJSON[] = []; 33 let hasTaskItems = false; 34 35 const flushTaskItems = () => { 36 if (!taskItems.length) return; 37 nodes.push({ 38 type: "taskList", 39 content: taskItems, 40 }); 41 taskItems = []; 42 }; 43 44 for (const line of lines) { 45 const trimmedLine = line.trim(); 46 if (!trimmedLine) { 47 flushTaskItems(); 48 continue; 49 } 50 51 const taskMatch = TASK_LINE_REGEX.exec(line); 52 if (taskMatch) { 53 hasTaskItems = true; 54 taskItems.push({ 55 type: "taskItem", 56 attrs: { checked: taskMatch[1].toLowerCase() === "x" }, 57 content: [createParagraphNode(taskMatch[2] || "")], 58 }); 59 continue; 60 } 61 62 flushTaskItems(); 63 64 const headingMatch = HEADING_REGEX.exec(trimmedLine); 65 if (headingMatch) { 66 nodes.push({ 67 type: "heading", 68 attrs: { level: headingMatch[1].length }, 69 content: [{ type: "text", text: headingMatch[2].trim() }], 70 }); 71 continue; 72 } 73 74 nodes.push(createParagraphNode(trimmedLine)); 75 } 76 77 flushTaskItems(); 78 79 if (!hasTaskItems || !nodes.length) { 80 return null; 81 } 82 83 return nodes; 84}