n8n-nodes-notion-advanced 1.1.32-beta → 1.2.1-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.
@@ -9,5 +9,9 @@ export declare class NotionAITool implements INodeType {
|
|
9
9
|
static createDatabaseEntry(executeFunctions: IExecuteFunctions, itemIndex: number): Promise<IDataObject>;
|
10
10
|
static queryDatabase(executeFunctions: IExecuteFunctions, itemIndex: number): Promise<IDataObject>;
|
11
11
|
static parseContentToBlocks(content: string): IDataObject[];
|
12
|
+
static processXmlTags(content: string, blocks: IDataObject[]): string;
|
13
|
+
static getCalloutEmoji(type: string): string;
|
14
|
+
static getCalloutColor(type: string): string;
|
15
|
+
static parseBasicMarkdown(text: string): IDataObject[];
|
12
16
|
static parsePropertiesToUpdate(propertiesString: string): IDataObject;
|
13
17
|
}
|
@@ -446,96 +446,651 @@ class NotionAITool {
|
|
446
446
|
}
|
447
447
|
static parseContentToBlocks(content) {
|
448
448
|
const blocks = [];
|
449
|
-
|
449
|
+
// Handle both actual newlines and escaped \n characters
|
450
|
+
const normalizedContent = content.replace(/\\n/g, '\n');
|
451
|
+
// First, process XML-like tags for reliable parsing
|
452
|
+
const processedContent = NotionAITool.processXmlTags(normalizedContent, blocks);
|
453
|
+
// Then process remaining content with traditional markdown patterns
|
454
|
+
const lines = processedContent.split('\n');
|
450
455
|
for (let i = 0; i < lines.length; i++) {
|
451
|
-
const line = lines[i]
|
452
|
-
|
456
|
+
const line = lines[i];
|
457
|
+
const trimmedLine = line.trim();
|
458
|
+
// Skip completely empty lines and XML placeholders
|
459
|
+
if (!trimmedLine || trimmedLine.startsWith('__XML_BLOCK_'))
|
453
460
|
continue;
|
454
|
-
//
|
455
|
-
if (
|
461
|
+
// Traditional markdown patterns (for backwards compatibility)
|
462
|
+
if (trimmedLine.startsWith('# ')) {
|
456
463
|
blocks.push({
|
457
|
-
object: 'block',
|
458
464
|
type: 'heading_1',
|
459
465
|
heading_1: {
|
460
|
-
rich_text: [(0, NotionUtils_1.createRichText)(
|
466
|
+
rich_text: [(0, NotionUtils_1.createRichText)(trimmedLine.substring(2).trim())],
|
461
467
|
},
|
462
468
|
});
|
463
469
|
}
|
464
|
-
else if (
|
470
|
+
else if (trimmedLine.startsWith('## ')) {
|
465
471
|
blocks.push({
|
466
|
-
object: 'block',
|
467
472
|
type: 'heading_2',
|
468
473
|
heading_2: {
|
469
|
-
rich_text: [(0, NotionUtils_1.createRichText)(
|
474
|
+
rich_text: [(0, NotionUtils_1.createRichText)(trimmedLine.substring(3).trim())],
|
470
475
|
},
|
471
476
|
});
|
472
477
|
}
|
473
|
-
else if (
|
478
|
+
else if (trimmedLine.startsWith('### ')) {
|
474
479
|
blocks.push({
|
475
|
-
object: 'block',
|
476
480
|
type: 'heading_3',
|
477
481
|
heading_3: {
|
478
|
-
rich_text: [(0, NotionUtils_1.createRichText)(
|
482
|
+
rich_text: [(0, NotionUtils_1.createRichText)(trimmedLine.substring(4).trim())],
|
479
483
|
},
|
480
484
|
});
|
481
485
|
}
|
482
|
-
else if (
|
486
|
+
else if (trimmedLine === '---' || trimmedLine === '***') {
|
487
|
+
blocks.push({
|
488
|
+
type: 'divider',
|
489
|
+
divider: {},
|
490
|
+
});
|
491
|
+
}
|
492
|
+
else if (trimmedLine.includes('[!') && trimmedLine.startsWith('>')) {
|
493
|
+
// Callout blocks: > [!info] content
|
494
|
+
const calloutMatch = trimmedLine.match(/^>\s*\[!(\w+)\]\s*(.*)/i);
|
495
|
+
if (calloutMatch) {
|
496
|
+
const [, calloutType, text] = calloutMatch;
|
497
|
+
const emoji = NotionAITool.getCalloutEmoji(calloutType.toLowerCase());
|
498
|
+
const color = NotionAITool.getCalloutColor(calloutType.toLowerCase());
|
499
|
+
blocks.push({
|
500
|
+
type: 'callout',
|
501
|
+
callout: {
|
502
|
+
rich_text: NotionAITool.parseBasicMarkdown(text),
|
503
|
+
icon: { type: 'emoji', emoji },
|
504
|
+
color: color,
|
505
|
+
},
|
506
|
+
});
|
507
|
+
}
|
508
|
+
else {
|
509
|
+
blocks.push({
|
510
|
+
type: 'quote',
|
511
|
+
quote: {
|
512
|
+
rich_text: NotionAITool.parseBasicMarkdown(trimmedLine.substring(1).trim()),
|
513
|
+
},
|
514
|
+
});
|
515
|
+
}
|
516
|
+
}
|
517
|
+
else if (trimmedLine.startsWith(' && trimmedLine.endsWith(')')) {
|
518
|
+
// Image: 
|
519
|
+
const match = trimmedLine.match(/^!\[(.*?)\]\((.*?)\)$/);
|
520
|
+
if (match) {
|
521
|
+
const [, altText, url] = match;
|
522
|
+
blocks.push({
|
523
|
+
type: 'image',
|
524
|
+
image: {
|
525
|
+
type: 'external',
|
526
|
+
external: { url },
|
527
|
+
caption: altText ? NotionAITool.parseBasicMarkdown(altText) : [],
|
528
|
+
},
|
529
|
+
});
|
530
|
+
}
|
531
|
+
}
|
532
|
+
else if (trimmedLine.startsWith('$$') && trimmedLine.endsWith('$$') && trimmedLine.length > 4) {
|
533
|
+
// Equation: $$equation$$
|
534
|
+
const equation = trimmedLine.substring(2, trimmedLine.length - 2).trim();
|
535
|
+
blocks.push({
|
536
|
+
type: 'equation',
|
537
|
+
equation: {
|
538
|
+
expression: equation,
|
539
|
+
},
|
540
|
+
});
|
541
|
+
}
|
542
|
+
else if ((trimmedLine.startsWith('http://') || trimmedLine.startsWith('https://')) && !trimmedLine.includes(' ')) {
|
543
|
+
// Check if it's a video URL for embed, otherwise bookmark
|
544
|
+
const videoPatterns = [
|
545
|
+
/(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)/i,
|
546
|
+
/(?:https?:\/\/)?(?:www\.)?(?:vimeo\.com\/)/i,
|
547
|
+
/(?:https?:\/\/)?(?:www\.)?(?:dailymotion\.com\/video\/)/i,
|
548
|
+
/(?:https?:\/\/)?(?:www\.)?(?:twitch\.tv\/)/i,
|
549
|
+
/(?:https?:\/\/)?(?:www\.)?(?:loom\.com\/share\/)/i,
|
550
|
+
/(?:https?:\/\/)?(?:www\.)?(?:figma\.com\/)/i,
|
551
|
+
/(?:https?:\/\/)?(?:www\.)?(?:miro\.com\/)/i,
|
552
|
+
/(?:https?:\/\/)?(?:codepen\.io\/)/i
|
553
|
+
];
|
554
|
+
const isEmbeddableUrl = videoPatterns.some(pattern => pattern.test(trimmedLine));
|
555
|
+
if (isEmbeddableUrl) {
|
556
|
+
blocks.push({
|
557
|
+
type: 'embed',
|
558
|
+
embed: {
|
559
|
+
url: trimmedLine,
|
560
|
+
},
|
561
|
+
});
|
562
|
+
}
|
563
|
+
else {
|
564
|
+
blocks.push({
|
565
|
+
type: 'bookmark',
|
566
|
+
bookmark: {
|
567
|
+
url: trimmedLine,
|
568
|
+
},
|
569
|
+
});
|
570
|
+
}
|
571
|
+
}
|
572
|
+
else if (trimmedLine.startsWith('- [') && (trimmedLine.includes('[ ]') || trimmedLine.includes('[x]') || trimmedLine.includes('[X]'))) {
|
573
|
+
// To-do list items: - [ ] or - [x] or - [X]
|
574
|
+
const isChecked = trimmedLine.includes('[x]') || trimmedLine.includes('[X]');
|
575
|
+
const text = trimmedLine.replace(/^-\s*\[[ xX]\]\s*/, '').trim();
|
576
|
+
blocks.push({
|
577
|
+
type: 'to_do',
|
578
|
+
to_do: {
|
579
|
+
rich_text: NotionAITool.parseBasicMarkdown(text),
|
580
|
+
checked: isChecked,
|
581
|
+
},
|
582
|
+
});
|
583
|
+
}
|
584
|
+
else if (trimmedLine.startsWith('- ') && !trimmedLine.startsWith('- [')) {
|
585
|
+
// Bullet list items: - item (but not todos)
|
586
|
+
const listText = trimmedLine.substring(2).trim();
|
483
587
|
blocks.push({
|
484
|
-
object: 'block',
|
485
588
|
type: 'bulleted_list_item',
|
486
589
|
bulleted_list_item: {
|
487
|
-
rich_text:
|
590
|
+
rich_text: NotionAITool.parseBasicMarkdown(listText),
|
488
591
|
},
|
489
592
|
});
|
490
593
|
}
|
491
|
-
else if (
|
594
|
+
else if (/^\d+\.\s/.test(trimmedLine)) {
|
595
|
+
// Numbered list items: 1. item
|
596
|
+
const listText = trimmedLine.replace(/^\d+\.\s/, '').trim();
|
492
597
|
blocks.push({
|
493
|
-
object: 'block',
|
494
598
|
type: 'numbered_list_item',
|
495
599
|
numbered_list_item: {
|
496
|
-
rich_text:
|
600
|
+
rich_text: NotionAITool.parseBasicMarkdown(listText),
|
497
601
|
},
|
498
602
|
});
|
499
603
|
}
|
500
|
-
else if (
|
604
|
+
else if (trimmedLine.startsWith('> ') && !trimmedLine.includes('[!')) {
|
605
|
+
// Quote block (but not callout)
|
501
606
|
blocks.push({
|
502
|
-
object: 'block',
|
503
607
|
type: 'quote',
|
504
608
|
quote: {
|
505
|
-
rich_text:
|
609
|
+
rich_text: NotionAITool.parseBasicMarkdown(trimmedLine.substring(2).trim()),
|
506
610
|
},
|
507
611
|
});
|
508
612
|
}
|
509
|
-
else if (
|
613
|
+
else if (trimmedLine.startsWith('```')) {
|
510
614
|
// Handle code blocks
|
615
|
+
const language = trimmedLine.substring(3).trim() || 'plain text';
|
511
616
|
const codeLines = [];
|
512
617
|
i++; // Skip the opening ```
|
618
|
+
// Collect all code lines until closing ```
|
513
619
|
while (i < lines.length && !lines[i].trim().startsWith('```')) {
|
514
620
|
codeLines.push(lines[i]);
|
515
621
|
i++;
|
516
622
|
}
|
517
623
|
blocks.push({
|
518
|
-
object: 'block',
|
519
624
|
type: 'code',
|
520
625
|
code: {
|
521
626
|
rich_text: [(0, NotionUtils_1.createRichText)(codeLines.join('\n'))],
|
522
|
-
language: 'plain text',
|
627
|
+
language: language === 'plain text' ? 'plain_text' : language,
|
523
628
|
},
|
524
629
|
});
|
525
630
|
}
|
526
631
|
else {
|
527
|
-
// Regular paragraph
|
632
|
+
// Regular paragraph - handle basic markdown formatting
|
633
|
+
const richText = NotionAITool.parseBasicMarkdown(trimmedLine);
|
528
634
|
blocks.push({
|
529
|
-
object: 'block',
|
530
635
|
type: 'paragraph',
|
531
636
|
paragraph: {
|
532
|
-
rich_text:
|
637
|
+
rich_text: richText,
|
533
638
|
},
|
534
639
|
});
|
535
640
|
}
|
536
641
|
}
|
537
642
|
return blocks;
|
538
643
|
}
|
644
|
+
// New XML-like tag processing function
|
645
|
+
static processXmlTags(content, blocks) {
|
646
|
+
let processedContent = content;
|
647
|
+
let blockCounter = 0;
|
648
|
+
// Process XML-like tags in order of priority
|
649
|
+
const tagProcessors = [
|
650
|
+
// Callouts: <callout type="info">content</callout>
|
651
|
+
{
|
652
|
+
regex: /<callout\s*(?:type="([^"]*)")?\s*>(.*?)<\/callout>/gis,
|
653
|
+
processor: (match, type = 'info', content) => {
|
654
|
+
const emoji = NotionAITool.getCalloutEmoji(type.toLowerCase());
|
655
|
+
const color = NotionAITool.getCalloutColor(type.toLowerCase());
|
656
|
+
blocks.push({
|
657
|
+
type: 'callout',
|
658
|
+
callout: {
|
659
|
+
rich_text: NotionAITool.parseBasicMarkdown(content.trim()),
|
660
|
+
icon: { type: 'emoji', emoji },
|
661
|
+
color: color,
|
662
|
+
},
|
663
|
+
});
|
664
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
665
|
+
}
|
666
|
+
},
|
667
|
+
// Code blocks: <code language="javascript">content</code>
|
668
|
+
{
|
669
|
+
regex: /<code\s*(?:language="([^"]*)")?\s*>(.*?)<\/code>/gis,
|
670
|
+
processor: (match, language = 'plain_text', content) => {
|
671
|
+
blocks.push({
|
672
|
+
type: 'code',
|
673
|
+
code: {
|
674
|
+
rich_text: [(0, NotionUtils_1.createRichText)(content.trim())],
|
675
|
+
language: language === 'plain text' ? 'plain_text' : language,
|
676
|
+
},
|
677
|
+
});
|
678
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
679
|
+
}
|
680
|
+
},
|
681
|
+
// Images: <image src="url" alt="description">caption</image>
|
682
|
+
{
|
683
|
+
regex: /<image\s+src="([^"]*)"(?:\s+alt="([^"]*)")?\s*>(.*?)<\/image>/gis,
|
684
|
+
processor: (match, src, alt = '', caption = '') => {
|
685
|
+
const captionText = caption.trim() || alt;
|
686
|
+
blocks.push({
|
687
|
+
type: 'image',
|
688
|
+
image: {
|
689
|
+
type: 'external',
|
690
|
+
external: { url: src },
|
691
|
+
caption: captionText ? NotionAITool.parseBasicMarkdown(captionText) : [],
|
692
|
+
},
|
693
|
+
});
|
694
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
695
|
+
}
|
696
|
+
},
|
697
|
+
// Self-closing images: <image src="url" alt="description"/>
|
698
|
+
{
|
699
|
+
regex: /<image\s+src="([^"]*)"(?:\s+alt="([^"]*)")?\s*\/>/gis,
|
700
|
+
processor: (match, src, alt = '') => {
|
701
|
+
blocks.push({
|
702
|
+
type: 'image',
|
703
|
+
image: {
|
704
|
+
type: 'external',
|
705
|
+
external: { url: src },
|
706
|
+
caption: alt ? NotionAITool.parseBasicMarkdown(alt) : [],
|
707
|
+
},
|
708
|
+
});
|
709
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
710
|
+
}
|
711
|
+
},
|
712
|
+
// Equations: <equation>E=mc^2</equation>
|
713
|
+
{
|
714
|
+
regex: /<equation>(.*?)<\/equation>/gis,
|
715
|
+
processor: (match, expression) => {
|
716
|
+
blocks.push({
|
717
|
+
type: 'equation',
|
718
|
+
equation: {
|
719
|
+
expression: expression.trim(),
|
720
|
+
},
|
721
|
+
});
|
722
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
723
|
+
}
|
724
|
+
},
|
725
|
+
// Embeds: <embed>url</embed>
|
726
|
+
{
|
727
|
+
regex: /<embed>(.*?)<\/embed>/gis,
|
728
|
+
processor: (match, url) => {
|
729
|
+
blocks.push({
|
730
|
+
type: 'embed',
|
731
|
+
embed: {
|
732
|
+
url: url.trim(),
|
733
|
+
},
|
734
|
+
});
|
735
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
736
|
+
}
|
737
|
+
},
|
738
|
+
// Bookmarks: <bookmark>url</bookmark>
|
739
|
+
{
|
740
|
+
regex: /<bookmark>(.*?)<\/bookmark>/gis,
|
741
|
+
processor: (match, url) => {
|
742
|
+
blocks.push({
|
743
|
+
type: 'bookmark',
|
744
|
+
bookmark: {
|
745
|
+
url: url.trim(),
|
746
|
+
},
|
747
|
+
});
|
748
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
749
|
+
}
|
750
|
+
},
|
751
|
+
// Toggles: <toggle>title</toggle>
|
752
|
+
{
|
753
|
+
regex: /<toggle>(.*?)<\/toggle>/gis,
|
754
|
+
processor: (match, title) => {
|
755
|
+
blocks.push({
|
756
|
+
type: 'toggle',
|
757
|
+
toggle: {
|
758
|
+
rich_text: NotionAITool.parseBasicMarkdown(title.trim()),
|
759
|
+
children: [],
|
760
|
+
},
|
761
|
+
});
|
762
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
763
|
+
}
|
764
|
+
},
|
765
|
+
// Quotes: <quote>content</quote>
|
766
|
+
{
|
767
|
+
regex: /<quote>(.*?)<\/quote>/gis,
|
768
|
+
processor: (match, content) => {
|
769
|
+
blocks.push({
|
770
|
+
type: 'quote',
|
771
|
+
quote: {
|
772
|
+
rich_text: NotionAITool.parseBasicMarkdown(content.trim()),
|
773
|
+
},
|
774
|
+
});
|
775
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
776
|
+
}
|
777
|
+
},
|
778
|
+
// Dividers: <divider/> or <divider></divider>
|
779
|
+
{
|
780
|
+
regex: /<divider\s*\/?>/gis,
|
781
|
+
processor: (match) => {
|
782
|
+
blocks.push({
|
783
|
+
type: 'divider',
|
784
|
+
divider: {},
|
785
|
+
});
|
786
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
787
|
+
}
|
788
|
+
},
|
789
|
+
// To-do items: <todo checked="true">content</todo>
|
790
|
+
{
|
791
|
+
regex: /<todo\s*(?:checked="([^"]*)")?\s*>(.*?)<\/todo>/gis,
|
792
|
+
processor: (match, checked = 'false', content) => {
|
793
|
+
const isChecked = checked.toLowerCase() === 'true';
|
794
|
+
blocks.push({
|
795
|
+
type: 'to_do',
|
796
|
+
to_do: {
|
797
|
+
rich_text: NotionAITool.parseBasicMarkdown(content.trim()),
|
798
|
+
checked: isChecked,
|
799
|
+
},
|
800
|
+
});
|
801
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
802
|
+
}
|
803
|
+
},
|
804
|
+
// Headings: <h1>content</h1>, <h2>content</h2>, <h3>content</h3>
|
805
|
+
{
|
806
|
+
regex: /<h([123])>(.*?)<\/h[123]>/gis,
|
807
|
+
processor: (match, level, content) => {
|
808
|
+
const headingType = `heading_${level}`;
|
809
|
+
blocks.push({
|
810
|
+
type: headingType,
|
811
|
+
[headingType]: {
|
812
|
+
rich_text: [(0, NotionUtils_1.createRichText)(content.trim())],
|
813
|
+
},
|
814
|
+
});
|
815
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
816
|
+
}
|
817
|
+
},
|
818
|
+
// Paragraphs: <p>content</p>
|
819
|
+
{
|
820
|
+
regex: /<p>(.*?)<\/p>/gis,
|
821
|
+
processor: (match, content) => {
|
822
|
+
blocks.push({
|
823
|
+
type: 'paragraph',
|
824
|
+
paragraph: {
|
825
|
+
rich_text: NotionAITool.parseBasicMarkdown(content.trim()),
|
826
|
+
},
|
827
|
+
});
|
828
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
829
|
+
}
|
830
|
+
},
|
831
|
+
// Bulleted lists: <ul><li>item</li></ul>
|
832
|
+
{
|
833
|
+
regex: /<ul>(.*?)<\/ul>/gis,
|
834
|
+
processor: (match, listContent) => {
|
835
|
+
// Extract individual list items
|
836
|
+
const items = listContent.match(/<li>(.*?)<\/li>/gis) || [];
|
837
|
+
items.forEach(item => {
|
838
|
+
const itemContent = item.replace(/<\/?li>/gi, '').trim();
|
839
|
+
blocks.push({
|
840
|
+
type: 'bulleted_list_item',
|
841
|
+
bulleted_list_item: {
|
842
|
+
rich_text: NotionAITool.parseBasicMarkdown(itemContent),
|
843
|
+
},
|
844
|
+
});
|
845
|
+
});
|
846
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
847
|
+
}
|
848
|
+
},
|
849
|
+
// Numbered lists: <ol><li>item</li></ol>
|
850
|
+
{
|
851
|
+
regex: /<ol>(.*?)<\/ol>/gis,
|
852
|
+
processor: (match, listContent) => {
|
853
|
+
// Extract individual list items
|
854
|
+
const items = listContent.match(/<li>(.*?)<\/li>/gis) || [];
|
855
|
+
items.forEach(item => {
|
856
|
+
const itemContent = item.replace(/<\/?li>/gi, '').trim();
|
857
|
+
blocks.push({
|
858
|
+
type: 'numbered_list_item',
|
859
|
+
numbered_list_item: {
|
860
|
+
rich_text: NotionAITool.parseBasicMarkdown(itemContent),
|
861
|
+
},
|
862
|
+
});
|
863
|
+
});
|
864
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
865
|
+
}
|
866
|
+
},
|
867
|
+
// Standalone list items: <li>content</li>
|
868
|
+
{
|
869
|
+
regex: /<li>(.*?)<\/li>/gis,
|
870
|
+
processor: (match, content) => {
|
871
|
+
blocks.push({
|
872
|
+
type: 'bulleted_list_item',
|
873
|
+
bulleted_list_item: {
|
874
|
+
rich_text: NotionAITool.parseBasicMarkdown(content.trim()),
|
875
|
+
},
|
876
|
+
});
|
877
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
878
|
+
}
|
879
|
+
},
|
880
|
+
// Blockquotes: <blockquote>content</blockquote>
|
881
|
+
{
|
882
|
+
regex: /<blockquote>(.*?)<\/blockquote>/gis,
|
883
|
+
processor: (match, content) => {
|
884
|
+
blocks.push({
|
885
|
+
type: 'quote',
|
886
|
+
quote: {
|
887
|
+
rich_text: NotionAITool.parseBasicMarkdown(content.trim()),
|
888
|
+
},
|
889
|
+
});
|
890
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
891
|
+
}
|
892
|
+
},
|
893
|
+
// Preformatted text: <pre>content</pre>
|
894
|
+
{
|
895
|
+
regex: /<pre>(.*?)<\/pre>/gis,
|
896
|
+
processor: (match, content) => {
|
897
|
+
blocks.push({
|
898
|
+
type: 'code',
|
899
|
+
code: {
|
900
|
+
rich_text: [(0, NotionUtils_1.createRichText)(content.trim())],
|
901
|
+
language: 'plain_text',
|
902
|
+
},
|
903
|
+
});
|
904
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
905
|
+
}
|
906
|
+
},
|
907
|
+
// Strong/Bold: <strong>content</strong> or <b>content</b>
|
908
|
+
{
|
909
|
+
regex: /<(strong|b)>(.*?)<\/(strong|b)>/gis,
|
910
|
+
processor: (match, tag, content) => {
|
911
|
+
blocks.push({
|
912
|
+
type: 'paragraph',
|
913
|
+
paragraph: {
|
914
|
+
rich_text: NotionAITool.parseBasicMarkdown(`**${content.trim()}**`),
|
915
|
+
},
|
916
|
+
});
|
917
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
918
|
+
}
|
919
|
+
},
|
920
|
+
// Emphasis/Italic: <em>content</em> or <i>content</i>
|
921
|
+
{
|
922
|
+
regex: /<(em|i)>(.*?)<\/(em|i)>/gis,
|
923
|
+
processor: (match, tag, content) => {
|
924
|
+
blocks.push({
|
925
|
+
type: 'paragraph',
|
926
|
+
paragraph: {
|
927
|
+
rich_text: NotionAITool.parseBasicMarkdown(`*${content.trim()}*`),
|
928
|
+
},
|
929
|
+
});
|
930
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
931
|
+
}
|
932
|
+
},
|
933
|
+
// Line breaks: <br/> or <br>
|
934
|
+
{
|
935
|
+
regex: /<br\s*\/?>/gis,
|
936
|
+
processor: (match) => {
|
937
|
+
blocks.push({
|
938
|
+
type: 'paragraph',
|
939
|
+
paragraph: {
|
940
|
+
rich_text: [(0, NotionUtils_1.createRichText)('')],
|
941
|
+
},
|
942
|
+
});
|
943
|
+
return `__XML_BLOCK_${blockCounter++}__`;
|
944
|
+
}
|
945
|
+
},
|
946
|
+
];
|
947
|
+
// Process each tag type
|
948
|
+
tagProcessors.forEach(({ regex, processor }) => {
|
949
|
+
processedContent = processedContent.replace(regex, (match, group1, group2, group3) => {
|
950
|
+
return processor(match, group1 || '', group2 || '', group3 || '');
|
951
|
+
});
|
952
|
+
});
|
953
|
+
return processedContent;
|
954
|
+
}
|
955
|
+
// Helper function to get callout emoji based on type
|
956
|
+
static getCalloutEmoji(type) {
|
957
|
+
const emojiMap = {
|
958
|
+
'info': 'ℹ️',
|
959
|
+
'warning': '⚠️',
|
960
|
+
'danger': '🚨',
|
961
|
+
'error': '❌',
|
962
|
+
'note': '📝',
|
963
|
+
'tip': '💡',
|
964
|
+
'success': '✅',
|
965
|
+
'question': '❓',
|
966
|
+
};
|
967
|
+
return emojiMap[type] || 'ℹ️';
|
968
|
+
}
|
969
|
+
// Helper function to get callout color based on type
|
970
|
+
static getCalloutColor(type) {
|
971
|
+
const colorMap = {
|
972
|
+
'info': 'blue',
|
973
|
+
'warning': 'yellow',
|
974
|
+
'danger': 'red',
|
975
|
+
'error': 'red',
|
976
|
+
'note': 'gray',
|
977
|
+
'tip': 'green',
|
978
|
+
'success': 'green',
|
979
|
+
'question': 'purple',
|
980
|
+
};
|
981
|
+
return colorMap[type] || 'gray';
|
982
|
+
}
|
983
|
+
// Helper function to parse basic markdown formatting in text
|
984
|
+
static parseBasicMarkdown(text) {
|
985
|
+
const richTextObjects = [];
|
986
|
+
// Find and collect all formatting patterns with their positions
|
987
|
+
const patterns = [
|
988
|
+
{ regex: /\[([^\]]+)\]\(([^)]+)\)/g, type: 'link' }, // [text](url)
|
989
|
+
{ regex: /\*\*\*([^*]+)\*\*\*/g, type: 'bold_italic' }, // ***bold italic***
|
990
|
+
{ regex: /\*\*([^*]+)\*\*/g, type: 'bold' }, // **bold**
|
991
|
+
{ regex: /\*([^*]+)\*/g, type: 'italic' }, // *italic*
|
992
|
+
{ regex: /~~([^~]+)~~/g, type: 'strikethrough' }, // ~~strikethrough~~
|
993
|
+
{ regex: /`([^`]+)`/g, type: 'code' }, // `code`
|
994
|
+
];
|
995
|
+
const matches = [];
|
996
|
+
// Collect all matches
|
997
|
+
patterns.forEach(pattern => {
|
998
|
+
const regex = new RegExp(pattern.regex.source, 'g');
|
999
|
+
let match;
|
1000
|
+
while ((match = regex.exec(text)) !== null) {
|
1001
|
+
if (pattern.type === 'link') {
|
1002
|
+
matches.push({
|
1003
|
+
start: match.index,
|
1004
|
+
end: match.index + match[0].length,
|
1005
|
+
text: match[1], // Link text
|
1006
|
+
type: pattern.type,
|
1007
|
+
url: match[2] // Link URL
|
1008
|
+
});
|
1009
|
+
}
|
1010
|
+
else {
|
1011
|
+
matches.push({
|
1012
|
+
start: match.index,
|
1013
|
+
end: match.index + match[0].length,
|
1014
|
+
text: match[1], // Inner text
|
1015
|
+
type: pattern.type
|
1016
|
+
});
|
1017
|
+
}
|
1018
|
+
}
|
1019
|
+
});
|
1020
|
+
// Sort matches by position and resolve overlaps (prefer longer matches)
|
1021
|
+
matches.sort((a, b) => {
|
1022
|
+
if (a.start !== b.start)
|
1023
|
+
return a.start - b.start;
|
1024
|
+
return (b.end - b.start) - (a.end - a.start); // Prefer longer matches
|
1025
|
+
});
|
1026
|
+
// Remove overlapping matches (keep the first/longer one)
|
1027
|
+
const resolvedMatches = [];
|
1028
|
+
for (const match of matches) {
|
1029
|
+
const hasOverlap = resolvedMatches.some(existing => (match.start < existing.end && match.end > existing.start));
|
1030
|
+
if (!hasOverlap) {
|
1031
|
+
resolvedMatches.push(match);
|
1032
|
+
}
|
1033
|
+
}
|
1034
|
+
// Sort again by position
|
1035
|
+
resolvedMatches.sort((a, b) => a.start - b.start);
|
1036
|
+
// If no formatting found, return simple rich text
|
1037
|
+
if (resolvedMatches.length === 0) {
|
1038
|
+
return [(0, NotionUtils_1.createRichText)(text)];
|
1039
|
+
}
|
1040
|
+
// Build rich text segments
|
1041
|
+
let lastIndex = 0;
|
1042
|
+
resolvedMatches.forEach(match => {
|
1043
|
+
// Add plain text before this match
|
1044
|
+
if (match.start > lastIndex) {
|
1045
|
+
const plainText = text.substring(lastIndex, match.start);
|
1046
|
+
if (plainText) {
|
1047
|
+
richTextObjects.push((0, NotionUtils_1.createRichText)(plainText));
|
1048
|
+
}
|
1049
|
+
}
|
1050
|
+
// Add formatted text
|
1051
|
+
const richTextObj = {
|
1052
|
+
type: 'text',
|
1053
|
+
text: { content: match.text },
|
1054
|
+
annotations: {}
|
1055
|
+
};
|
1056
|
+
// Apply formatting based on type
|
1057
|
+
switch (match.type) {
|
1058
|
+
case 'bold':
|
1059
|
+
richTextObj.annotations.bold = true;
|
1060
|
+
break;
|
1061
|
+
case 'italic':
|
1062
|
+
richTextObj.annotations.italic = true;
|
1063
|
+
break;
|
1064
|
+
case 'bold_italic':
|
1065
|
+
richTextObj.annotations.bold = true;
|
1066
|
+
richTextObj.annotations.italic = true;
|
1067
|
+
break;
|
1068
|
+
case 'strikethrough':
|
1069
|
+
richTextObj.annotations.strikethrough = true;
|
1070
|
+
break;
|
1071
|
+
case 'code':
|
1072
|
+
richTextObj.annotations.code = true;
|
1073
|
+
break;
|
1074
|
+
case 'link':
|
1075
|
+
richTextObj.text.link = { url: match.url };
|
1076
|
+
break;
|
1077
|
+
}
|
1078
|
+
// Clean up empty annotations
|
1079
|
+
if (Object.keys(richTextObj.annotations).length === 0) {
|
1080
|
+
delete richTextObj.annotations;
|
1081
|
+
}
|
1082
|
+
richTextObjects.push(richTextObj);
|
1083
|
+
lastIndex = match.end;
|
1084
|
+
});
|
1085
|
+
// Add remaining plain text
|
1086
|
+
if (lastIndex < text.length) {
|
1087
|
+
const remainingText = text.substring(lastIndex);
|
1088
|
+
if (remainingText) {
|
1089
|
+
richTextObjects.push((0, NotionUtils_1.createRichText)(remainingText));
|
1090
|
+
}
|
1091
|
+
}
|
1092
|
+
return richTextObjects.length > 0 ? richTextObjects : [(0, NotionUtils_1.createRichText)(text)];
|
1093
|
+
}
|
539
1094
|
static parsePropertiesToUpdate(propertiesString) {
|
540
1095
|
try {
|
541
1096
|
// Try to parse as JSON first
|
@@ -64,6 +64,10 @@ export declare function createBookmarkBlock(url: string, caption?: RichTextObjec
|
|
64
64
|
* Creates an embed block
|
65
65
|
*/
|
66
66
|
export declare function createEmbedBlock(url: string, caption?: RichTextObject[]): Block;
|
67
|
+
/**
|
68
|
+
* Creates a toggle block
|
69
|
+
*/
|
70
|
+
export declare function createToggleBlock(text: string | RichTextObject[], color?: NotionColor, children?: Block[]): Block;
|
67
71
|
/**
|
68
72
|
* Creates a table block with rows
|
69
73
|
*/
|
@@ -16,6 +16,7 @@ exports.createEquationBlock = createEquationBlock;
|
|
16
16
|
exports.createImageBlock = createImageBlock;
|
17
17
|
exports.createBookmarkBlock = createBookmarkBlock;
|
18
18
|
exports.createEmbedBlock = createEmbedBlock;
|
19
|
+
exports.createToggleBlock = createToggleBlock;
|
19
20
|
exports.createTableBlock = createTableBlock;
|
20
21
|
exports.convertBlockInput = convertBlockInput;
|
21
22
|
exports.resolvePageId = resolvePageId;
|
@@ -294,6 +295,19 @@ function createEmbedBlock(url, caption) {
|
|
294
295
|
},
|
295
296
|
};
|
296
297
|
}
|
298
|
+
/**
|
299
|
+
* Creates a toggle block
|
300
|
+
*/
|
301
|
+
function createToggleBlock(text, color, children) {
|
302
|
+
return {
|
303
|
+
type: 'toggle',
|
304
|
+
toggle: {
|
305
|
+
rich_text: typeof text === 'string' ? [createRichText(text)] : text,
|
306
|
+
color,
|
307
|
+
children,
|
308
|
+
},
|
309
|
+
};
|
310
|
+
}
|
297
311
|
/**
|
298
312
|
* Creates a table block with rows
|
299
313
|
*/
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "n8n-nodes-notion-advanced",
|
3
|
-
"version": "1.1
|
3
|
+
"version": "1.2.1-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
|
"build": "echo 'Already built'",
|