n8n-nodes-notion-advanced 1.2.32-beta → 1.2.34-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1487,21 +1487,27 @@ class NotionAITool {
|
|
1487
1487
|
processed = processed.replace(/<\/?li\s*[^>]*>/gi, '');
|
1488
1488
|
// Remove any other common list-related fragments
|
1489
1489
|
processed = processed.replace(/<\/?[uo]l\s*[^>]*>/gi, '');
|
1490
|
-
//
|
1491
|
-
|
1490
|
+
// Convert inline HTML formatting to markdown BEFORE removing tags
|
1491
|
+
processed = NotionAITool.convertInlineHtmlToMarkdown(processed);
|
1492
|
+
// Final cleanup of any remaining unhandled tags
|
1492
1493
|
const result = processed
|
1493
|
-
.replace(/<[^>]*>/g, ' ') // Remove any remaining HTML tags
|
1494
1494
|
.replace(/\s+/g, ' ') // Clean up whitespace
|
1495
1495
|
.trim();
|
1496
1496
|
return result;
|
1497
1497
|
}
|
1498
1498
|
catch (error) {
|
1499
1499
|
console.warn('Error processing nested HTML in list item:', error);
|
1500
|
-
// Fallback:
|
1501
|
-
|
1502
|
-
.
|
1503
|
-
.replace(/\s+/g, ' ')
|
1504
|
-
|
1500
|
+
// Fallback: convert inline HTML then remove any remaining tags
|
1501
|
+
try {
|
1502
|
+
const fallback = NotionAITool.convertInlineHtmlToMarkdown(processed);
|
1503
|
+
return fallback.replace(/\s+/g, ' ').trim();
|
1504
|
+
}
|
1505
|
+
catch (fallbackError) {
|
1506
|
+
return processed
|
1507
|
+
.replace(/<[^>]*>/g, ' ')
|
1508
|
+
.replace(/\s+/g, ' ')
|
1509
|
+
.trim();
|
1510
|
+
}
|
1505
1511
|
}
|
1506
1512
|
}
|
1507
1513
|
// Helper function to convert inline HTML to markdown
|
@@ -1538,7 +1544,6 @@ class NotionAITool {
|
|
1538
1544
|
}
|
1539
1545
|
// Build hierarchy structure for lists using HierarchyNode approach
|
1540
1546
|
static buildListHierarchy(listContent, listType, childHierarchyNodes) {
|
1541
|
-
var _a;
|
1542
1547
|
try {
|
1543
1548
|
// Process each <li> element and build hierarchy
|
1544
1549
|
const listItems = NotionAITool.extractListItemsWithBranching(listContent);
|
@@ -1547,7 +1552,7 @@ class NotionAITool {
|
|
1547
1552
|
const listItemPositions = NotionAITool.getListItemPositions(listContent);
|
1548
1553
|
for (let i = 0; i < listItems.length; i++) {
|
1549
1554
|
const item = listItems[i];
|
1550
|
-
if (!item.text && !item.children.length)
|
1555
|
+
if (!item.text && !item.children.length && !(item.extractedChildBlocks && item.extractedChildBlocks.length > 0))
|
1551
1556
|
continue;
|
1552
1557
|
// Create list item block
|
1553
1558
|
const listItemBlock = {
|
@@ -1565,19 +1570,24 @@ class NotionAITool {
|
|
1565
1570
|
}
|
1566
1571
|
// Collect child hierarchy nodes for this list item based on position mapping
|
1567
1572
|
const itemChildNodes = [];
|
1568
|
-
//
|
1569
|
-
|
1570
|
-
|
1571
|
-
const
|
1572
|
-
for (const
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1573
|
+
// Add extracted child blocks (from XML tags within list items)
|
1574
|
+
// These are the primary source for child blocks in list items
|
1575
|
+
if (item.extractedChildBlocks && Array.isArray(item.extractedChildBlocks)) {
|
1576
|
+
const extractedBlocks = item.extractedChildBlocks;
|
1577
|
+
for (const block of extractedBlocks) {
|
1578
|
+
itemChildNodes.push({
|
1579
|
+
block,
|
1580
|
+
children: [],
|
1581
|
+
metadata: {
|
1582
|
+
sourcePosition: i,
|
1583
|
+
tagName: block.type
|
1584
|
+
}
|
1585
|
+
});
|
1579
1586
|
}
|
1580
1587
|
}
|
1588
|
+
// Note: We prioritize extractedChildBlocks over childHierarchyNodes for list items
|
1589
|
+
// since list-specific processing in extractListItemsWithBranching is more accurate
|
1590
|
+
// for handling child blocks within <li> elements
|
1581
1591
|
// Process nested list children (traditional nested lists)
|
1582
1592
|
if (item.children.length > 0) {
|
1583
1593
|
for (const child of item.children) {
|
@@ -1597,7 +1607,7 @@ class NotionAITool {
|
|
1597
1607
|
tagName: listType
|
1598
1608
|
}
|
1599
1609
|
};
|
1600
|
-
// Only add if it has content or children
|
1610
|
+
// Only add if it has content or children (including extracted child blocks)
|
1601
1611
|
const listData = listItemBlock[listType];
|
1602
1612
|
if ((listData.rich_text && listData.rich_text.length > 0) || itemChildNodes.length > 0) {
|
1603
1613
|
listItemHierarchyNodes.push(listItemHierarchyNode);
|
@@ -1824,13 +1834,198 @@ class NotionAITool {
|
|
1824
1834
|
continue;
|
1825
1835
|
}
|
1826
1836
|
const item = { text: '', children: [] };
|
1827
|
-
//
|
1837
|
+
// STEP 1: Extract and convert child block XML to actual blocks before removing from parent text
|
1838
|
+
let parentTextContent = fullItemContent;
|
1839
|
+
const extractedChildBlocks = [];
|
1840
|
+
// List of XML tags that create child blocks (these should be removed from parent text)
|
1841
|
+
const childBlockTags = [
|
1842
|
+
'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
1843
|
+
'blockquote', 'quote', 'callout', 'code', 'pre',
|
1844
|
+
'todo', 'toggle', 'image', 'embed', 'bookmark',
|
1845
|
+
'equation', 'divider'
|
1846
|
+
];
|
1847
|
+
// Extract child blocks first, then remove from parent text
|
1848
|
+
childBlockTags.forEach(tag => {
|
1849
|
+
// Extract and process paired tags first
|
1850
|
+
const pairedRegex = new RegExp(`<${tag}[^>]*>(.*?)<\\/${tag}>`, 'gis');
|
1851
|
+
let match;
|
1852
|
+
while ((match = pairedRegex.exec(parentTextContent)) !== null) {
|
1853
|
+
const fullMatch = match[0];
|
1854
|
+
const content = match[1];
|
1855
|
+
// Create child block based on tag type
|
1856
|
+
try {
|
1857
|
+
let childBlock = null;
|
1858
|
+
switch (tag) {
|
1859
|
+
case 'embed':
|
1860
|
+
childBlock = {
|
1861
|
+
type: 'embed',
|
1862
|
+
embed: { url: content.trim() }
|
1863
|
+
};
|
1864
|
+
break;
|
1865
|
+
case 'bookmark':
|
1866
|
+
childBlock = {
|
1867
|
+
type: 'bookmark',
|
1868
|
+
bookmark: { url: content.trim() }
|
1869
|
+
};
|
1870
|
+
break;
|
1871
|
+
case 'p':
|
1872
|
+
const markdownContent = NotionAITool.convertInlineHtmlToMarkdown(content.trim());
|
1873
|
+
childBlock = {
|
1874
|
+
type: 'paragraph',
|
1875
|
+
paragraph: { rich_text: NotionAITool.parseBasicMarkdown(markdownContent) }
|
1876
|
+
};
|
1877
|
+
break;
|
1878
|
+
case 'h1':
|
1879
|
+
case 'h2':
|
1880
|
+
case 'h3':
|
1881
|
+
const headingType = `heading_${tag.charAt(1)}`;
|
1882
|
+
childBlock = {
|
1883
|
+
type: headingType,
|
1884
|
+
[headingType]: { rich_text: [{ type: 'text', text: { content: content.trim() } }] }
|
1885
|
+
};
|
1886
|
+
break;
|
1887
|
+
case 'quote':
|
1888
|
+
case 'blockquote':
|
1889
|
+
childBlock = {
|
1890
|
+
type: 'quote',
|
1891
|
+
quote: { rich_text: NotionAITool.parseBasicMarkdown(content.trim()) }
|
1892
|
+
};
|
1893
|
+
break;
|
1894
|
+
case 'callout':
|
1895
|
+
// Extract type attribute if present
|
1896
|
+
const typeMatch = fullMatch.match(/type="([^"]*)"/);
|
1897
|
+
const calloutType = typeMatch ? typeMatch[1] : 'info';
|
1898
|
+
const emoji = NotionAITool.getCalloutEmoji(calloutType.toLowerCase());
|
1899
|
+
const color = NotionAITool.getCalloutColor(calloutType.toLowerCase());
|
1900
|
+
childBlock = {
|
1901
|
+
type: 'callout',
|
1902
|
+
callout: {
|
1903
|
+
rich_text: NotionAITool.parseBasicMarkdown(content.trim()),
|
1904
|
+
icon: { type: 'emoji', emoji },
|
1905
|
+
color: color
|
1906
|
+
}
|
1907
|
+
};
|
1908
|
+
break;
|
1909
|
+
case 'code':
|
1910
|
+
case 'pre':
|
1911
|
+
// Extract language attribute if present
|
1912
|
+
const langMatch = fullMatch.match(/language="([^"]*)"/);
|
1913
|
+
const language = langMatch ? langMatch[1] : 'plain_text';
|
1914
|
+
childBlock = {
|
1915
|
+
type: 'code',
|
1916
|
+
code: {
|
1917
|
+
rich_text: [{ type: 'text', text: { content: content.trim() } }],
|
1918
|
+
language: language === 'plain text' ? 'plain_text' : language
|
1919
|
+
}
|
1920
|
+
};
|
1921
|
+
break;
|
1922
|
+
case 'todo':
|
1923
|
+
// Extract checked attribute if present
|
1924
|
+
const checkedMatch = fullMatch.match(/checked="([^"]*)"/);
|
1925
|
+
const isChecked = checkedMatch ? checkedMatch[1].toLowerCase() === 'true' : false;
|
1926
|
+
childBlock = {
|
1927
|
+
type: 'to_do',
|
1928
|
+
to_do: {
|
1929
|
+
rich_text: NotionAITool.parseBasicMarkdown(content.trim()),
|
1930
|
+
checked: isChecked
|
1931
|
+
}
|
1932
|
+
};
|
1933
|
+
break;
|
1934
|
+
case 'toggle':
|
1935
|
+
childBlock = {
|
1936
|
+
type: 'toggle',
|
1937
|
+
toggle: {
|
1938
|
+
rich_text: NotionAITool.parseBasicMarkdown(content.trim()),
|
1939
|
+
children: []
|
1940
|
+
}
|
1941
|
+
};
|
1942
|
+
break;
|
1943
|
+
case 'equation':
|
1944
|
+
childBlock = {
|
1945
|
+
type: 'equation',
|
1946
|
+
equation: { expression: content.trim() }
|
1947
|
+
};
|
1948
|
+
break;
|
1949
|
+
case 'image':
|
1950
|
+
// Extract src and alt attributes
|
1951
|
+
const srcMatch = fullMatch.match(/src="([^"]*)"/);
|
1952
|
+
const altMatch = fullMatch.match(/alt="([^"]*)"/);
|
1953
|
+
const src = srcMatch ? srcMatch[1] : '';
|
1954
|
+
const alt = altMatch ? altMatch[1] : '';
|
1955
|
+
const caption = content.trim() || alt;
|
1956
|
+
childBlock = {
|
1957
|
+
type: 'image',
|
1958
|
+
image: {
|
1959
|
+
type: 'external',
|
1960
|
+
external: { url: src },
|
1961
|
+
caption: caption ? NotionAITool.parseBasicMarkdown(caption) : []
|
1962
|
+
}
|
1963
|
+
};
|
1964
|
+
break;
|
1965
|
+
case 'divider':
|
1966
|
+
childBlock = {
|
1967
|
+
type: 'divider',
|
1968
|
+
divider: {}
|
1969
|
+
};
|
1970
|
+
break;
|
1971
|
+
}
|
1972
|
+
if (childBlock) {
|
1973
|
+
extractedChildBlocks.push(childBlock);
|
1974
|
+
}
|
1975
|
+
}
|
1976
|
+
catch (error) {
|
1977
|
+
console.warn(`Error creating child block for tag ${tag}:`, error);
|
1978
|
+
}
|
1979
|
+
}
|
1980
|
+
// Handle self-closing tags (mainly for divider, image)
|
1981
|
+
const selfClosingRegex = new RegExp(`<${tag}[^>]*\\/>`, 'gis');
|
1982
|
+
let selfMatch;
|
1983
|
+
while ((selfMatch = selfClosingRegex.exec(parentTextContent)) !== null) {
|
1984
|
+
const fullMatch = selfMatch[0];
|
1985
|
+
try {
|
1986
|
+
let childBlock = null;
|
1987
|
+
if (tag === 'divider') {
|
1988
|
+
childBlock = {
|
1989
|
+
type: 'divider',
|
1990
|
+
divider: {}
|
1991
|
+
};
|
1992
|
+
}
|
1993
|
+
else if (tag === 'image') {
|
1994
|
+
// Extract src and alt attributes
|
1995
|
+
const srcMatch = fullMatch.match(/src="([^"]*)"/);
|
1996
|
+
const altMatch = fullMatch.match(/alt="([^"]*)"/);
|
1997
|
+
const src = srcMatch ? srcMatch[1] : '';
|
1998
|
+
const alt = altMatch ? altMatch[1] : '';
|
1999
|
+
childBlock = {
|
2000
|
+
type: 'image',
|
2001
|
+
image: {
|
2002
|
+
type: 'external',
|
2003
|
+
external: { url: src },
|
2004
|
+
caption: alt ? NotionAITool.parseBasicMarkdown(alt) : []
|
2005
|
+
}
|
2006
|
+
};
|
2007
|
+
}
|
2008
|
+
if (childBlock) {
|
2009
|
+
extractedChildBlocks.push(childBlock);
|
2010
|
+
}
|
2011
|
+
}
|
2012
|
+
catch (error) {
|
2013
|
+
console.warn(`Error creating self-closing child block for tag ${tag}:`, error);
|
2014
|
+
}
|
2015
|
+
}
|
2016
|
+
// Now remove both paired and self-closing tags from parent text
|
2017
|
+
parentTextContent = parentTextContent.replace(pairedRegex, '');
|
2018
|
+
parentTextContent = parentTextContent.replace(selfClosingRegex, '');
|
2019
|
+
});
|
2020
|
+
// Store extracted child blocks in the item for later use
|
2021
|
+
item.extractedChildBlocks = extractedChildBlocks;
|
2022
|
+
// STEP 2: Process the content to separate remaining text from nested lists
|
1828
2023
|
let contentPos = 0;
|
1829
2024
|
let textParts = [];
|
1830
|
-
while (contentPos <
|
2025
|
+
while (contentPos < parentTextContent.length) {
|
1831
2026
|
// Look for the next nested list (ul or ol)
|
1832
|
-
const nextUlStart =
|
1833
|
-
const nextOlStart =
|
2027
|
+
const nextUlStart = parentTextContent.indexOf('<ul', contentPos);
|
2028
|
+
const nextOlStart = parentTextContent.indexOf('<ol', contentPos);
|
1834
2029
|
let nextListStart = -1;
|
1835
2030
|
let listType = '';
|
1836
2031
|
if (nextUlStart !== -1 && (nextOlStart === -1 || nextUlStart < nextOlStart)) {
|
@@ -1843,22 +2038,22 @@ class NotionAITool {
|
|
1843
2038
|
}
|
1844
2039
|
if (nextListStart === -1) {
|
1845
2040
|
// No more nested lists - add remaining text
|
1846
|
-
const remainingText =
|
2041
|
+
const remainingText = parentTextContent.substring(contentPos);
|
1847
2042
|
if (remainingText.trim()) {
|
1848
2043
|
textParts.push(remainingText);
|
1849
2044
|
}
|
1850
2045
|
break;
|
1851
2046
|
}
|
1852
2047
|
// Add text before the nested list
|
1853
|
-
const textBefore =
|
2048
|
+
const textBefore = parentTextContent.substring(contentPos, nextListStart);
|
1854
2049
|
if (textBefore.trim()) {
|
1855
2050
|
textParts.push(textBefore);
|
1856
2051
|
}
|
1857
2052
|
// Find the end of this nested list
|
1858
|
-
const listOpenEnd =
|
2053
|
+
const listOpenEnd = parentTextContent.indexOf('>', nextListStart);
|
1859
2054
|
if (listOpenEnd === -1) {
|
1860
2055
|
// Malformed list tag
|
1861
|
-
textParts.push(
|
2056
|
+
textParts.push(parentTextContent.substring(contentPos));
|
1862
2057
|
break;
|
1863
2058
|
}
|
1864
2059
|
// Track depth to find the matching closing tag
|
@@ -1867,9 +2062,9 @@ class NotionAITool {
|
|
1867
2062
|
let listEnd = -1;
|
1868
2063
|
const openTag = `<${listType}`;
|
1869
2064
|
const closeTag = `</${listType}>`;
|
1870
|
-
while (listSearchPos <
|
1871
|
-
const nextListOpen =
|
1872
|
-
const nextListClose =
|
2065
|
+
while (listSearchPos < parentTextContent.length && listDepth > 0) {
|
2066
|
+
const nextListOpen = parentTextContent.indexOf(openTag, listSearchPos);
|
2067
|
+
const nextListClose = parentTextContent.indexOf(closeTag, listSearchPos);
|
1873
2068
|
if (nextListClose === -1)
|
1874
2069
|
break;
|
1875
2070
|
if (nextListOpen !== -1 && nextListOpen < nextListClose) {
|
@@ -1886,7 +2081,8 @@ class NotionAITool {
|
|
1886
2081
|
}
|
1887
2082
|
}
|
1888
2083
|
if (listEnd !== -1) {
|
1889
|
-
// Extract the content between <ul>/<ol> and </ul>/<ol>
|
2084
|
+
// Extract the content between <ul>/<ol> and </ul>/<ol> from ORIGINAL content
|
2085
|
+
// (not parentTextContent which has child blocks removed)
|
1890
2086
|
const listContent = fullItemContent.substring(listOpenEnd + 1, listEnd - closeTag.length);
|
1891
2087
|
item.children.push({
|
1892
2088
|
type: listType,
|
@@ -1896,7 +2092,7 @@ class NotionAITool {
|
|
1896
2092
|
}
|
1897
2093
|
else {
|
1898
2094
|
// Malformed nested list - treat remaining as text
|
1899
|
-
textParts.push(
|
2095
|
+
textParts.push(parentTextContent.substring(contentPos));
|
1900
2096
|
break;
|
1901
2097
|
}
|
1902
2098
|
}
|
@@ -1908,8 +2104,8 @@ class NotionAITool {
|
|
1908
2104
|
item.text = cleanText;
|
1909
2105
|
}
|
1910
2106
|
}
|
1911
|
-
// Only add items that have either text or
|
1912
|
-
if (item.text.trim() || item.children.length > 0) {
|
2107
|
+
// Only add items that have either text, children, or extracted child blocks
|
2108
|
+
if (item.text.trim() || item.children.length > 0 || (item.extractedChildBlocks && item.extractedChildBlocks.length > 0)) {
|
1913
2109
|
items.push(item);
|
1914
2110
|
}
|
1915
2111
|
pos = liEnd + 5; // Move past </li>
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "n8n-nodes-notion-advanced",
|
3
|
-
"version": "1.2.
|
3
|
+
"version": "1.2.34-beta",
|
4
4
|
"description": "Advanced n8n Notion nodes: Full-featured workflow node + AI Agent Tool for intelligent Notion automation with 25+ block types (BETA)",
|
5
5
|
"scripts": {},
|
6
6
|
"files": [
|