-
-
Save shenli/57cfd7f211dbd45fc78b1177d9fdf269 to your computer and use it in GitHub Desktop.
Convert to Notion Block
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { JSONContent } from "novel"; | |
// Recursively extract text content from nodes | |
const extractText = (node: any): string => { | |
if (!node) return ''; | |
// If the node directly contains text, return it | |
if (node.type === 'text' && typeof node.text === 'string') { | |
return node.text; | |
} | |
// If the node has content that is an array, recursively process each child node | |
if (Array.isArray(node.content)) { | |
return node.content.map(extractText).join(''); | |
} | |
// Fallback for nodes without text or recognizable structure | |
return ''; | |
}; | |
// Map Tiptap node to Notion block for simple cases (text, paragraph, quote, code) | |
const mapSimpleNodeToNotionBlock = (type: string, node: any) => { | |
let notionType: string; | |
switch (type) { | |
case 'paragraph': | |
notionType = 'paragraph'; | |
break; | |
case 'quote': | |
notionType = 'quote'; | |
break; | |
case 'code': | |
notionType = 'code'; | |
break; | |
default: | |
notionType = 'paragraph'; // Fallback to paragraph | |
} | |
return { | |
object: 'block', | |
type: notionType, | |
[notionType]: { | |
rich_text: [{ | |
type: 'text', | |
text: { | |
content: extractText(node), | |
}, | |
}], | |
"color": "default" | |
} | |
}; | |
}; | |
// Function to handle heading nodes | |
const mapHeadingNodeToNotionBlock = (node: any): any => { | |
const level = node.attrs.level || 1; // Default to level 1 if not specified | |
const type = `heading_${Math.min(level, 3)}`; // Notion supports heading levels 1, 2, 3 | |
return { | |
object: 'block', | |
type, | |
[type]: { | |
text: [{ | |
type: 'text', | |
text: { | |
content: extractText(node), | |
}, | |
}], | |
}, | |
}; | |
}; | |
// Function to handle list items (bulleted, numbered, to-do) | |
const mapListNodeToNotionBlocks = (node: any, listType: string): any[] => { | |
const blockType = listType === 'bullet_list' ? 'bulleted_list_item' : | |
listType === 'ordered_list' ? 'numbered_list_item' : | |
'to_do'; | |
return node.content.map((item: any) => ({ | |
object: 'block', | |
type: blockType, | |
[blockType]: { | |
text: [{ | |
type: 'text', | |
text: { | |
content: extractText(item.content[0]), // Assuming first child node is the text | |
}, | |
}], | |
// For to_do, additional properties can be specified like 'checked' | |
}, | |
})); | |
}; | |
// Main conversion function | |
export const convertTiptapToNotion = (tiptapContent: JSONContent): any[] => { | |
let notionBlocks: any[] = []; | |
tiptapContent.content.forEach((node: any) => { | |
switch (node.type) { | |
case 'text': | |
case 'paragraph': | |
case 'quote': | |
case 'code': | |
notionBlocks.push(mapSimpleNodeToNotionBlock(node.type, node)); | |
break; | |
case 'heading': | |
notionBlocks.push(mapHeadingNodeToNotionBlock(node)); | |
break; | |
case 'bulletList': | |
case 'ordered_list': | |
case 'to_do': | |
notionBlocks = notionBlocks.concat(mapListNodeToNotionBlocks(node, node.type)); | |
break; | |
// Handle other types as needed | |
default: | |
console.warn(`Unsupported node type: ${node.type}`); | |
} | |
}); | |
return notionBlocks; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment