feat(editor): provide callout markdown export options middleware (#12283)
Closes: [BS-3491](https://linear.app/affine-design/issue/BS-3491/支持-callout-导出成-vitepress-文档站支持的-markdown-格式) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added support for exporting callout blocks in both GFM and Admonition markdown styles, including configurable admonition types and custom titles. - Introduced middleware to customize callout export options for markdown serialization. - **Tests** - Enhanced test coverage for callout markdown export, including scenarios with and without export middleware for different styles and content structures. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -4,6 +4,9 @@ import {
|
|||||||
TableModelFlavour,
|
TableModelFlavour,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
|
CalloutAdmonitionType,
|
||||||
|
CalloutExportStyle,
|
||||||
|
calloutMarkdownExportMiddleware,
|
||||||
embedSyncedDocMiddleware,
|
embedSyncedDocMiddleware,
|
||||||
MarkdownAdapter,
|
MarkdownAdapter,
|
||||||
} from '@blocksuite/affine-shared/adapters';
|
} from '@blocksuite/affine-shared/adapters';
|
||||||
@@ -2449,119 +2452,288 @@ World!
|
|||||||
expect(target.file).toBe(markdown);
|
expect(target.file).toBe(markdown);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('callout', async () => {
|
describe('callout', () => {
|
||||||
const blockSnapshot: BlockSnapshot = {
|
test('without export middleware', async () => {
|
||||||
type: 'block',
|
const blockSnapshot: BlockSnapshot = {
|
||||||
id: 'block:vu6SK6WJpW',
|
type: 'block',
|
||||||
flavour: 'affine:page',
|
id: 'block:vu6SK6WJpW',
|
||||||
props: {
|
flavour: 'affine:page',
|
||||||
title: {
|
props: {
|
||||||
'$blocksuite:internal:text$': true,
|
title: {
|
||||||
delta: [],
|
'$blocksuite:internal:text$': true,
|
||||||
},
|
delta: [],
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:Tk4gSPocAt',
|
|
||||||
flavour: 'affine:surface',
|
|
||||||
props: {
|
|
||||||
elements: {},
|
|
||||||
},
|
},
|
||||||
children: [],
|
|
||||||
},
|
},
|
||||||
{
|
children: [
|
||||||
type: 'block',
|
{
|
||||||
id: 'block:WfnS5ZDCJT',
|
type: 'block',
|
||||||
flavour: 'affine:note',
|
id: 'block:Tk4gSPocAt',
|
||||||
props: {
|
flavour: 'affine:surface',
|
||||||
xywh: '[0,0,800,95]',
|
props: {
|
||||||
background: DefaultTheme.noteBackgrounColor,
|
elements: {},
|
||||||
index: 'a0',
|
},
|
||||||
hidden: false,
|
children: [],
|
||||||
displayMode: NoteDisplayMode.DocAndEdgeless,
|
|
||||||
},
|
},
|
||||||
children: [
|
{
|
||||||
{
|
type: 'block',
|
||||||
type: 'block',
|
id: 'block:WfnS5ZDCJT',
|
||||||
id: 'block:8hOLxad5Fv',
|
flavour: 'affine:note',
|
||||||
flavour: 'affine:callout',
|
props: {
|
||||||
props: {
|
xywh: '[0,0,800,95]',
|
||||||
emoji: '💡',
|
background: DefaultTheme.noteBackgrounColor,
|
||||||
},
|
index: 'a0',
|
||||||
children: [
|
hidden: false,
|
||||||
{
|
displayMode: NoteDisplayMode.DocAndEdgeless,
|
||||||
type: 'block',
|
},
|
||||||
id: 'block:8hOLxad5Fv',
|
children: [
|
||||||
flavour: 'affine:paragraph',
|
{
|
||||||
props: {
|
type: 'block',
|
||||||
type: 'text',
|
id: 'block:8hOLxad5Fv',
|
||||||
text: {
|
flavour: 'affine:callout',
|
||||||
'$blocksuite:internal:text$': true,
|
props: {
|
||||||
delta: [{ insert: 'First callout' }],
|
emoji: '💡',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'block',
|
||||||
|
id: 'block:8hOLxad5Fv',
|
||||||
|
flavour: 'affine:paragraph',
|
||||||
|
props: {
|
||||||
|
type: 'text',
|
||||||
|
text: {
|
||||||
|
'$blocksuite:internal:text$': true,
|
||||||
|
delta: [{ insert: 'First callout' }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
children: [],
|
||||||
},
|
},
|
||||||
children: [],
|
],
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxadvdv',
|
|
||||||
flavour: 'affine:callout',
|
|
||||||
props: {
|
|
||||||
emoji: '',
|
|
||||||
},
|
},
|
||||||
children: [
|
{
|
||||||
{
|
type: 'block',
|
||||||
type: 'block',
|
id: 'block:8hOLxadvdv',
|
||||||
id: 'block:8hOLxad5Fv',
|
flavour: 'affine:callout',
|
||||||
flavour: 'affine:paragraph',
|
props: {
|
||||||
props: {
|
emoji: '',
|
||||||
type: 'text',
|
},
|
||||||
text: {
|
children: [
|
||||||
'$blocksuite:internal:text$': true,
|
{
|
||||||
delta: [{ insert: 'Second callout without emoji' }],
|
type: 'block',
|
||||||
|
id: 'block:8hOLxad5Fv',
|
||||||
|
flavour: 'affine:paragraph',
|
||||||
|
props: {
|
||||||
|
type: 'text',
|
||||||
|
text: {
|
||||||
|
'$blocksuite:internal:text$': true,
|
||||||
|
delta: [
|
||||||
|
{ insert: 'Warning second callout without emoji' },
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
children: [],
|
||||||
},
|
},
|
||||||
children: [],
|
{
|
||||||
},
|
type: 'block',
|
||||||
],
|
id: 'block:8hOLxad5Fv',
|
||||||
},
|
flavour: 'affine:paragraph',
|
||||||
{
|
props: {
|
||||||
type: 'block',
|
type: 'text',
|
||||||
id: 'block:8hOLxbfdb',
|
text: {
|
||||||
flavour: 'affine:paragraph',
|
'$blocksuite:internal:text$': true,
|
||||||
props: {
|
delta: [{ insert: 'Text in second callout' }],
|
||||||
type: 'quote',
|
},
|
||||||
text: {
|
},
|
||||||
'$blocksuite:internal:text$': true,
|
children: [],
|
||||||
delta: [{ insert: 'This is a regular blockquote' }],
|
},
|
||||||
},
|
],
|
||||||
},
|
},
|
||||||
children: [],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
};
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const markdown = `> \\[!💡]
|
const markdown = `> \\[!💡]
|
||||||
>
|
>
|
||||||
> First callout
|
> First callout
|
||||||
|
|
||||||
> \\[!]
|
> \\[!]
|
||||||
>
|
>
|
||||||
> Second callout without emoji
|
> Warning second callout without emoji
|
||||||
|
>
|
||||||
> This is a regular blockquote
|
> Text in second callout
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const mdAdapter = new MarkdownAdapter(createJob(), provider);
|
const mdAdapter = new MarkdownAdapter(createJob(), provider);
|
||||||
const target = await mdAdapter.fromBlockSnapshot({
|
const target = await mdAdapter.fromBlockSnapshot({
|
||||||
snapshot: blockSnapshot,
|
snapshot: blockSnapshot,
|
||||||
|
});
|
||||||
|
expect(target.file).toBe(markdown);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with export middleware', async () => {
|
||||||
|
const blockSnapshot: BlockSnapshot = {
|
||||||
|
type: 'block',
|
||||||
|
id: 'block:vu6SK6WJpW',
|
||||||
|
flavour: 'affine:page',
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
'$blocksuite:internal:text$': true,
|
||||||
|
delta: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'block',
|
||||||
|
id: 'block:Tk4gSPocAt',
|
||||||
|
flavour: 'affine:surface',
|
||||||
|
props: {
|
||||||
|
elements: {},
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'block',
|
||||||
|
id: 'block:WfnS5ZDCJT',
|
||||||
|
flavour: 'affine:note',
|
||||||
|
props: {
|
||||||
|
xywh: '[0,0,800,95]',
|
||||||
|
background: DefaultTheme.noteBackgrounColor,
|
||||||
|
index: 'a0',
|
||||||
|
hidden: false,
|
||||||
|
displayMode: NoteDisplayMode.DocAndEdgeless,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'block',
|
||||||
|
id: 'block:8hOLxad5Fv',
|
||||||
|
flavour: 'affine:callout',
|
||||||
|
props: {
|
||||||
|
emoji: '💡',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'block',
|
||||||
|
id: 'block:8hOLxad5Fv',
|
||||||
|
flavour: 'affine:paragraph',
|
||||||
|
props: {
|
||||||
|
type: 'text',
|
||||||
|
text: {
|
||||||
|
'$blocksuite:internal:text$': true,
|
||||||
|
delta: [
|
||||||
|
{ insert: 'Callout that does not have a title' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'block',
|
||||||
|
id: 'block:8hOLxadvdv',
|
||||||
|
flavour: 'affine:callout',
|
||||||
|
props: {
|
||||||
|
emoji: '',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'block',
|
||||||
|
id: 'block:8hOLxad5Fv',
|
||||||
|
flavour: 'affine:paragraph',
|
||||||
|
props: {
|
||||||
|
type: 'text',
|
||||||
|
text: {
|
||||||
|
'$blocksuite:internal:text$': true,
|
||||||
|
delta: [
|
||||||
|
{
|
||||||
|
insert:
|
||||||
|
'Warning callout with custom title and multiple paragraphs',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'block',
|
||||||
|
id: 'block:8hOLxad5Fv',
|
||||||
|
flavour: 'affine:paragraph',
|
||||||
|
props: {
|
||||||
|
type: 'text',
|
||||||
|
text: {
|
||||||
|
'$blocksuite:internal:text$': true,
|
||||||
|
delta: [{ insert: 'Text in second callout' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'block',
|
||||||
|
id: 'block:8hOLxad5Fv',
|
||||||
|
flavour: 'affine:callout',
|
||||||
|
props: {
|
||||||
|
emoji: '💡',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'block',
|
||||||
|
id: 'block:8hOLxad5Fv',
|
||||||
|
flavour: 'affine:paragraph',
|
||||||
|
props: {
|
||||||
|
type: 'text',
|
||||||
|
text: {
|
||||||
|
'$blocksuite:internal:text$': true,
|
||||||
|
delta: [
|
||||||
|
{ insert: 'details' },
|
||||||
|
{ insert: ' ' },
|
||||||
|
{ insert: '\nText in details callout with new line' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const markdown = `::: info
|
||||||
|
|
||||||
|
Callout that does not have a title
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: warning callout with custom title and multiple paragraphs
|
||||||
|
|
||||||
|
Text in second callout
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: details
|
||||||
|
|
||||||
|
Text in details callout with new line
|
||||||
|
|
||||||
|
:::
|
||||||
|
`;
|
||||||
|
|
||||||
|
const mdAdapter = new MarkdownAdapter(
|
||||||
|
createJob([
|
||||||
|
calloutMarkdownExportMiddleware({
|
||||||
|
style: CalloutExportStyle.Admonitions,
|
||||||
|
admonitionType: CalloutAdmonitionType.Info,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
provider
|
||||||
|
);
|
||||||
|
const target = await mdAdapter.fromBlockSnapshot({
|
||||||
|
snapshot: blockSnapshot,
|
||||||
|
});
|
||||||
|
expect(target.file).toBe(markdown);
|
||||||
});
|
});
|
||||||
expect(target.file).toBe(markdown);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,17 @@ import { CalloutBlockSchema } from '@blocksuite/affine-model';
|
|||||||
import {
|
import {
|
||||||
BlockMarkdownAdapterExtension,
|
BlockMarkdownAdapterExtension,
|
||||||
type BlockMarkdownAdapterMatcher,
|
type BlockMarkdownAdapterMatcher,
|
||||||
|
CALLOUT_MARKDOWN_EXPORT_OPTIONS_KEY,
|
||||||
|
type CalloutAdmonitionType,
|
||||||
|
CalloutAdmonitionTypeSet,
|
||||||
|
CalloutExportStyle,
|
||||||
|
type CalloutMarkdownExportOptions,
|
||||||
|
calloutMarkdownExportOptionsSchema,
|
||||||
|
DEFAULT_ADMONITION_TYPE,
|
||||||
getCalloutEmoji,
|
getCalloutEmoji,
|
||||||
isCalloutNode,
|
isCalloutNode,
|
||||||
} from '@blocksuite/affine-shared/adapters';
|
} from '@blocksuite/affine-shared/adapters';
|
||||||
import { nanoid } from '@blocksuite/store';
|
import { type DeltaInsert, nanoid } from '@blocksuite/store';
|
||||||
|
|
||||||
// Currently, the callout block children can only be paragraph block or list block
|
// Currently, the callout block children can only be paragraph block or list block
|
||||||
// In mdast, the node types are `paragraph`, `list`, `heading`, `blockquote`
|
// In mdast, the node types are `paragraph`, `list`, `heading`, `blockquote`
|
||||||
@@ -16,6 +23,29 @@ const CALLOUT_BLOCK_CHILDREN_TYPES = new Set([
|
|||||||
'blockquote',
|
'blockquote',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const ADMONITION_SYMBOL = ':::';
|
||||||
|
const DEFAULT_OPTIONS: CalloutMarkdownExportOptions = {
|
||||||
|
style: CalloutExportStyle.GFM,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the callout export options from the configs
|
||||||
|
* @param configs - The configs of the callout block
|
||||||
|
* @returns The callout export options
|
||||||
|
*/
|
||||||
|
function getCalloutExportOptions(
|
||||||
|
configs: Map<string, unknown>
|
||||||
|
): CalloutMarkdownExportOptions {
|
||||||
|
let exportOptions: CalloutMarkdownExportOptions = DEFAULT_OPTIONS;
|
||||||
|
try {
|
||||||
|
const options = configs.get(CALLOUT_MARKDOWN_EXPORT_OPTIONS_KEY);
|
||||||
|
if (options) {
|
||||||
|
exportOptions = calloutMarkdownExportOptionsSchema.parse(options);
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
return exportOptions;
|
||||||
|
}
|
||||||
|
|
||||||
export const calloutBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = {
|
export const calloutBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = {
|
||||||
flavour: CalloutBlockSchema.model.flavour,
|
flavour: CalloutBlockSchema.model.flavour,
|
||||||
toMatch: o => isCalloutNode(o.node),
|
toMatch: o => isCalloutNode(o.node),
|
||||||
@@ -57,29 +87,118 @@ export const calloutBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = {
|
|||||||
fromBlockSnapshot: {
|
fromBlockSnapshot: {
|
||||||
enter: (o, context) => {
|
enter: (o, context) => {
|
||||||
const emoji = o.node.props.emoji as string;
|
const emoji = o.node.props.emoji as string;
|
||||||
const { walkerContext } = context;
|
const { walkerContext, configs } = context;
|
||||||
walkerContext
|
|
||||||
.openNode(
|
const exportOptions = getCalloutExportOptions(configs);
|
||||||
{
|
const { style, admonitionType } = exportOptions;
|
||||||
type: 'blockquote',
|
// If the style is admonitions, we should handle the first child
|
||||||
children: [],
|
if (style === CalloutExportStyle.Admonitions) {
|
||||||
},
|
let type = admonitionType ?? DEFAULT_ADMONITION_TYPE;
|
||||||
'children'
|
let customTitle = '';
|
||||||
)
|
let restOfText = '';
|
||||||
.openNode({
|
|
||||||
type: 'paragraph',
|
const firstChild = o.node.children[0];
|
||||||
children: [
|
const isTextNode = !!firstChild.props.text;
|
||||||
|
// If the first child is a text block, we should get the type and custom title from the first line of the text
|
||||||
|
// And remove the first child from the children
|
||||||
|
// Otherwise, we should use the default admonition type as the type
|
||||||
|
if (isTextNode) {
|
||||||
|
const textDelta = (firstChild.props.text ?? { delta: [] }) as {
|
||||||
|
delta: DeltaInsert[];
|
||||||
|
};
|
||||||
|
// Get the text of the first child
|
||||||
|
const text = textDelta.delta.reduce((acc, delta) => {
|
||||||
|
if (delta.insert) {
|
||||||
|
acc += delta.insert;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, '');
|
||||||
|
|
||||||
|
// If the text is not empty, we should try to get type and custom title from the text
|
||||||
|
if (text) {
|
||||||
|
// Get the first line of the text
|
||||||
|
const firstLine = text.includes('\n') ? text.split('\n')[0] : text;
|
||||||
|
// Get the rest of the text besides the first line
|
||||||
|
restOfText = text.split('\n').slice(1).join('\n');
|
||||||
|
// Get the possible type from the first line
|
||||||
|
const possibleType = firstLine.split(' ')[0].toLowerCase();
|
||||||
|
// If the type is a valid admonition type, we should use it as the type
|
||||||
|
if (CalloutAdmonitionTypeSet.has(possibleType)) {
|
||||||
|
type = possibleType as CalloutAdmonitionType;
|
||||||
|
// Get the custom title from the first line
|
||||||
|
customTitle = firstLine.split(' ').slice(1).join(' ').trim();
|
||||||
|
// Remove the first child from the children
|
||||||
|
o.node.children = o.node.children.slice(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an admonition symbol paragraph to the start of the children
|
||||||
|
const admonitionSymbol =
|
||||||
|
`${ADMONITION_SYMBOL} ${type} ${customTitle}`.trim();
|
||||||
|
walkerContext
|
||||||
|
.openNode({
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
value: admonitionSymbol,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.closeNode();
|
||||||
|
|
||||||
|
// Add the rest of the text to the children content
|
||||||
|
if (restOfText) {
|
||||||
|
walkerContext
|
||||||
|
.openNode({
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [{ type: 'text', value: `${restOfText}` }],
|
||||||
|
})
|
||||||
|
.closeNode();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
walkerContext
|
||||||
|
.openNode(
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'blockquote',
|
||||||
value: `[!${emoji}]`,
|
children: [],
|
||||||
},
|
},
|
||||||
],
|
'children'
|
||||||
})
|
)
|
||||||
.closeNode();
|
.openNode({
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
value: `[!${emoji}]`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.closeNode();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
leave: (_, context) => {
|
leave: (_, context) => {
|
||||||
const { walkerContext } = context;
|
const { walkerContext, configs } = context;
|
||||||
walkerContext.closeNode();
|
const exportOptions = getCalloutExportOptions(configs);
|
||||||
|
const { style } = exportOptions;
|
||||||
|
// If the style is admonitions, we should add an admonition symbol paragraph to the end of the children
|
||||||
|
if (style === CalloutExportStyle.Admonitions) {
|
||||||
|
walkerContext
|
||||||
|
.openNode({
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
value: ADMONITION_SYMBOL,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.closeNode();
|
||||||
|
} else {
|
||||||
|
// If the style is gfm, we should close the outer blockquote node
|
||||||
|
walkerContext.closeNode();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import type { TransformerMiddleware } from '@blocksuite/store';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const CALLOUT_MARKDOWN_EXPORT_OPTIONS_KEY =
|
||||||
|
'calloutMarkdownExportOptions';
|
||||||
|
|
||||||
|
export enum CalloutExportStyle {
|
||||||
|
GFM = 'GFM',
|
||||||
|
Admonitions = 'Admonitions',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CalloutAdmonitionType {
|
||||||
|
Info = 'info',
|
||||||
|
Tip = 'tip',
|
||||||
|
Warning = 'warning',
|
||||||
|
Danger = 'danger',
|
||||||
|
Details = 'details',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_ADMONITION_TYPE = CalloutAdmonitionType.Info;
|
||||||
|
|
||||||
|
export const CalloutAdmonitionTypeSet: Set<string> = new Set(
|
||||||
|
Object.values(CalloutAdmonitionType)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const calloutMarkdownExportOptionsSchema = z.object({
|
||||||
|
style: z.nativeEnum(CalloutExportStyle),
|
||||||
|
admonitionType: z.nativeEnum(CalloutAdmonitionType).optional(),
|
||||||
|
});
|
||||||
|
export type CalloutMarkdownExportOptions = z.infer<
|
||||||
|
typeof calloutMarkdownExportOptionsSchema
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Middleware to set the export style of the callout block
|
||||||
|
* @param style - The markdown export style of the callout block
|
||||||
|
* @returns A TransformerMiddleware that sets the markdown export style of the callout block
|
||||||
|
*/
|
||||||
|
export const calloutMarkdownExportMiddleware = (
|
||||||
|
options: CalloutMarkdownExportOptions
|
||||||
|
): TransformerMiddleware => {
|
||||||
|
return ({ adapterConfigs }) => {
|
||||||
|
adapterConfigs.set(CALLOUT_MARKDOWN_EXPORT_OPTIONS_KEY, options);
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export * from './callout-export-options';
|
||||||
export * from './code';
|
export * from './code';
|
||||||
export * from './copy';
|
export * from './copy';
|
||||||
export * from './doc-link';
|
export * from './doc-link';
|
||||||
|
|||||||
Reference in New Issue
Block a user