@jackuait/blok 0.8.3-beta.2 → 0.8.3-beta.4
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.
- package/dist/blok.mjs +2 -2
- package/dist/chunks/{blok-0-8moMz9.mjs → blok-eDCl1y00.mjs} +451 -379
- package/dist/chunks/{constants-PWnlOOrQ.mjs → constants-Duj_CSIT.mjs} +1 -1
- package/dist/chunks/{tools-BBmJ8jqK.mjs → tools-BG9c26dK.mjs} +97 -74
- package/dist/full.mjs +3 -3
- package/dist/react.mjs +2 -2
- package/dist/tools.mjs +2 -2
- package/package.json +1 -1
- package/src/components/utils/data-model-transform.ts +198 -4
- package/src/tools/callout/index.ts +51 -0
- package/types/tools-entry.d.ts +4 -0
|
@@ -54,6 +54,50 @@ interface LegacyToggleListData {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Legacy callout data structure for data model transformation.
|
|
59
|
+
* Old format: { title?: string, body: { blocks: [] } | null, variant, emoji, isEmojiVisible }
|
|
60
|
+
*/
|
|
61
|
+
interface LegacyCalloutData {
|
|
62
|
+
title?: string;
|
|
63
|
+
body?: {
|
|
64
|
+
blocks?: OutputBlockData[];
|
|
65
|
+
} | null;
|
|
66
|
+
variant?: string;
|
|
67
|
+
emoji?: string | null;
|
|
68
|
+
isEmojiVisible?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Map legacy callout variant to backgroundColor preset name
|
|
73
|
+
*/
|
|
74
|
+
const VARIANT_TO_BG_PRESET: Record<string, string | null> = {
|
|
75
|
+
general: null,
|
|
76
|
+
note: 'blue',
|
|
77
|
+
important: 'purple',
|
|
78
|
+
warning: 'orange',
|
|
79
|
+
additional: 'yellow',
|
|
80
|
+
recommendation: 'green',
|
|
81
|
+
caution: 'red',
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Map backgroundColor preset name back to legacy variant
|
|
86
|
+
*/
|
|
87
|
+
const BG_PRESET_TO_VARIANT: Record<string, string> = {
|
|
88
|
+
blue: 'note',
|
|
89
|
+
purple: 'important',
|
|
90
|
+
orange: 'warning',
|
|
91
|
+
yellow: 'additional',
|
|
92
|
+
green: 'recommendation',
|
|
93
|
+
red: 'caution',
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Default emoji for callout blocks
|
|
98
|
+
*/
|
|
99
|
+
const CALLOUT_DEFAULT_EMOJI = '💡';
|
|
100
|
+
|
|
57
101
|
/**
|
|
58
102
|
* Result of analyzing the input data format
|
|
59
103
|
*/
|
|
@@ -172,6 +216,21 @@ const isLegacyToggleListBlock = (block: OutputBlockData): block is OutputBlockDa
|
|
|
172
216
|
return typeof data === 'object' && data !== null && 'title' in data;
|
|
173
217
|
};
|
|
174
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Check if a block is in legacy callout format
|
|
221
|
+
* Legacy format: { type: "callout", data: { body: { blocks: [...] }, variant, emoji, isEmojiVisible } }
|
|
222
|
+
* New format has textColor/backgroundColor instead of variant/body
|
|
223
|
+
*/
|
|
224
|
+
const isLegacyCalloutBlock = (block: OutputBlockData): block is OutputBlockData<string, LegacyCalloutData> => {
|
|
225
|
+
if (block.type !== 'callout') {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const data = block.data as Record<string, unknown>;
|
|
230
|
+
|
|
231
|
+
return typeof data === 'object' && data !== null && 'body' in data;
|
|
232
|
+
};
|
|
233
|
+
|
|
175
234
|
/**
|
|
176
235
|
* Check if a block contains nested hierarchy in its items
|
|
177
236
|
*/
|
|
@@ -211,10 +270,15 @@ export const analyzeDataFormat = (blocks: OutputBlockData[]): DataFormatAnalysis
|
|
|
211
270
|
// Check if any block uses legacy toggleList format
|
|
212
271
|
const foundLegacyToggle = blocks.some(isLegacyToggleListBlock);
|
|
213
272
|
|
|
214
|
-
if (
|
|
273
|
+
// Check if any block uses legacy callout format (has body field)
|
|
274
|
+
const foundLegacyCallout = blocks.some(isLegacyCalloutBlock);
|
|
275
|
+
|
|
276
|
+
if (foundLegacyList || foundLegacyToggle || foundLegacyCallout) {
|
|
215
277
|
// Check if there's actual nesting for the hasHierarchy flag
|
|
216
278
|
const hasNesting = blocks.some(hasNestedItems) || blocks.some(block =>
|
|
217
279
|
isLegacyToggleListBlock(block) && block.data.body?.blocks !== undefined && block.data.body.blocks.length > 0
|
|
280
|
+
) || blocks.some(block =>
|
|
281
|
+
isLegacyCalloutBlock(block) && block.data.body?.blocks !== undefined && block.data.body.blocks.length > 0
|
|
218
282
|
);
|
|
219
283
|
|
|
220
284
|
return { format: 'legacy', hasHierarchy: hasNesting };
|
|
@@ -363,6 +427,57 @@ const expandToggleListToHierarchical = (
|
|
|
363
427
|
return blocks;
|
|
364
428
|
};
|
|
365
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Expand a legacy callout block into flat callout block + child blocks
|
|
432
|
+
*/
|
|
433
|
+
const expandCalloutToHierarchical = (
|
|
434
|
+
block: OutputBlockData<string, LegacyCalloutData>
|
|
435
|
+
): OutputBlockData[] => {
|
|
436
|
+
const blocks: OutputBlockData[] = [];
|
|
437
|
+
const calloutId = block.id ?? generateBlockId();
|
|
438
|
+
const bodyBlocks = block.data.body?.blocks ?? [];
|
|
439
|
+
|
|
440
|
+
// Collect child IDs, ensuring each child has an ID
|
|
441
|
+
const childIds: BlockId[] = [];
|
|
442
|
+
const childBlocks: OutputBlockData[] = [];
|
|
443
|
+
|
|
444
|
+
for (const childBlock of bodyBlocks) {
|
|
445
|
+
const childId = childBlock.id ?? generateBlockId();
|
|
446
|
+
|
|
447
|
+
childIds.push(childId);
|
|
448
|
+
childBlocks.push({
|
|
449
|
+
...childBlock,
|
|
450
|
+
id: childId,
|
|
451
|
+
parent: calloutId,
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Map variant → backgroundColor preset
|
|
456
|
+
const variant = block.data.variant ?? 'general';
|
|
457
|
+
const backgroundColor = variant in VARIANT_TO_BG_PRESET ? VARIANT_TO_BG_PRESET[variant] : null;
|
|
458
|
+
|
|
459
|
+
// Map emoji + isEmojiVisible → emoji string
|
|
460
|
+
const emoji: string = block.data.isEmojiVisible === false
|
|
461
|
+
? ''
|
|
462
|
+
: (block.data.emoji ?? CALLOUT_DEFAULT_EMOJI);
|
|
463
|
+
|
|
464
|
+
blocks.push({
|
|
465
|
+
id: calloutId,
|
|
466
|
+
type: 'callout',
|
|
467
|
+
data: {
|
|
468
|
+
emoji,
|
|
469
|
+
textColor: null,
|
|
470
|
+
backgroundColor,
|
|
471
|
+
},
|
|
472
|
+
...(block.tunes !== undefined ? { tunes: block.tunes } : {}),
|
|
473
|
+
...(childIds.length > 0 ? { content: childIds } : {}),
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
blocks.push(...childBlocks);
|
|
477
|
+
|
|
478
|
+
return blocks;
|
|
479
|
+
};
|
|
480
|
+
|
|
366
481
|
/**
|
|
367
482
|
* Expand legacy nested format to hierarchical flat-with-references format
|
|
368
483
|
* @param blocks - array of blocks potentially containing nested structures
|
|
@@ -382,6 +497,11 @@ export const expandToHierarchical = (blocks: OutputBlockData[]): OutputBlockData
|
|
|
382
497
|
// Expand toggleList to flat toggle + child blocks
|
|
383
498
|
const expanded = expandToggleListToHierarchical(block);
|
|
384
499
|
|
|
500
|
+
expandedBlocks.push(...expanded);
|
|
501
|
+
} else if (isLegacyCalloutBlock(block)) {
|
|
502
|
+
// Expand legacy callout to flat callout + child blocks
|
|
503
|
+
const expanded = expandCalloutToHierarchical(block);
|
|
504
|
+
|
|
385
505
|
expandedBlocks.push(...expanded);
|
|
386
506
|
} else {
|
|
387
507
|
// Non-list blocks pass through unchanged (with guaranteed ID)
|
|
@@ -639,6 +759,71 @@ const isFlatModelListBlock = (block: OutputBlockData): boolean => {
|
|
|
639
759
|
return hasText && !hasItems;
|
|
640
760
|
};
|
|
641
761
|
|
|
762
|
+
/**
|
|
763
|
+
* Check if a block is a flat-model callout block (has no 'body' field)
|
|
764
|
+
*/
|
|
765
|
+
const isFlatModelCalloutBlock = (block: OutputBlockData): boolean => {
|
|
766
|
+
if (block.type !== 'callout') {
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
const data = block.data as Record<string, unknown>;
|
|
771
|
+
|
|
772
|
+
return typeof data === 'object' && data !== null && !('body' in data);
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Process a root callout block (flat model) and convert to a legacy callout block
|
|
777
|
+
*/
|
|
778
|
+
const processRootCalloutItem = (
|
|
779
|
+
block: OutputBlockData,
|
|
780
|
+
blockMap: Map<BlockId, OutputBlockData>,
|
|
781
|
+
processedIds: Set<BlockId>
|
|
782
|
+
): OutputBlockData => {
|
|
783
|
+
markBlockAsProcessed(block.id, processedIds);
|
|
784
|
+
|
|
785
|
+
const data = block.data as Record<string, unknown>;
|
|
786
|
+
|
|
787
|
+
// Map backgroundColor preset → variant
|
|
788
|
+
const backgroundColor = data.backgroundColor as string | null | undefined;
|
|
789
|
+
const variant = backgroundColor !== null && backgroundColor !== undefined
|
|
790
|
+
? (BG_PRESET_TO_VARIANT[backgroundColor] ?? 'general')
|
|
791
|
+
: 'general';
|
|
792
|
+
|
|
793
|
+
// Map emoji string → isEmojiVisible + emoji
|
|
794
|
+
const emojiValue = data.emoji as string | undefined;
|
|
795
|
+
const isEmojiVisible = typeof emojiValue === 'string' && emojiValue.length > 0;
|
|
796
|
+
const emoji = isEmojiVisible ? emojiValue : null;
|
|
797
|
+
|
|
798
|
+
// Collect child blocks
|
|
799
|
+
const childBlocks: OutputBlockData[] = [];
|
|
800
|
+
const contentIds = block.content ?? [];
|
|
801
|
+
|
|
802
|
+
for (const childId of contentIds) {
|
|
803
|
+
const childBlock = blockMap.get(childId);
|
|
804
|
+
|
|
805
|
+
if (childBlock) {
|
|
806
|
+
markBlockAsProcessed(childId, processedIds);
|
|
807
|
+
childBlocks.push(stripHierarchyFields(childBlock));
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
const legacyBlock: OutputBlockData = {
|
|
812
|
+
id: block.id,
|
|
813
|
+
type: 'callout',
|
|
814
|
+
data: {
|
|
815
|
+
title: '',
|
|
816
|
+
variant,
|
|
817
|
+
emoji,
|
|
818
|
+
isEmojiVisible,
|
|
819
|
+
...(childBlocks.length > 0 ? { body: { blocks: childBlocks } } : {}),
|
|
820
|
+
},
|
|
821
|
+
...(block.tunes !== undefined ? { tunes: block.tunes } : {}),
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
return legacyBlock;
|
|
825
|
+
};
|
|
826
|
+
|
|
642
827
|
/**
|
|
643
828
|
* Collapse hierarchical flat-with-references format back to legacy nested format
|
|
644
829
|
* @param blocks - array of flat blocks with parent/content references
|
|
@@ -654,12 +839,13 @@ export const collapseToLegacy = (blocks: OutputBlockData[]): OutputBlockData[] =
|
|
|
654
839
|
}
|
|
655
840
|
}
|
|
656
841
|
|
|
657
|
-
// If no flat-model list or
|
|
842
|
+
// If no flat-model list, toggle, or callout blocks, just strip hierarchy fields and return
|
|
658
843
|
const hasFlatListBlocks = blocks.some(isFlatModelListBlock);
|
|
659
844
|
const hasFlatToggleBlocks = blocks.some(isFlatModelToggleBlock);
|
|
660
845
|
const hasFlatToggleableHeaders = blocks.some(b => isToggleableHeaderBlock(b) && !b.parent);
|
|
846
|
+
const hasFlatCalloutBlocks = blocks.some(isFlatModelCalloutBlock);
|
|
661
847
|
|
|
662
|
-
if (!hasFlatListBlocks && !hasFlatToggleBlocks && !hasFlatToggleableHeaders) {
|
|
848
|
+
if (!hasFlatListBlocks && !hasFlatToggleBlocks && !hasFlatToggleableHeaders && !hasFlatCalloutBlocks) {
|
|
663
849
|
return blocks.map(stripHierarchyFields);
|
|
664
850
|
}
|
|
665
851
|
|
|
@@ -680,7 +866,9 @@ export const collapseToLegacy = (blocks: OutputBlockData[]): OutputBlockData[] =
|
|
|
680
866
|
const isRootToggleItem = isFlatToggleBlock && !block.parent;
|
|
681
867
|
const isToggleableHeader = isToggleableHeaderBlock(block);
|
|
682
868
|
const isRootToggleableHeader = isToggleableHeader && !block.parent;
|
|
683
|
-
const
|
|
869
|
+
const isFlatCallout = isFlatModelCalloutBlock(block);
|
|
870
|
+
const isRootCallout = isFlatCallout && !block.parent;
|
|
871
|
+
const isNonListItem = !isFlatListBlock && !isFlatToggleBlock && !isToggleableHeader && !isFlatCallout;
|
|
684
872
|
|
|
685
873
|
if (isRootListItem) {
|
|
686
874
|
const listBlock = processRootListItem(block, blockMap, processedIds);
|
|
@@ -700,6 +888,12 @@ export const collapseToLegacy = (blocks: OutputBlockData[]): OutputBlockData[] =
|
|
|
700
888
|
result.push(legacyBlock);
|
|
701
889
|
}
|
|
702
890
|
|
|
891
|
+
if (isRootCallout) {
|
|
892
|
+
const calloutBlock = processRootCalloutItem(block, blockMap, processedIds);
|
|
893
|
+
|
|
894
|
+
result.push(calloutBlock);
|
|
895
|
+
}
|
|
896
|
+
|
|
703
897
|
if (isNonListItem) {
|
|
704
898
|
result.push(stripHierarchyFields(block));
|
|
705
899
|
markBlockAsProcessed(block.id, processedIds);
|
|
@@ -27,6 +27,35 @@ import {
|
|
|
27
27
|
DEFAULT_EMOJI,
|
|
28
28
|
} from './constants';
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Resolve emoji from legacy callout data fields
|
|
32
|
+
*/
|
|
33
|
+
function resolveLegacyEmoji(data: Record<string, unknown>): string {
|
|
34
|
+
if (data.isEmojiVisible === false) {
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (typeof data.emoji === 'string' && data.emoji.length > 0) {
|
|
39
|
+
return data.emoji;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return DEFAULT_EMOJI;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Map legacy callout variant to backgroundColor preset name.
|
|
47
|
+
* Used when receiving data from older format that has variant instead of backgroundColor.
|
|
48
|
+
*/
|
|
49
|
+
const VARIANT_TO_BG_PRESET: Record<string, string | null> = {
|
|
50
|
+
general: null,
|
|
51
|
+
note: 'blue',
|
|
52
|
+
important: 'purple',
|
|
53
|
+
warning: 'orange',
|
|
54
|
+
additional: 'yellow',
|
|
55
|
+
recommendation: 'green',
|
|
56
|
+
caution: 'red',
|
|
57
|
+
};
|
|
58
|
+
|
|
30
59
|
export class CalloutTool implements BlockTool {
|
|
31
60
|
private readonly api: API;
|
|
32
61
|
private readonly readOnly: boolean;
|
|
@@ -47,6 +76,13 @@ export class CalloutTool implements BlockTool {
|
|
|
47
76
|
}
|
|
48
77
|
|
|
49
78
|
private normalizeData(data: Partial<CalloutData>): CalloutData {
|
|
79
|
+
const legacyData = data as Record<string, unknown>;
|
|
80
|
+
const hasLegacyFields = 'variant' in legacyData || 'isEmojiVisible' in legacyData;
|
|
81
|
+
|
|
82
|
+
if (hasLegacyFields) {
|
|
83
|
+
return this.normalizeLegacyData(legacyData);
|
|
84
|
+
}
|
|
85
|
+
|
|
50
86
|
return {
|
|
51
87
|
emoji: typeof data.emoji === 'string' ? data.emoji : DEFAULT_EMOJI,
|
|
52
88
|
textColor: typeof data.textColor === 'string' ? data.textColor : null,
|
|
@@ -54,6 +90,21 @@ export class CalloutTool implements BlockTool {
|
|
|
54
90
|
};
|
|
55
91
|
}
|
|
56
92
|
|
|
93
|
+
private normalizeLegacyData(data: Record<string, unknown>): CalloutData {
|
|
94
|
+
// Map variant to backgroundColor
|
|
95
|
+
const variant = typeof data.variant === 'string' ? data.variant : 'general';
|
|
96
|
+
const backgroundColor = variant in VARIANT_TO_BG_PRESET ? VARIANT_TO_BG_PRESET[variant] : null;
|
|
97
|
+
|
|
98
|
+
// Map isEmojiVisible + emoji to emoji string
|
|
99
|
+
const emoji = resolveLegacyEmoji(data);
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
emoji,
|
|
103
|
+
textColor: null,
|
|
104
|
+
backgroundColor: backgroundColor ?? null,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
57
108
|
public render(): HTMLElement {
|
|
58
109
|
const dom = buildCalloutDOM({
|
|
59
110
|
emoji: this._data.emoji,
|
package/types/tools-entry.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ export const Italic: InlineToolConstructable;
|
|
|
32
32
|
export const Link: InlineToolConstructable;
|
|
33
33
|
export const Convert: InlineToolConstructable;
|
|
34
34
|
export const Marker: InlineToolConstructable;
|
|
35
|
+
export const Strikethrough: InlineToolConstructable;
|
|
36
|
+
export const Underline: InlineToolConstructable;
|
|
35
37
|
|
|
36
38
|
// Block tunes
|
|
37
39
|
export const Delete: BlockTuneConstructable;
|
|
@@ -54,4 +56,6 @@ export const defaultInlineTools: {
|
|
|
54
56
|
readonly italic: {};
|
|
55
57
|
readonly link: {};
|
|
56
58
|
readonly marker: {};
|
|
59
|
+
readonly strikethrough: {};
|
|
60
|
+
readonly underline: {};
|
|
57
61
|
};
|