@puredesktop/platform-editor 1.0.0-beta.1 → 1.0.0-beta.2

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.
Files changed (45) hide show
  1. package/package.json +1 -1
  2. package/src/CollapsePanel.tsx +35 -35
  3. package/src/DocumentDiffView.tsx +130 -130
  4. package/src/DocumentEditor.test.ts +118 -118
  5. package/src/DocumentEditor.tsx +2651 -2631
  6. package/src/alignedLineDiff.test.ts +52 -52
  7. package/src/alignedLineDiff.ts +67 -67
  8. package/src/collectionAssetSrc.ts +2 -2
  9. package/src/commentUtils.test.ts +350 -350
  10. package/src/commentUtils.ts +546 -546
  11. package/src/constants/toolKeys.ts +14 -14
  12. package/src/contentFormat.test.ts +27 -27
  13. package/src/contentFormat.ts +18 -18
  14. package/src/editorExtensions.ts +155 -155
  15. package/src/extensions/appliedChangeMark.ts +115 -115
  16. package/src/extensions/autoReviewPrompts.test.ts +529 -529
  17. package/src/extensions/autoReviewPrompts.ts +1442 -1442
  18. package/src/extensions/collectionImage.ts +26 -26
  19. package/src/extensions/collectionImagePaste.ts +215 -215
  20. package/src/extensions/commentMark.ts +223 -223
  21. package/src/extensions/documentAssetIdentity.ts +54 -54
  22. package/src/extensions/figure.ts +92 -92
  23. package/src/extensions/footnote.ts +186 -186
  24. package/src/extensions/indexMarker.ts +88 -88
  25. package/src/extensions/mathEditing.ts +239 -239
  26. package/src/extensions/mermaidBlock.tsx +297 -297
  27. package/src/extensions/slashCommands.test.ts +170 -170
  28. package/src/extensions/slashCommands.ts +746 -746
  29. package/src/extensions/smartTypography.test.ts +74 -74
  30. package/src/extensions/smartTypography.ts +120 -120
  31. package/src/index.ts +138 -137
  32. package/src/insertCollectionImage.test.ts +60 -60
  33. package/src/insertCollectionImage.ts +63 -63
  34. package/src/insertInlineAssetKind.test.ts +67 -67
  35. package/src/insertInlineAssetKind.ts +49 -49
  36. package/src/mermaidPreview.test.ts +54 -54
  37. package/src/mermaidPreview.ts +115 -115
  38. package/src/styled.ts +697 -676
  39. package/src/toolbar/ToolBtn.tsx +77 -63
  40. package/src/toolbar/Toolbar.test.tsx +325 -325
  41. package/src/toolbar/Toolbar.tsx +662 -624
  42. package/src/toolbar/groups/HeadingGroup.tsx +175 -153
  43. package/src/toolbar/overlayTypes.ts +28 -28
  44. package/src/useEditorExtensions.ts +22 -22
  45. package/src/utils/markdownUtils.ts +331 -331
@@ -1,60 +1,60 @@
1
- import type { Editor } from '@tiptap/core'
2
- import { describe, expect, it, vi } from 'vitest'
3
- import { insertCollectionImage } from './insertCollectionImage.js'
4
-
5
- function createMockEditor(): {
6
- editor: Editor
7
- insertContent: ReturnType<typeof vi.fn>
8
- run: ReturnType<typeof vi.fn>
9
- } {
10
- const run = vi.fn()
11
- const insertContent = vi.fn().mockReturnThis()
12
- const chain = {
13
- focus: vi.fn().mockReturnThis(),
14
- insertContent,
15
- run,
16
- }
17
-
18
- const editor = {
19
- isDestroyed: false,
20
- chain: vi.fn(() => chain),
21
- schema: { nodes: { image: {} } },
22
- } as unknown as Editor
23
-
24
- return { editor, insertContent, run }
25
- }
26
-
27
- describe('insertCollectionImage', () => {
28
- it('inserts image node with data URL and collectionAssetSrc', () => {
29
- const { editor, insertContent, run } = createMockEditor()
30
- const bytes = Uint8Array.from([0x89, 0x50, 0x4e, 0x47])
31
-
32
- insertCollectionImage(editor, {
33
- relativePath: 'assets/figures/chart.png',
34
- alt: 'Chart',
35
- bytes,
36
- mimeType: 'image/png',
37
- })
38
-
39
- expect(insertContent).toHaveBeenCalledWith({
40
- type: 'image',
41
- attrs: {
42
- src: expect.stringMatching(/^data:image\/png;base64,/),
43
- alt: 'Chart',
44
- collectionAssetSrc: 'assets/figures/chart.png',
45
- },
46
- })
47
- expect(run).toHaveBeenCalled()
48
- })
49
-
50
- it('returns false when editor is missing', () => {
51
- expect(
52
- insertCollectionImage(null, {
53
- relativePath: 'assets/a.png',
54
- alt: 'A',
55
- bytes: new Uint8Array([1]),
56
- mimeType: 'image/png',
57
- }),
58
- ).toBe(false)
59
- })
60
- })
1
+ import type { Editor } from '@tiptap/core'
2
+ import { describe, expect, it, vi } from 'vitest'
3
+ import { insertCollectionImage } from './insertCollectionImage.js'
4
+
5
+ function createMockEditor(): {
6
+ editor: Editor
7
+ insertContent: ReturnType<typeof vi.fn>
8
+ run: ReturnType<typeof vi.fn>
9
+ } {
10
+ const run = vi.fn()
11
+ const insertContent = vi.fn().mockReturnThis()
12
+ const chain = {
13
+ focus: vi.fn().mockReturnThis(),
14
+ insertContent,
15
+ run,
16
+ }
17
+
18
+ const editor = {
19
+ isDestroyed: false,
20
+ chain: vi.fn(() => chain),
21
+ schema: { nodes: { image: {} } },
22
+ } as unknown as Editor
23
+
24
+ return { editor, insertContent, run }
25
+ }
26
+
27
+ describe('insertCollectionImage', () => {
28
+ it('inserts image node with data URL and collectionAssetSrc', () => {
29
+ const { editor, insertContent, run } = createMockEditor()
30
+ const bytes = Uint8Array.from([0x89, 0x50, 0x4e, 0x47])
31
+
32
+ insertCollectionImage(editor, {
33
+ relativePath: 'assets/figures/chart.png',
34
+ alt: 'Chart',
35
+ bytes,
36
+ mimeType: 'image/png',
37
+ })
38
+
39
+ expect(insertContent).toHaveBeenCalledWith({
40
+ type: 'image',
41
+ attrs: {
42
+ src: expect.stringMatching(/^data:image\/png;base64,/),
43
+ alt: 'Chart',
44
+ collectionAssetSrc: 'assets/figures/chart.png',
45
+ },
46
+ })
47
+ expect(run).toHaveBeenCalled()
48
+ })
49
+
50
+ it('returns false when editor is missing', () => {
51
+ expect(
52
+ insertCollectionImage(null, {
53
+ relativePath: 'assets/a.png',
54
+ alt: 'A',
55
+ bytes: new Uint8Array([1]),
56
+ mimeType: 'image/png',
57
+ }),
58
+ ).toBe(false)
59
+ })
60
+ })
@@ -1,63 +1,63 @@
1
- import type { Editor } from '@tiptap/core'
2
-
3
- export function uint8ArrayToBase64(bytes: Uint8Array): string {
4
- let binary = ''
5
- const chunk = 0x8000
6
- for (let i = 0; i < bytes.length; i += chunk) {
7
- binary += String.fromCharCode(...bytes.subarray(i, i + chunk))
8
- }
9
- return btoa(binary)
10
- }
11
-
12
- export interface InsertCollectionImageOptions {
13
- relativePath: string
14
- alt: string
15
- bytes: Uint8Array
16
- mimeType: string
17
- }
18
-
19
- /** Insert a file collection asset — data URL display + relative path for storage normalize. */
20
- export function insertCollectionImage(
21
- editor: Editor | null | undefined,
22
- options: InsertCollectionImageOptions,
23
- ): boolean {
24
- if (!editor || editor.isDestroyed) return false
25
- const imageNode = editor.schema.nodes.image
26
- if (!imageNode) return false
27
-
28
- const dataUrl = `data:${options.mimeType};base64,${uint8ArrayToBase64(
29
- options.bytes,
30
- )}`
31
- editor
32
- .chain()
33
- .focus()
34
- .insertContent({
35
- type: 'image',
36
- attrs: {
37
- src: dataUrl,
38
- alt: options.alt,
39
- collectionAssetSrc: options.relativePath,
40
- },
41
- })
42
- .run()
43
- return true
44
- }
45
-
46
- export function insertCollectionImageFromBinaryResult(
47
- editor: Editor | null | undefined,
48
- options: {
49
- relativePath: string
50
- alt: string
51
- mimeType: string
52
- base64: string
53
- },
54
- ): boolean {
55
- const binary = atob(options.base64)
56
- const bytes = Uint8Array.from(binary, char => char.charCodeAt(0))
57
- return insertCollectionImage(editor, {
58
- relativePath: options.relativePath,
59
- alt: options.alt,
60
- bytes,
61
- mimeType: options.mimeType,
62
- })
63
- }
1
+ import type { Editor } from '@tiptap/core'
2
+
3
+ export function uint8ArrayToBase64(bytes: Uint8Array): string {
4
+ let binary = ''
5
+ const chunk = 0x8000
6
+ for (let i = 0; i < bytes.length; i += chunk) {
7
+ binary += String.fromCharCode(...bytes.subarray(i, i + chunk))
8
+ }
9
+ return btoa(binary)
10
+ }
11
+
12
+ export interface InsertCollectionImageOptions {
13
+ relativePath: string
14
+ alt: string
15
+ bytes: Uint8Array
16
+ mimeType: string
17
+ }
18
+
19
+ /** Insert a file collection asset — data URL display + relative path for storage normalize. */
20
+ export function insertCollectionImage(
21
+ editor: Editor | null | undefined,
22
+ options: InsertCollectionImageOptions,
23
+ ): boolean {
24
+ if (!editor || editor.isDestroyed) return false
25
+ const imageNode = editor.schema.nodes.image
26
+ if (!imageNode) return false
27
+
28
+ const dataUrl = `data:${options.mimeType};base64,${uint8ArrayToBase64(
29
+ options.bytes,
30
+ )}`
31
+ editor
32
+ .chain()
33
+ .focus()
34
+ .insertContent({
35
+ type: 'image',
36
+ attrs: {
37
+ src: dataUrl,
38
+ alt: options.alt,
39
+ collectionAssetSrc: options.relativePath,
40
+ },
41
+ })
42
+ .run()
43
+ return true
44
+ }
45
+
46
+ export function insertCollectionImageFromBinaryResult(
47
+ editor: Editor | null | undefined,
48
+ options: {
49
+ relativePath: string
50
+ alt: string
51
+ mimeType: string
52
+ base64: string
53
+ },
54
+ ): boolean {
55
+ const binary = atob(options.base64)
56
+ const bytes = Uint8Array.from(binary, char => char.charCodeAt(0))
57
+ return insertCollectionImage(editor, {
58
+ relativePath: options.relativePath,
59
+ alt: options.alt,
60
+ bytes,
61
+ mimeType: options.mimeType,
62
+ })
63
+ }
@@ -1,67 +1,67 @@
1
- import type { Editor } from '@tiptap/core'
2
- import { describe, expect, it, vi } from 'vitest'
3
- import { insertInlineAssetKind } from './insertInlineAssetKind.js'
4
-
5
- function createMockEditor(
6
- overrides: {
7
- insertMermaidBlock?: ReturnType<typeof vi.fn>
8
- insertContent?: ReturnType<typeof vi.fn>
9
- insertBlockMath?: ReturnType<typeof vi.fn>
10
- } = {},
11
- ): Editor {
12
- const run = vi.fn()
13
- const chain = {
14
- focus: vi.fn().mockReturnThis(),
15
- insertMermaidBlock:
16
- overrides.insertMermaidBlock ?? vi.fn().mockReturnThis(),
17
- insertContent: overrides.insertContent ?? vi.fn().mockReturnThis(),
18
- insertBlockMath: overrides.insertBlockMath ?? vi.fn().mockReturnThis(),
19
- run,
20
- }
21
-
22
- return {
23
- isDestroyed: false,
24
- chain: vi.fn(() => chain),
25
- } as unknown as Editor
26
- }
27
-
28
- describe('insertInlineAssetKind', () => {
29
- it('inserts mermaid from sourceCode', () => {
30
- const insertMermaidBlock = vi.fn().mockReturnThis()
31
- const editor = createMockEditor({ insertMermaidBlock })
32
-
33
- insertInlineAssetKind(editor, 'mermaid', 'flowchart LR\n A --> B')
34
-
35
- expect(insertMermaidBlock).toHaveBeenCalledWith('flowchart LR\n A --> B')
36
- })
37
-
38
- it('inserts table HTML from sourceCode', () => {
39
- const insertContent = vi.fn().mockReturnThis()
40
- const editor = createMockEditor({ insertContent })
41
- const table = '<table><tbody><tr><td>Cell</td></tr></tbody></table>'
42
-
43
- insertInlineAssetKind(editor, 'table', table)
44
-
45
- expect(insertContent).toHaveBeenCalledWith(table)
46
- })
47
-
48
- it('inserts block math via insertBlockMath when available', () => {
49
- const insertBlockMath = vi.fn().mockReturnThis()
50
- const editor = createMockEditor({ insertBlockMath })
51
-
52
- insertInlineAssetKind(editor, 'math', '\\alpha + \\beta')
53
-
54
- expect(insertBlockMath).toHaveBeenCalledWith({
55
- latex: '\\alpha + \\beta',
56
- })
57
- })
58
-
59
- it('no-ops when editor is destroyed', () => {
60
- const editor = createMockEditor()
61
- Object.defineProperty(editor, 'isDestroyed', { value: true })
62
-
63
- insertInlineAssetKind(editor, 'mermaid', 'flowchart TD\n A --> B')
64
-
65
- expect(editor.chain).not.toHaveBeenCalled()
66
- })
67
- })
1
+ import type { Editor } from '@tiptap/core'
2
+ import { describe, expect, it, vi } from 'vitest'
3
+ import { insertInlineAssetKind } from './insertInlineAssetKind.js'
4
+
5
+ function createMockEditor(
6
+ overrides: {
7
+ insertMermaidBlock?: ReturnType<typeof vi.fn>
8
+ insertContent?: ReturnType<typeof vi.fn>
9
+ insertBlockMath?: ReturnType<typeof vi.fn>
10
+ } = {},
11
+ ): Editor {
12
+ const run = vi.fn()
13
+ const chain = {
14
+ focus: vi.fn().mockReturnThis(),
15
+ insertMermaidBlock:
16
+ overrides.insertMermaidBlock ?? vi.fn().mockReturnThis(),
17
+ insertContent: overrides.insertContent ?? vi.fn().mockReturnThis(),
18
+ insertBlockMath: overrides.insertBlockMath ?? vi.fn().mockReturnThis(),
19
+ run,
20
+ }
21
+
22
+ return {
23
+ isDestroyed: false,
24
+ chain: vi.fn(() => chain),
25
+ } as unknown as Editor
26
+ }
27
+
28
+ describe('insertInlineAssetKind', () => {
29
+ it('inserts mermaid from sourceCode', () => {
30
+ const insertMermaidBlock = vi.fn().mockReturnThis()
31
+ const editor = createMockEditor({ insertMermaidBlock })
32
+
33
+ insertInlineAssetKind(editor, 'mermaid', 'flowchart LR\n A --> B')
34
+
35
+ expect(insertMermaidBlock).toHaveBeenCalledWith('flowchart LR\n A --> B')
36
+ })
37
+
38
+ it('inserts table HTML from sourceCode', () => {
39
+ const insertContent = vi.fn().mockReturnThis()
40
+ const editor = createMockEditor({ insertContent })
41
+ const table = '<table><tbody><tr><td>Cell</td></tr></tbody></table>'
42
+
43
+ insertInlineAssetKind(editor, 'table', table)
44
+
45
+ expect(insertContent).toHaveBeenCalledWith(table)
46
+ })
47
+
48
+ it('inserts block math via insertBlockMath when available', () => {
49
+ const insertBlockMath = vi.fn().mockReturnThis()
50
+ const editor = createMockEditor({ insertBlockMath })
51
+
52
+ insertInlineAssetKind(editor, 'math', '\\alpha + \\beta')
53
+
54
+ expect(insertBlockMath).toHaveBeenCalledWith({
55
+ latex: '\\alpha + \\beta',
56
+ })
57
+ })
58
+
59
+ it('no-ops when editor is destroyed', () => {
60
+ const editor = createMockEditor()
61
+ Object.defineProperty(editor, 'isDestroyed', { value: true })
62
+
63
+ insertInlineAssetKind(editor, 'mermaid', 'flowchart TD\n A --> B')
64
+
65
+ expect(editor.chain).not.toHaveBeenCalled()
66
+ })
67
+ })
@@ -1,49 +1,49 @@
1
- import type { Editor } from '@tiptap/core'
2
-
3
- export type InlineAssetInsertKind = 'mermaid' | 'table' | 'math'
4
-
5
- const DEFAULT_MERMAID = `flowchart TD
6
- A[Start] --> B[End]`
7
-
8
- const DEFAULT_TABLE = '<table><tbody><tr><td></td></tr></tbody></table>'
9
-
10
- const DEFAULT_MATH = 'E = mc^2'
11
-
12
- /** TipTap commands for inline collection assets — no paths, no IO. */
13
- export function insertInlineAssetKind(
14
- editor: Editor | null | undefined,
15
- kind: InlineAssetInsertKind,
16
- sourceCode?: string,
17
- ): void {
18
- if (!editor || editor.isDestroyed) return
19
-
20
- switch (kind) {
21
- case 'mermaid':
22
- editor
23
- .chain()
24
- .focus()
25
- .insertMermaidBlock(sourceCode?.trim() || DEFAULT_MERMAID)
26
- .run()
27
- return
28
- case 'table':
29
- editor
30
- .chain()
31
- .focus()
32
- .insertContent(sourceCode?.trim() || DEFAULT_TABLE)
33
- .run()
34
- return
35
- case 'math': {
36
- const latex = sourceCode?.trim() || DEFAULT_MATH
37
- const commands = editor.chain().focus() as ReturnType<Editor['chain']> & {
38
- insertBlockMath?: (opts: {
39
- latex: string
40
- }) => ReturnType<Editor['chain']>
41
- }
42
- if (typeof commands.insertBlockMath === 'function') {
43
- commands.insertBlockMath({ latex }).run()
44
- return
45
- }
46
- editor.chain().focus().insertContent(`$$${latex}$$`).run()
47
- }
48
- }
49
- }
1
+ import type { Editor } from '@tiptap/core'
2
+
3
+ export type InlineAssetInsertKind = 'mermaid' | 'table' | 'math'
4
+
5
+ const DEFAULT_MERMAID = `flowchart TD
6
+ A[Start] --> B[End]`
7
+
8
+ const DEFAULT_TABLE = '<table><tbody><tr><td></td></tr></tbody></table>'
9
+
10
+ const DEFAULT_MATH = 'E = mc^2'
11
+
12
+ /** TipTap commands for inline collection assets — no paths, no IO. */
13
+ export function insertInlineAssetKind(
14
+ editor: Editor | null | undefined,
15
+ kind: InlineAssetInsertKind,
16
+ sourceCode?: string,
17
+ ): void {
18
+ if (!editor || editor.isDestroyed) return
19
+
20
+ switch (kind) {
21
+ case 'mermaid':
22
+ editor
23
+ .chain()
24
+ .focus()
25
+ .insertMermaidBlock(sourceCode?.trim() || DEFAULT_MERMAID)
26
+ .run()
27
+ return
28
+ case 'table':
29
+ editor
30
+ .chain()
31
+ .focus()
32
+ .insertContent(sourceCode?.trim() || DEFAULT_TABLE)
33
+ .run()
34
+ return
35
+ case 'math': {
36
+ const latex = sourceCode?.trim() || DEFAULT_MATH
37
+ const commands = editor.chain().focus() as ReturnType<Editor['chain']> & {
38
+ insertBlockMath?: (opts: {
39
+ latex: string
40
+ }) => ReturnType<Editor['chain']>
41
+ }
42
+ if (typeof commands.insertBlockMath === 'function') {
43
+ commands.insertBlockMath({ latex }).run()
44
+ return
45
+ }
46
+ editor.chain().focus().insertContent(`$$${latex}$$`).run()
47
+ }
48
+ }
49
+ }
@@ -1,54 +1,54 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest'
2
-
3
- const mermaidRender = vi.hoisted(() => vi.fn())
4
-
5
- vi.mock('mermaid', () => ({
6
- default: {
7
- initialize: vi.fn(),
8
- render: mermaidRender,
9
- },
10
- }))
11
-
12
- import {
13
- expandMermaidBlocksForPrint,
14
- extractMermaidSourceFromHtml,
15
- renderMermaidPreviewSvg,
16
- } from './mermaidPreview.js'
17
-
18
- describe('mermaidPreview', () => {
19
- beforeEach(() => {
20
- mermaidRender.mockReset()
21
- mermaidRender.mockResolvedValue({
22
- svg: '<svg data-testid="mermaid"></svg>',
23
- })
24
- })
25
-
26
- it('extractMermaidSourceFromHtml preserves arrow syntax', () => {
27
- const html =
28
- '<figure data-type="mermaid"><pre><code class="language-mermaid">flowchart TD\n A --> B</code></pre></figure>'
29
-
30
- expect(extractMermaidSourceFromHtml(html)).toBe('flowchart TD\n A --> B')
31
- })
32
-
33
- it('renderMermaidPreviewSvg returns svg from mermaid.render', async () => {
34
- const svg = await renderMermaidPreviewSvg('flowchart TD\n A --> B', {
35
- theme: 'default',
36
- })
37
- expect(mermaidRender).toHaveBeenCalled()
38
- expect(svg).toContain('data-testid="mermaid"')
39
- })
40
-
41
- it('expandMermaidBlocksForPrint replaces blocks with SVG wrapper', async () => {
42
- const html =
43
- '<figure data-type="mermaid"><pre><code class="language-mermaid">flowchart TD\n A --> B</code></pre></figure>'
44
-
45
- const expanded = await expandMermaidBlocksForPrint(html, {
46
- theme: 'default',
47
- })
48
-
49
- expect(mermaidRender).toHaveBeenCalled()
50
- expect(expanded).toContain('platform-print-mermaid')
51
- expect(expanded).toContain('<svg data-testid="mermaid"></svg>')
52
- expect(expanded).not.toContain('language-mermaid')
53
- })
54
- })
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+
3
+ const mermaidRender = vi.hoisted(() => vi.fn())
4
+
5
+ vi.mock('mermaid', () => ({
6
+ default: {
7
+ initialize: vi.fn(),
8
+ render: mermaidRender,
9
+ },
10
+ }))
11
+
12
+ import {
13
+ expandMermaidBlocksForPrint,
14
+ extractMermaidSourceFromHtml,
15
+ renderMermaidPreviewSvg,
16
+ } from './mermaidPreview.js'
17
+
18
+ describe('mermaidPreview', () => {
19
+ beforeEach(() => {
20
+ mermaidRender.mockReset()
21
+ mermaidRender.mockResolvedValue({
22
+ svg: '<svg data-testid="mermaid"></svg>',
23
+ })
24
+ })
25
+
26
+ it('extractMermaidSourceFromHtml preserves arrow syntax', () => {
27
+ const html =
28
+ '<figure data-type="mermaid"><pre><code class="language-mermaid">flowchart TD\n A --> B</code></pre></figure>'
29
+
30
+ expect(extractMermaidSourceFromHtml(html)).toBe('flowchart TD\n A --> B')
31
+ })
32
+
33
+ it('renderMermaidPreviewSvg returns svg from mermaid.render', async () => {
34
+ const svg = await renderMermaidPreviewSvg('flowchart TD\n A --> B', {
35
+ theme: 'default',
36
+ })
37
+ expect(mermaidRender).toHaveBeenCalled()
38
+ expect(svg).toContain('data-testid="mermaid"')
39
+ })
40
+
41
+ it('expandMermaidBlocksForPrint replaces blocks with SVG wrapper', async () => {
42
+ const html =
43
+ '<figure data-type="mermaid"><pre><code class="language-mermaid">flowchart TD\n A --> B</code></pre></figure>'
44
+
45
+ const expanded = await expandMermaidBlocksForPrint(html, {
46
+ theme: 'default',
47
+ })
48
+
49
+ expect(mermaidRender).toHaveBeenCalled()
50
+ expect(expanded).toContain('platform-print-mermaid')
51
+ expect(expanded).toContain('<svg data-testid="mermaid"></svg>')
52
+ expect(expanded).not.toContain('language-mermaid')
53
+ })
54
+ })