Closes: [BS-3358](https://linear.app/affine-design/issue/BS-3358/remark-callout-plugin)
Closes: [BS-3247](https://linear.app/affine-design/issue/BS-3247/callout-markdown-adapter-适配)
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
- **New Features**
- Added support for callout blocks in Markdown, enabling recognition and conversion of callout syntax (e.g., `[!emoji]`) to and from block structures.
- **Bug Fixes**
- Improved handling to distinguish callout blocks from regular blockquotes and paragraphs during Markdown processing.
- **Tests**
- Introduced comprehensive tests for callout block serialization, deserialization, and plugin behavior to ensure correct Markdown handling.
- **Chores**
- Added a new dependency for Markdown AST traversal.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
### TL;DR
Added unit tests for block and selection commands, along with a new test helper system for creating test documents.
### What changed?
- Added unit tests for several commands:
- `getFirstContentBlockCommand`
- `getLastContentBlockCommand`
- `isNothingSelectedCommand`
- Created a new test helpers make it easier to create structured test documents with a html-like syntax:
```typescript
import { describe, expect, it } from 'vitest';
import { affine } from '../__tests__/utils/affine-template';
describe('My Test', () => {
it('should correctly handle document structure', () => {
const doc = affine`
<affine-page>
<affine-note>
<affine-paragraph>Test content</affine-paragraph>
</affine-note>
</affine-page>
`;
// Get blocks
const pages = doc.getBlocksByFlavour('affine:page');
const notes = doc.getBlocksByFlavour('affine:note');
const paragraphs = doc.getBlocksByFlavour('affine:paragraph');
expect(pages.length).toBe(1);
expect(notes.length).toBe(1);
expect(paragraphs.length).toBe(1);
// Perform more tests here...
});
});
```