diff --git a/CHANGELOG.md b/CHANGELOG.md index e4e42d1b58..23a369d52b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to ### Added +- ✨(frontend) add markdown download option #2608 - ✨(frontend) Add "Copy link to block" feature #2547 ### Changed diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts index c6dfbb6834..6bf41e732f 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts @@ -30,7 +30,7 @@ test.describe('Doc Export', () => { await expect(page.getByTestId('modal-export-title')).toBeVisible(); await expect( page.getByText( - 'Export your document to download in .pdf, .docx, .odt or .html(zip) format.', + 'Export your document to download in .pdf, .docx, .odt, .md or .html(zip) format.', ), ).toBeVisible(); await expect(page.getByRole('combobox', { name: 'Format' })).toBeVisible(); @@ -94,6 +94,32 @@ test.describe('Doc Export', () => { expect(download.suggestedFilename()).toBe(`${randomDoc}.odt`); }); + test('it exports the doc to markdown', async ({ page, browserName }) => { + const [randomDoc] = await createDoc( + page, + 'doc-editor-markdown', + browserName, + 1, + ); + + await verifyDocName(page, randomDoc); + await writeInEditor({ page, text: 'Hello Markdown export' }); + + await clickInEditorMenu(page, 'Download'); + + await page.getByRole('combobox', { name: 'Format' }).click(); + await page.getByRole('option', { name: 'Markdown' }).click(); + + const downloadPromise = page.waitForEvent('download'); + await page.getByTestId('doc-export-download-button').click(); + + const download = await downloadPromise; + expect(download.suggestedFilename()).toBe(`${randomDoc}.md`); + + const markdownBuffer = await cs.toBuffer(await download.createReadStream()); + expect(markdownBuffer.toString('utf8')).toContain('Hello Markdown export'); + }); + test('it exports the doc to html zip', async ({ page, browserName }) => { const [randomDoc] = await createDoc( page, diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx index 70a5188981..2ddd8ddc1d 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx @@ -59,6 +59,11 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => { const formatSelect = useMemo(() => { const formatOptions = (exportAGPL?.formats || []).concat([ + { + label: t('Markdown'), + value: 'markdown', + labelDescription: t('.md'), + }, { label: t('HTML'), value: 'html', @@ -101,6 +106,13 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => { let blobExport = await exportAGPL?.docToBlob(format, documentTitle); + if (!blobExport && format === 'markdown') { + const markdown = await editor.blocksToMarkdownLossy(); + blobExport = new Blob([markdown], { + type: 'text/markdown;charset=utf-8', + }); + } + if (!blobExport && format === 'html') { // Use BlockNote "full HTML" export so that we stay closer to the editor rendering. const fullHtml = await editor.blocksToFullHTML(); @@ -142,7 +154,8 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => { return; } - const downloadExtension = format === 'html' ? 'zip' : format; + const downloadExtension = + format === 'html' ? 'zip' : format === 'markdown' ? 'md' : format; downloadFile(blobExport, `${filename}.${downloadExtension}`);