Skip to content

Instantly share code, notes, and snippets.

@shenli

shenli/notion.ts Secret

Last active May 9, 2024 13:58
Show Gist options
  • Save shenli/57cfd7f211dbd45fc78b1177d9fdf269 to your computer and use it in GitHub Desktop.
Save shenli/57cfd7f211dbd45fc78b1177d9fdf269 to your computer and use it in GitHub Desktop.
Convert to Notion Block
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