From edf46c67334ed8bd5adbb0be719ab0dd51ffa266 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com>
Date: Thu, 16 Jul 2026 22:30:25 +0800
Subject: [PATCH 5/7] Add portable callout page blocks

---
 src/components/Editor.css                     |  62 +++++++++++
 src/components/editorSchema.tsx               |  46 ++++++++
 .../tolariaEditorFormatting.test.tsx          |  58 ++++++++++
 .../tolariaEditorFormattingConfig.ts          | 101 +++++++++++++++++-
 src/utils/blockNoteDirectMarkdown.test.ts     |  12 +++
 src/utils/blockNoteDirectMarkdown.ts          |  12 +++
 src/utils/calloutMarkdown.ts                  |  70 ++++++++++++
 src/utils/editorDurableMarkdown.test.ts       |  17 +++
 src/utils/editorDurableMarkdown.ts            |   3 +-
 9 files changed, 379 insertions(+), 2 deletions(-)
 create mode 100644 src/utils/calloutMarkdown.ts

diff --git a/src/components/Editor.css b/src/components/Editor.css
index 24f0738..5db3fe1 100644
--- a/src/components/Editor.css
+++ b/src/components/Editor.css
@@ -80,6 +80,68 @@
   line-height: 1.65;
 }
 
+.tolaria-callout {
+  --tolaria-callout-accent: #2563eb;
+  --tolaria-callout-bg: color-mix(in srgb, #2563eb 9%, var(--bg-primary));
+  display: grid;
+  grid-template-columns: 4px minmax(0, 1fr);
+  gap: 12px;
+  width: 100%;
+  margin: 0.45rem 0;
+  padding: 11px 13px 11px 0;
+  border: 1px solid color-mix(in srgb, var(--tolaria-callout-accent) 28%, var(--border));
+  border-radius: 8px;
+  background: var(--tolaria-callout-bg);
+}
+
+.tolaria-callout[data-kind="success"] {
+  --tolaria-callout-accent: #168a48;
+  --tolaria-callout-bg: color-mix(in srgb, #168a48 10%, var(--bg-primary));
+}
+
+.tolaria-callout[data-kind="warning"] {
+  --tolaria-callout-accent: #b7791f;
+  --tolaria-callout-bg: color-mix(in srgb, #b7791f 13%, var(--bg-primary));
+}
+
+.tolaria-callout[data-kind="danger"] {
+  --tolaria-callout-accent: #dc2626;
+  --tolaria-callout-bg: color-mix(in srgb, #dc2626 10%, var(--bg-primary));
+}
+
+.tolaria-callout__marker {
+  width: 4px;
+  min-height: 100%;
+  border-radius: 999px;
+  background: var(--tolaria-callout-accent);
+}
+
+.tolaria-callout__body {
+  display: grid;
+  gap: 4px;
+  min-width: 0;
+}
+
+.tolaria-callout__label {
+  color: var(--tolaria-callout-accent);
+  font-size: 11px;
+  font-weight: 700;
+  line-height: 1.2;
+  text-transform: uppercase;
+}
+
+.tolaria-callout__content {
+  min-width: 0;
+  color: var(--text-primary);
+  font-size: 14px;
+  line-height: 1.6;
+}
+
+.tolaria-callout__content:empty::before {
+  color: var(--text-muted);
+  content: 'Write a note';
+}
+
 /* Breadcrumb bar: border can still react to the data attribute, but the
    breadcrumb filename/title stays visible at all times. */
 .breadcrumb-bar {
diff --git a/src/components/editorSchema.tsx b/src/components/editorSchema.tsx
index 2984eff..7d62518 100644
--- a/src/components/editorSchema.tsx
+++ b/src/components/editorSchema.tsx
@@ -25,6 +25,11 @@ import { MERMAID_BLOCK_TYPE, mermaidFenceSource } from '../utils/mermaidMarkdown
 import { TLDRAW_BLOCK_TYPE, TLDRAW_DEFAULT_HEIGHT } from '../utils/tldrawMarkdown'
 import { MARKDOWN_HIGHLIGHT_STYLE } from '../utils/markdownHighlightMarkdown'
 import { GUANGHU_PORTAL_BLOCK_TYPE } from '../utils/guanghuPortalMarkdown'
+import {
+  calloutKind,
+  TOLARIA_CALLOUT_BLOCK_TYPE,
+  type TolariaCalloutKind,
+} from '../utils/calloutMarkdown'
 import type { VaultEntry } from '../types'
 import { createTolariaCodeBlockOptions } from './codeBlockOptions'
 import { NoteTitleIcon } from './NoteTitleIcon'
@@ -350,6 +355,45 @@ const GuanghuPortalBlock = createReactBlockSpec(
   },
 )
 
+const CALLOUT_LABELS: Record<TolariaCalloutKind, string> = {
+  danger: 'Danger',
+  info: 'Info',
+  success: 'Success',
+  warning: 'Warning',
+}
+
+function TolariaCalloutRenderer({
+  block,
+  contentRef,
+}: {
+  block: { props: { kind?: string } }
+  contentRef: (node: HTMLElement | null) => void
+}) {
+  const kind = calloutKind(block.props.kind)
+  return (
+    <aside className="tolaria-callout" data-kind={kind}>
+      <span className="tolaria-callout__marker" aria-hidden="true" />
+      <div className="tolaria-callout__body">
+        <span className="tolaria-callout__label">{CALLOUT_LABELS[kind]}</span>
+        <div ref={contentRef} className="tolaria-callout__content" />
+      </div>
+    </aside>
+  )
+}
+
+const TolariaCalloutBlock = createReactBlockSpec(
+  {
+    type: TOLARIA_CALLOUT_BLOCK_TYPE,
+    propSchema: {
+      kind: { default: 'info' },
+    },
+    content: 'inline',
+  },
+  {
+    render: TolariaCalloutRenderer,
+  },
+)
+
 export function mediaBlockPropsForPreviewRuntime<T extends MediaBlockPreviewProps>(
   props: T,
   externalMediaPreview: boolean,
@@ -450,6 +494,7 @@ const codeBlock = createCodeBlockSpec(createTolariaCodeBlockOptions())
 const audioBlock = AudioBlockSpec()
 const mathBlock = MathBlock()
 const guanghuPortalBlock = GuanghuPortalBlock()
+const tolariaCalloutBlock = TolariaCalloutBlock()
 const mermaidBlock = MermaidBlock()
 const tldrawBlock = TldrawBlock()
 const videoBlock = VideoBlockSpec()
@@ -485,6 +530,7 @@ export const schema = BlockNoteSchema.create({
   blockSpecs: {
     audio: audioBlock,
     guanghuPortalBlock,
+    [TOLARIA_CALLOUT_BLOCK_TYPE]: tolariaCalloutBlock,
     mathBlock,
     mermaidBlock,
     tldrawBlock,
diff --git a/src/components/tolariaEditorFormatting.test.tsx b/src/components/tolariaEditorFormatting.test.tsx
index 5c5143b..7bd06c2 100644
--- a/src/components/tolariaEditorFormatting.test.tsx
+++ b/src/components/tolariaEditorFormatting.test.tsx
@@ -7,7 +7,9 @@ vi.mock('../lib/telemetry', () => ({
 }))
 
 import {
+  addItemsAfterKey,
   addItemsToMediaGroup,
+  createCalloutSlashMenuItems,
   createMathSlashMenuItem,
   filterTolariaFormattingToolbarItems,
   filterTolariaSlashMenuItems,
@@ -18,6 +20,7 @@ import {
 import { trackEvent } from '../lib/telemetry'
 import { MATH_BLOCK_TYPE } from '../utils/mathMarkdown'
 import { mermaidFenceSource } from '../utils/mermaidMarkdown'
+import { TOLARIA_CALLOUT_BLOCK_TYPE } from '../utils/calloutMarkdown'
 
 describe('tolariaEditorFormatting', () => {
   it('keeps the markdown-safe toolbar controls and block type select', () => {
@@ -73,6 +76,7 @@ describe('tolariaEditorFormatting', () => {
       { key: 'heading_4', title: 'Heading 4', onItemClick: () => {} },
       { key: 'bullet_list', title: 'Bullet List', subtext: 'Default list copy', onItemClick: () => {} },
       { key: 'code_block', title: 'Code Block', subtext: 'Default code copy', onItemClick: () => {} },
+      { key: 'tolaria_callout_info', title: 'Info callout', onItemClick: () => {} },
       { key: 'heading_5', title: 'Heading 5', onItemClick: () => {} },
       { key: 'heading_6', title: 'Heading 6', onItemClick: () => {} },
     ] satisfies TolariaSlashMenuTestItem[])
@@ -82,12 +86,14 @@ describe('tolariaEditorFormatting', () => {
       'heading_4',
       'bullet_list',
       'code_block',
+      'tolaria_callout_info',
     ])
     expect(items.map((item) => item.subtext)).toEqual([
       undefined,
       undefined,
       undefined,
       undefined,
+      undefined,
     ])
   })
 
@@ -224,6 +230,58 @@ describe('tolariaEditorFormatting', () => {
     ])
   })
 
+  it('places callout commands after quote blocks', () => {
+    type TolariaSlashMenuTestItem = {
+      key: string
+      title: string
+      group: string
+      onItemClick: () => void
+    }
+
+    const items = addItemsAfterKey([
+      { key: 'paragraph', title: 'Paragraph', group: 'Basic blocks', onItemClick: () => {} },
+      { key: 'quote', title: 'Quote', group: 'Basic blocks', onItemClick: () => {} },
+      { key: 'code_block', title: 'Code Block', group: 'Basic blocks', onItemClick: () => {} },
+    ] satisfies TolariaSlashMenuTestItem[], 'quote', [
+      { key: 'tolaria_callout_info', title: 'Info callout', group: 'Basic blocks', onItemClick: () => {} },
+    ])
+
+    expect(items.map(item => item.key)).toEqual([
+      'paragraph',
+      'quote',
+      'tolaria_callout_info',
+      'code_block',
+    ])
+  })
+
+  it('keeps callout slash commands searchable and inserts an editable callout block', async () => {
+    const block = { id: 'active-block' }
+    const editor = {
+      getTextCursorPosition: () => ({ block }),
+      replaceBlocks: () => {},
+    }
+    const replaceBlocks = vi.spyOn(editor, 'replaceBlocks')
+    const items = createCalloutSlashMenuItems(editor as never)
+    const warningItem = items.find(item => item.key === 'tolaria_callout_warning')
+
+    expect(warningItem).toEqual(expect.objectContaining({
+      title: 'Warning callout',
+      aliases: expect.arrayContaining(['warning', '警告']),
+    }))
+
+    warningItem?.onItemClick()
+
+    expect(replaceBlocks).toHaveBeenCalledWith([block], [{
+      type: TOLARIA_CALLOUT_BLOCK_TYPE,
+      props: { kind: 'warning' },
+      content: [{
+        type: 'text',
+        text: 'Warning callout',
+        styles: {},
+      }],
+    }])
+  })
+
   it('creates a math slash command with a default display equation', () => {
     const block = { id: 'active-block' }
     const editor = {
diff --git a/src/components/tolariaEditorFormattingConfig.ts b/src/components/tolariaEditorFormattingConfig.ts
index b55dede..614cbf5 100644
--- a/src/components/tolariaEditorFormattingConfig.ts
+++ b/src/components/tolariaEditorFormattingConfig.ts
@@ -6,9 +6,11 @@ import {
 import { createElement, type ReactElement } from 'react'
 import {
   CodeBlock,
+  CheckCircle,
   File,
   FlowArrow,
   ImageSquare,
+  Info,
   ListBullets,
   ListChecks,
   ListNumbers,
@@ -26,6 +28,8 @@ import {
   TextHFour,
   TextHFive,
   TextHSix,
+  Warning,
+  XCircle,
   Video,
   type Icon as PhosphorIcon,
 } from '@phosphor-icons/react'
@@ -38,6 +42,10 @@ import {
 import { MATH_BLOCK_TYPE } from '../utils/mathMarkdown'
 import { MERMAID_BLOCK_TYPE, mermaidFenceSource } from '../utils/mermaidMarkdown'
 import { TLDRAW_BLOCK_TYPE, TLDRAW_DEFAULT_HEIGHT } from '../utils/tldrawMarkdown'
+import {
+  TOLARIA_CALLOUT_BLOCK_TYPE,
+  type TolariaCalloutKind,
+} from '../utils/calloutMarkdown'
 
 type TolariaSlashMenuItem = DefaultReactSuggestionItem & { key: string }
 type TolariaBlockTypeSelectItem = RichEditorBlockTypeDefinition & {
@@ -55,6 +63,12 @@ type BlockSlashMenuItemConfig = {
   title: string
   type: string
 }
+type CalloutSlashMenuItemConfig = {
+  aliases: string[]
+  key: string
+  kind: TolariaCalloutKind
+  title: string
+}
 type TolariaSlashMenuLabels = {
   mathTitle: string
 }
@@ -115,6 +129,10 @@ const TOLARIA_SLASH_MENU_ICONS: Partial<Record<string, PhosphorIcon>> = {
   numbered_list: ListNumbers,
   paragraph: Paragraph,
   quote: Quotes,
+  tolaria_callout_danger: XCircle,
+  tolaria_callout_info: Info,
+  tolaria_callout_success: CheckCircle,
+  tolaria_callout_warning: Warning,
   table: Table,
   toggle_heading: TextHOne,
   toggle_heading_2: TextHTwo,
@@ -164,6 +182,64 @@ function createMermaidSlashMenuItem(
   })
 }
 
+function createCalloutSlashMenuItem(
+  editor: Parameters<typeof getDefaultReactSlashMenuItems>[0],
+  config: CalloutSlashMenuItemConfig,
+): TolariaSlashMenuItem {
+  const blockEditor = editor as unknown as SlashInsertEditor
+
+  return {
+    key: config.key,
+    title: config.title,
+    aliases: config.aliases,
+    group: 'Basic blocks',
+    onItemClick: () => {
+      const block = blockEditor.getTextCursorPosition().block
+      blockEditor.replaceBlocks([block], [{
+        type: TOLARIA_CALLOUT_BLOCK_TYPE,
+        props: { kind: config.kind },
+        content: [{
+          type: 'text',
+          text: config.title,
+          styles: {},
+        }],
+      }])
+      trackEvent('editor_callout_slash_command_used', { kind: config.kind })
+    },
+  } as TolariaSlashMenuItem
+}
+
+export function createCalloutSlashMenuItems(
+  editor: Parameters<typeof getDefaultReactSlashMenuItems>[0],
+): TolariaSlashMenuItem[] {
+  return [
+    createCalloutSlashMenuItem(editor, {
+      key: 'tolaria_callout_info',
+      title: 'Info callout',
+      aliases: ['callout', 'note', 'info', 'tip', '提示', '高亮'],
+      kind: 'info',
+    }),
+    createCalloutSlashMenuItem(editor, {
+      key: 'tolaria_callout_success',
+      title: 'Success callout',
+      aliases: ['callout', 'success', 'done', 'ok', '完成', '成功'],
+      kind: 'success',
+    }),
+    createCalloutSlashMenuItem(editor, {
+      key: 'tolaria_callout_warning',
+      title: 'Warning callout',
+      aliases: ['callout', 'warning', 'alert', 'todo', '警告', '注意'],
+      kind: 'warning',
+    }),
+    createCalloutSlashMenuItem(editor, {
+      key: 'tolaria_callout_danger',
+      title: 'Danger callout',
+      aliases: ['callout', 'danger', 'risk', 'error', '风险', '危险'],
+      kind: 'danger',
+    }),
+  ]
+}
+
 export function createMathSlashMenuItem(
   editor: Parameters<typeof getDefaultReactSlashMenuItems>[0],
   labels: TolariaSlashMenuLabels = { mathTitle: 'Math' },
@@ -218,6 +294,23 @@ export function addItemsToMediaGroup(
   return nextItems
 }
 
+export function addItemsAfterKey(
+  items: TolariaSlashMenuItem[],
+  insertAfterKey: string,
+  insertedItems: TolariaSlashMenuItem[],
+): TolariaSlashMenuItem[] {
+  const nextItems = [...items]
+  const insertIndex = nextItems.findIndex((item) => item.key === insertAfterKey)
+
+  if (insertIndex === -1) {
+    nextItems.unshift(...insertedItems)
+    return nextItems
+  }
+
+  nextItems.splice(insertIndex + 1, 0, ...insertedItems)
+  return nextItems
+}
+
 function createTolariaSlashMenuIcon(Icon: PhosphorIcon) {
   return createElement(
     'span',
@@ -273,8 +366,14 @@ export function getTolariaSlashMenuItems(
   query: string,
   labels?: TolariaSlashMenuLabels,
 ) {
+  const defaultItems = getDefaultReactSlashMenuItems(editor) as TolariaSlashMenuItem[]
+  const itemsWithCallouts = addItemsAfterKey(
+    defaultItems,
+    'quote',
+    createCalloutSlashMenuItems(editor),
+  )
   const items = addItemsToMediaGroup(
-    getDefaultReactSlashMenuItems(editor) as TolariaSlashMenuItem[],
+    itemsWithCallouts,
     [
       createMermaidSlashMenuItem(editor),
       createMathSlashMenuItem(editor, labels),
diff --git a/src/utils/blockNoteDirectMarkdown.test.ts b/src/utils/blockNoteDirectMarkdown.test.ts
index 524b29c..cabba60 100644
--- a/src/utils/blockNoteDirectMarkdown.test.ts
+++ b/src/utils/blockNoteDirectMarkdown.test.ts
@@ -5,6 +5,7 @@ import {
   serializeBlockNoteMarkdown,
   type DirectMarkdownCapableSerializer,
 } from './blockNoteDirectMarkdown'
+import { TOLARIA_CALLOUT_BLOCK_TYPE } from './calloutMarkdown'
 import { serializeRichEditorBodyToMarkdown } from './richEditorMarkdown'
 
 function makeEditor(document: unknown[]): DirectMarkdownCapableSerializer & { document: unknown[] } {
@@ -117,6 +118,17 @@ describe('BlockNote direct Markdown serialization', () => {
     ].join('\n'))
   })
 
+  it('serializes Tolaria callout blocks as portable Markdown callouts', () => {
+    expect(blocksToMarkdownDirect([
+      {
+        type: TOLARIA_CALLOUT_BLOCK_TYPE,
+        props: { kind: 'success' },
+        content: [{ type: 'text', text: 'Package is ready', styles: {} }],
+        children: [],
+      },
+    ]).markdown).toBe('> [!success] Package is ready')
+  })
+
   it('caches unchanged block objects across rich-editor body serialization', () => {
     const block = {
       type: 'paragraph',
diff --git a/src/utils/blockNoteDirectMarkdown.ts b/src/utils/blockNoteDirectMarkdown.ts
index c075831..2ac99a0 100644
--- a/src/utils/blockNoteDirectMarkdown.ts
+++ b/src/utils/blockNoteDirectMarkdown.ts
@@ -1,3 +1,5 @@
+import { calloutKind, TOLARIA_CALLOUT_BLOCK_TYPE } from './calloutMarkdown'
+
 interface TextStyles {
   [style: string]: string | boolean | undefined
 }
@@ -228,6 +230,15 @@ function quoteMarkdown(block: BlockLike): string {
   return text.split('\n').map(line => `> ${line}`).join('\n')
 }
 
+function calloutMarkdown(block: BlockLike): string {
+  const kind = calloutKind(block.props?.kind)
+  const text = serializeInlineContent(contentArray(block.content)) || 'Write a note'
+  return `[!${kind}] ${text}`
+    .split('\n')
+    .map(line => `> ${line}`)
+    .join('\n')
+}
+
 function tableCellMarkdown(cell: TableCellValue): string {
   const text = typeof cell === 'string'
     ? cell
@@ -273,6 +284,7 @@ const BLOCK_MARKDOWN_HANDLERS: Record<string, BlockMarkdownHandler> = {
   heading: headingMarkdown,
   quote: quoteMarkdown,
   table: tableMarkdown,
+  [TOLARIA_CALLOUT_BLOCK_TYPE]: calloutMarkdown,
 }
 
 function blockMarkdownWithoutChildren(block: BlockLike, context: SerializeContext): string | null {
diff --git a/src/utils/calloutMarkdown.ts b/src/utils/calloutMarkdown.ts
new file mode 100644
index 0000000..b6881b6
--- /dev/null
+++ b/src/utils/calloutMarkdown.ts
@@ -0,0 +1,70 @@
+import type { BlockLike } from './durableMarkdownBlocks'
+
+export const TOLARIA_CALLOUT_BLOCK_TYPE = 'tolariaCallout'
+
+export const TOLARIA_CALLOUT_KINDS = ['info', 'success', 'warning', 'danger'] as const
+
+export type TolariaCalloutKind = typeof TOLARIA_CALLOUT_KINDS[number]
+
+const CALLOUT_MARKER_RE = /^\[!(info|success|warning|danger)\]\s*(.*)$/iu
+
+function isCalloutKind(value: unknown): value is TolariaCalloutKind {
+  return typeof value === 'string' && TOLARIA_CALLOUT_KINDS.includes(value as TolariaCalloutKind)
+}
+
+function readTextOnlyContent(content: BlockLike['content']): string | null {
+  if (!Array.isArray(content) || content.length === 0) return null
+
+  let text = ''
+  for (const item of content) {
+    if (item.type !== 'text' || typeof item.text !== 'string') return null
+    text += item.text
+  }
+  return text
+}
+
+function calloutMatchFromQuote(block: BlockLike) {
+  if (block.type !== 'quote') return null
+
+  const text = readTextOnlyContent(block.content)
+  if (text === null) return null
+
+  const match = CALLOUT_MARKER_RE.exec(text)
+  if (!match) return null
+
+  const kind = match[1]?.toLowerCase()
+  if (!isCalloutKind(kind)) return null
+
+  return {
+    kind,
+    text: match[2] ?? '',
+  }
+}
+
+export function injectTolariaCalloutBlocks(blocks: unknown[]): unknown[] {
+  return (blocks as BlockLike[]).map((block) => {
+    const match = calloutMatchFromQuote(block)
+    const children = Array.isArray(block.children)
+      ? injectTolariaCalloutBlocks(block.children) as BlockLike[]
+      : block.children
+
+    if (!match) return { ...block, children }
+
+    return {
+      ...block,
+      type: TOLARIA_CALLOUT_BLOCK_TYPE,
+      props: {
+        ...(block.props ?? {}),
+        kind: match.kind,
+      },
+      content: match.text
+        ? [{ type: 'text', text: match.text, styles: {} }]
+        : [],
+      children,
+    }
+  })
+}
+
+export function calloutKind(value: unknown): TolariaCalloutKind {
+  return isCalloutKind(value) ? value : 'info'
+}
diff --git a/src/utils/editorDurableMarkdown.test.ts b/src/utils/editorDurableMarkdown.test.ts
index f5a230d..4e6679e 100644
--- a/src/utils/editorDurableMarkdown.test.ts
+++ b/src/utils/editorDurableMarkdown.test.ts
@@ -6,6 +6,7 @@ import {
   preProcessDurableEditorMarkdown,
   serializeDurableEditorBlocks,
 } from './editorDurableMarkdown'
+import { TOLARIA_CALLOUT_BLOCK_TYPE } from './calloutMarkdown'
 import { MERMAID_BLOCK_TYPE } from './mermaidMarkdown'
 import { TLDRAW_BLOCK_TYPE } from './tldrawMarkdown'
 
@@ -70,4 +71,20 @@ describe('editor durable markdown blocks', () => {
       },
     })
   })
+
+  it('restores Markdown callouts as Tolaria page blocks', () => {
+    const blocks = injectDurableEditorMarkdownBlocks([
+      {
+        type: 'quote',
+        content: [{ type: 'text', text: '[!warning] Check signing before sharing.', styles: {} }],
+        children: [],
+      },
+    ]) as Array<{ type: string; props?: Record<string, string>; content?: Array<{ text?: string }> }>
+
+    expect(blocks[0]).toMatchObject({
+      type: TOLARIA_CALLOUT_BLOCK_TYPE,
+      props: { kind: 'warning' },
+      content: [{ text: 'Check signing before sharing.' }],
+    })
+  })
 })
diff --git a/src/utils/editorDurableMarkdown.ts b/src/utils/editorDurableMarkdown.ts
index 6e6d30a..baa5670 100644
--- a/src/utils/editorDurableMarkdown.ts
+++ b/src/utils/editorDurableMarkdown.ts
@@ -16,6 +16,7 @@ import { serializeMathAwareBlocks } from './mathMarkdown'
 import { mermaidMarkdownCodec } from './mermaidMarkdown'
 import { tldrawMarkdownCodec } from './tldrawMarkdown'
 import { guanghuPortalMarkdownCodec } from './guanghuPortalMarkdown'
+import { injectTolariaCalloutBlocks } from './calloutMarkdown'
 
 const EDITOR_DURABLE_MARKDOWN_CODECS = [
   guanghuPortalMarkdownCodec,
@@ -36,7 +37,7 @@ export function injectDurableEditorMarkdownBlocks(blocks: unknown[]): unknown[]
     blocks,
     codecs: EDITOR_DURABLE_MARKDOWN_CODECS,
   })
-  return injectFileAttachmentBlocks(withDurableBlocks)
+  return injectTolariaCalloutBlocks(injectFileAttachmentBlocks(withDurableBlocks))
 }
 
 export function serializeDurableEditorBlocks(
-- 
2.50.1 (Apple Git-155)

