UNPKG

ai-knowledge-hub

Version:

MCP server that provides unified access to organizational knowledge across multiple platforms (local docs, Guru, Notion)

375 lines 11.2 kB
/** * Notion block builder utilities * Creates properly formatted Notion blocks from content */ /** * Create rich text array from plain text with optional formatting */ export function createRichText(content, formatting) { if (!content) { return []; } const richText = { type: 'text', text: { content, link: formatting?.link ?? null, }, annotations: { bold: formatting?.bold ?? false, italic: formatting?.italic ?? false, strikethrough: formatting?.strikethrough ?? false, underline: false, code: formatting?.code ?? false, color: formatting?.color ?? 'default', }, plain_text: content, href: formatting?.link?.url ?? null, }; return [richText]; } /** * Create rich text from markdown nodes with formatting */ export function createRichTextFromNodes(nodes) { const richTextArray = []; for (const node of nodes) { if (node.type === 'text' && node.content !== null && node.content !== undefined && node.content.length > 0) { const formatting = { bold: node.bold, italic: node.italic, strikethrough: node.strikethrough, code: node.code, link: node.link, }; richTextArray.push(...createRichText(node.content, formatting)); } } return richTextArray.length > 0 ? richTextArray : createRichText(''); } /** * Build a paragraph block */ export function buildParagraphBlock(content, formatting) { const richText = typeof content === 'string' ? createRichText(content, formatting) : createRichTextFromNodes(content); return { object: 'block', type: 'paragraph', paragraph: { rich_text: richText, color: formatting?.color ?? 'default', }, }; } /** * Build a heading block (H1, H2, or H3) */ export function buildHeadingBlock(content, level, formatting) { const richText = typeof content === 'string' ? createRichText(content, formatting) : createRichTextFromNodes(content); const headingType = `heading_${level}`; return { object: 'block', type: headingType, [headingType]: { rich_text: richText, color: formatting?.color ?? 'default', is_toggleable: false, }, }; } /** * Build a bulleted list item block */ export function buildBulletedListItemBlock(content, children, formatting) { const richText = typeof content === 'string' ? createRichText(content, formatting) : createRichTextFromNodes(content); return { object: 'block', type: 'bulleted_list_item', bulleted_list_item: { rich_text: richText, color: formatting?.color ?? 'default', children: children ?? [], }, }; } /** * Build a numbered list item block */ export function buildNumberedListItemBlock(content, children, formatting) { const richText = typeof content === 'string' ? createRichText(content, formatting) : createRichTextFromNodes(content); return { object: 'block', type: 'numbered_list_item', numbered_list_item: { rich_text: richText, color: formatting?.color ?? 'default', children: children ?? [], }, }; } /** * Build a to-do list item block (for task lists) */ export function buildToDoBlock(content, checked = false, children, formatting) { const richText = typeof content === 'string' ? createRichText(content, formatting) : createRichTextFromNodes(content); return { object: 'block', type: 'to_do', to_do: { rich_text: richText, checked, color: formatting?.color ?? 'default', children: children ?? [], }, }; } /** * Build a code block */ export function buildCodeBlock(code, language, caption) { return { object: 'block', type: 'code', code: { caption: caption !== null && caption !== undefined && caption.length > 0 ? createRichText(caption) : [], rich_text: createRichText(code), language: language ?? 'plain text', }, }; } /** * Build a quote block */ export function buildQuoteBlock(content, children, formatting) { const richText = typeof content === 'string' ? createRichText(content, formatting) : createRichTextFromNodes(content); return { object: 'block', type: 'quote', quote: { rich_text: richText, color: formatting?.color ?? 'default', children: children ?? [], }, }; } /** * Build a divider block (horizontal rule) */ export function buildDividerBlock() { return { object: 'block', type: 'divider', divider: {}, }; } /** * Build a callout block (used for special formatting) */ export function buildCalloutBlock(content, icon, color, children) { const richText = typeof content === 'string' ? createRichText(content) : createRichTextFromNodes(content); return { object: 'block', type: 'callout', callout: { rich_text: richText, icon: icon !== null && icon !== undefined && icon.length > 0 ? { type: 'emoji', emoji: icon, } : { type: 'emoji', emoji: '💡', }, color: color ?? 'default', children: children ?? [], }, }; } /** * Build a toggle block (collapsible content) */ export function buildToggleBlock(content, children, formatting) { const richText = typeof content === 'string' ? createRichText(content) : createRichTextFromNodes(content); return { object: 'block', type: 'toggle', toggle: { rich_text: richText, color: formatting?.color ?? 'default', children: children ?? [], }, }; } /** * Build a table block * Note: Notion tables require table_row blocks as children within the table block */ export function buildTableBlock(rows, hasColumnHeader = true, hasRowHeader = false) { if (rows.length === 0) { return []; } const tableWidth = rows[0].length; // Create table row blocks as children const tableRows = rows.map(row => { // Each cell should be an array of rich text objects const cells = row.map(cellContent => createRichText(cellContent)); return { object: 'block', type: 'table_row', table_row: { cells, }, }; }); // Create the table block with rows as children const tableBlock = { object: 'block', type: 'table', table: { table_width: tableWidth, has_column_header: hasColumnHeader, has_row_header: hasRowHeader, children: tableRows, }, }; // Return only the table block (rows are nested as children) return [tableBlock]; } /** * Build a table block from MarkdownNode arrays (supports rich text in cells) * Note: Notion tables require table_row blocks as children within the table block */ export function buildTableBlockFromNodes(rows, hasColumnHeader = true, hasRowHeader = false) { if (rows.length === 0) { return []; } const tableWidth = rows[0].length; // Create table row blocks as children const tableRows = rows.map(row => { // Each cell should be an array of rich text objects const cells = row.map(cellNode => { // If cell has children (rich text), use those; otherwise use content if (cellNode.children && cellNode.children.length > 0) { return createRichTextFromNodes(cellNode.children); } else { return createRichText(cellNode.content ?? ''); } }); return { object: 'block', type: 'table_row', table_row: { cells, }, }; }); // Create the table block with rows as children const tableBlock = { object: 'block', type: 'table', table: { table_width: tableWidth, has_column_header: hasColumnHeader, has_row_header: hasRowHeader, children: tableRows, }, }; // Return only the table block (rows are nested as children) return [tableBlock]; } /** * Build an image block */ export function buildImageBlock(url, caption) { const isExternal = url.startsWith('http://') || url.startsWith('https://'); return { object: 'block', type: 'image', image: { type: isExternal ? 'external' : 'file', [isExternal ? 'external' : 'file']: { url: url, }, caption: caption !== null && caption !== undefined ? createRichText(caption) : [], }, }; } /** * Build an embed block */ export function buildEmbedBlock(url, caption) { return { object: 'block', type: 'embed', embed: { url, caption: caption !== null && caption !== undefined && caption.length > 0 ? createRichText(caption) : [], }, }; } /** * Build a bookmark block */ export function buildBookmarkBlock(url, caption) { return { object: 'block', type: 'bookmark', bookmark: { url, caption: caption !== null && caption !== undefined && caption.length > 0 ? createRichText(caption) : [], }, }; } /** * Utility function to determine appropriate list block type */ export function buildListItemBlock(content, listType, checked, children, formatting) { switch (listType) { case 'bulleted': return buildBulletedListItemBlock(content, children, formatting); case 'numbered': return buildNumberedListItemBlock(content, children, formatting); case 'todo': return buildToDoBlock(content, checked ?? false, children, formatting); default: return buildBulletedListItemBlock(content, children, formatting); } } /** * Utility function to normalize heading levels for Notion (max H3) */ export function normalizeHeadingLevel(level) { if (level <= 1) { return 1; } if (level === 2) { return 2; } return 3; // H4, H5, H6 all become H3 in Notion } /** * Create a fallback block for unsupported content */ export function buildFallbackBlock(content, originalType) { const fallbackContent = originalType !== null && originalType !== undefined && originalType.length > 0 ? `[Unsupported ${originalType}]: ${content}` : content; return buildParagraphBlock(fallbackContent, { color: 'gray' }); } //# sourceMappingURL=notion-blocks.js.map