diff --git a/src/frontend/apps/e2e/__tests__/app-impress/pad-editor.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/pad-editor.spec.ts
index 7ad22de4..e2bd07e5 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/pad-editor.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/pad-editor.spec.ts
@@ -77,4 +77,34 @@ test.describe('Pad Editor', () => {
expect(pdfText).toContain('La directrice'); // This is the template text
expect(pdfText).toContain('Hello World'); // This is the pad text
});
+
+ test('markdown button converts from markdown to the editor syntax json', async ({
+ page,
+ browserName,
+ }) => {
+ const randomPad = await createPad(page, 'pad-markdown', browserName, 1);
+
+ await expect(page.locator('h2').getByText(randomPad[0])).toBeVisible();
+
+ await page.locator('.ProseMirror.bn-editor').click();
+ await page
+ .locator('.ProseMirror.bn-editor')
+ .fill('[test markdown](http://test-markdown.html)');
+
+ await expect(page.getByText('[test markdown]')).toBeVisible();
+
+ await page.getByText('[test markdown]').dblclick();
+ await page
+ .getByRole('button', {
+ name: 'M',
+ })
+ .click();
+
+ await expect(page.getByText('[test markdown]')).toBeHidden();
+ await expect(
+ page.getByRole('link', {
+ name: 'test markdown',
+ }),
+ ).toHaveAttribute('href', 'http://test-markdown.html');
+ });
});
diff --git a/src/frontend/apps/impress/src/features/pads/pad/components/BlockNoteEditor.tsx b/src/frontend/apps/impress/src/features/pads/pad/components/BlockNoteEditor.tsx
index 83bd7d32..c628e285 100644
--- a/src/frontend/apps/impress/src/features/pads/pad/components/BlockNoteEditor.tsx
+++ b/src/frontend/apps/impress/src/features/pads/pad/components/BlockNoteEditor.tsx
@@ -10,6 +10,8 @@ import { usePadStore } from '../stores';
import { Pad } from '../types';
import { randomColor } from '../utils';
+import { BlockNoteToolbar } from './BlockNoteToolbar';
+
interface BlockNoteEditorProps {
pad: Pad;
}
@@ -67,7 +69,9 @@ export const BlockNoteContent = ({ pad, provider }: BlockNoteContentProps) => {
};
`}
>
-
+
+
+
);
};
diff --git a/src/frontend/apps/impress/src/features/pads/pad/components/BlockNoteToolbar.tsx b/src/frontend/apps/impress/src/features/pads/pad/components/BlockNoteToolbar.tsx
new file mode 100644
index 00000000..ed451b94
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/pads/pad/components/BlockNoteToolbar.tsx
@@ -0,0 +1,129 @@
+import {
+ BasicTextStyleButton,
+ BlockTypeSelect,
+ ColorStyleButton,
+ CreateLinkButton,
+ FormattingToolbar,
+ FormattingToolbarController,
+ ImageCaptionButton,
+ NestBlockButton,
+ ReplaceImageButton,
+ TextAlignButton,
+ ToolbarButton,
+ UnnestBlockButton,
+ useBlockNoteEditor,
+} from '@blocknote/react';
+import '@blocknote/react/style.css';
+import { forEach, isArray } from 'lodash';
+import React from 'react';
+
+export const BlockNoteToolbar = () => {
+ return (
+ (
+
+
+
+ {/* Extra button to convert from markdown to json */}
+
+
+
+
+
+
+
+
+
+ {/* Extra button to toggle code styles */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+ />
+ );
+};
+
+type Block = {
+ type: string;
+ text: string;
+ content: Block[];
+};
+
+function isBlock(block: Block): block is Block {
+ return (
+ block.content &&
+ isArray(block.content) &&
+ block.content.length > 0 &&
+ typeof block.type !== 'undefined'
+ );
+}
+
+const recursiveContent = (content: Block[], base: string = '') => {
+ let fullContent = base;
+ for (const innerContent of content) {
+ if (innerContent.type === 'text') {
+ fullContent += innerContent.text;
+ } else if (isBlock(innerContent)) {
+ fullContent = recursiveContent(innerContent.content, fullContent);
+ }
+ }
+
+ return fullContent;
+};
+
+/**
+ * Custom Formatting Toolbar Button to convert markdown to json.
+ */
+export function MarkdownButton() {
+ const editor = useBlockNoteEditor();
+
+ const handleConvertMarkdown = () => {
+ const blocks = editor.getSelection()?.blocks;
+
+ forEach(blocks, async (block) => {
+ if (!isBlock(block as unknown as Block)) {
+ return;
+ }
+
+ try {
+ const fullContent = recursiveContent(
+ block.content as unknown as Block[],
+ );
+
+ const blockMarkdown =
+ await editor.tryParseMarkdownToBlocks(fullContent);
+ editor.replaceBlocks([block.id], blockMarkdown);
+ } catch (error) {
+ console.error('Error parsing Markdown:', error);
+ }
+ });
+ };
+
+ return (
+
+ M
+
+ );
+}