Plate
PlateEditorsTemplates
GitHub16kGitHub
DiscordDiscord
  • HTML Export
  • Export
  • Server-Side
  • Version History
  • Editable Voids
  • Huge Document
  • Hundreds Editors
  • Markdown Streaming
  • Preview Markdown
  • Collaboration Demo
  • Table Nomerge Demo
  • Excalidraw Demo
  • Code Drawing Demo
  • Single Block Demo
  • Find Demo
  • AI Demo
  • Align Demo
  • Autoformat Demo
  • Basic Nodes Demo
  • Block Menu Demo
  • Node Selection Demo
  • Column Demo
  • Code Block Demo
  • Callout Demo
  • Discussion Demo
  • Date Demo
  • Footnote Demo
  • Drag & Drop Demo
  • Emoji Demo
  • Equation Demo
  • Exit Break Demo
  • Floating Toolbar Demo
  • Font Demo
  • Indent Demo
  • List Demo
  • Line Height Demo
  • Link Demo
  • Media Demo
  • Mention Demo
  • Block Placeholder Demo
  • Serializing CSV Demo
  • Serializing Docx Demo
  • Serializing HTML Demo
  • Serializing Markdown Demo
  • Slash Command Demo
  • Plugin Rules Demo
  • Table Demo
  • Table of Contents Demo
  • Details Demo

Export

PreviousNext

Export a Plate document to HTML, PDF, image, Markdown, or Word.

Plus

This example exports the current Plate editor value from the browser. The registry export-toolbar-button owns the client-side download menu for HTML, PDF, image, Markdown, and Word output.

Demo

Loading…
HTML ExportServer-Side

On This Page

DemoToolbar sourceExport formatsDOCX static kitPlus exportRelated
Build your editor
Production-ready AI template and reusable components.
Get all-access

Toolbar source

Install the Export Toolbar Button component to add the menu to an editor toolbar.

'use client';
 
import { ArrowDownToLineIcon } from 'lucide-react';
import { createEditor } from 'platejs';
import { MarkdownPlugin } from 'platejs/markdown';
import { useEditor, useModelEditor } from 'platejs/react';
import { renderStaticHtml } from 'platejs/static';
import * as React from 'react';
import { toast } from 'sonner';
 
import {
  DOCX_EXPORT_STYLES,
  DocxExportKit,
} from '@/components/editor/docx-export';
import { useDocxSource } from '@/components/editor/docx-source';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/editor/dropdown-menu';
import { BaseEditorKit } from '@/components/editor/plugins-static';
import { ToolbarButton } from '@/components/editor/toolbar';
 
import { EditorStatic } from './editor-static';
 
const siteUrl = 'https://platejs.org';
 
const downloadFile = async (url: string, filename: string) => {
  const response = await fetch(url);
  const blob = await response.blob();
  const blobUrl = window.URL.createObjectURL(blob);
  const link = document.createElement('a');
 
  link.href = blobUrl;
  link.download = filename;
  document.body.append(link);
  link.click();
  link.remove();
  window.URL.revokeObjectURL(blobUrl);
};
 
export function ExportToolbarButton() {
  const editor = useEditor();
  const model = useModelEditor();
  const [open, setOpen] = React.useState(false);
  const docxSource = useDocxSource();
 
  const getCanvas = async () => {
    const { default: html2canvas } = await import('html2canvas-pro');
 
    const editorElement = editor.api.dom.resolveDOMNode(editor);
 
    if (!editorElement) {
      throw new Error('Cannot resolve editor DOM node for export.');
    }
 
    const canvas = await html2canvas(editorElement, {
      onclone: (document: Document) => {
        const innerEditorElement = document.querySelector(
          '[contenteditable="true"]'
        );
        if (innerEditorElement) {
          Array.from(innerEditorElement.querySelectorAll('*')).forEach(
            (element) => {
              const existingStyle = element.getAttribute('style') || '';
              element.setAttribute(
                'style',
                `${existingStyle}; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important`
              );
            }
          );
        }
      },
    });
 
    return canvas;
  };
 
  const exportToPdf = async () => {
    const [canvas, PDFLib] = await Promise.all([
      getCanvas(),
      import('pdf-lib'),
    ]);
    const pdfDoc = await PDFLib.PDFDocument.create();
    const page = pdfDoc.addPage([canvas.width, canvas.height]);
    const imageEmbed = await pdfDoc.embedPng(canvas.toDataURL('PNG'));
    const { height, width } = imageEmbed.scale(1);
    page.drawImage(imageEmbed, {
      height,
      width,
      x: 0,
      y: 0,
    });
    const pdfBase64 = await pdfDoc.saveAsBase64({ dataUri: true });
 
    await downloadFile(pdfBase64, 'plate.pdf');
  };
 
  const exportToImage = async () => {
    const canvas = await getCanvas();
    await downloadFile(canvas.toDataURL('image/png'), 'plate.png');
  };
 
  const exportToHtml = async () => {
    const editorStatic = createEditor({
      plugins: BaseEditorKit,
      initialValue: editor.read.children(),
    });
 
    const editorHtml = await renderStaticHtml(editorStatic, {
      editorComponent: EditorStatic,
      props: { style: { padding: '0 calc(50% - 350px)', paddingBottom: '' } },
    });
 
    const tailwindCss = `<link rel="stylesheet" href="${siteUrl}/tailwind.css">`;
    const katexCss = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.18/dist/katex.css" integrity="sha384-9PvLvaiSKCPkFKB1ZsEoTjgnJn+O3KvEwtsz37/XrkYft3DTk2gHdYvd9oWgW3tV" crossorigin="anonymous">`;
 
    const html = `<!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta name="color-scheme" content="light dark" />
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
        <link
          href="https://fonts.googleapis.com/css2?family=Inter:wght@400..700&family=JetBrains+Mono:wght@400..700&display=swap"
          rel="stylesheet"
        />
        ${tailwindCss}
        ${katexCss}
        <style>
          :root {
            --font-sans: 'Inter', 'Inter Fallback';
            --font-mono: 'JetBrains Mono', 'JetBrains Mono Fallback';
          }
        </style>
      </head>
      <body>
        ${editorHtml}
      </body>
    </html>`;
 
    const url = `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
 
    await downloadFile(url, 'plate.html');
  };
 
  const exportToMarkdown = async () => {
    const md = editor.plugin(MarkdownPlugin).api.serialize();
    const url = `data:text/markdown;charset=utf-8,${encodeURIComponent(md)}`;
    await downloadFile(url, 'plate.md');
  };
 
  const exportToWord = async () => {
    const { exportToDocx } = await import('platejs/docx/export');
    const result = await exportToDocx(model, {
      editorPlugins: [...BaseEditorKit, ...DocxExportKit],
      projection: docxSource ? 'review' : 'proposed',
      source: docxSource?.source ?? undefined,
      stylesheet: DOCX_EXPORT_STYLES,
    });
 
    if (!result.ok) {
      toast.error(
        result.diagnostics.find(({ severity }) => severity === 'error')
          ?.message ?? 'The Word document could not be exported.'
      );
 
      return;
    }
 
    const url = URL.createObjectURL(result.blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = 'plate.docx';
    document.body.append(link);
    link.click();
    link.remove();
    URL.revokeObjectURL(url);
 
    const warningCount = result.diagnostics.filter(
      ({ severity }) => severity === 'warning'
    ).length;
 
    if (warningCount > 0) {
      toast.warning(
        `Exported with ${warningCount} warning${warningCount === 1 ? '' : 's'}.`
      );
    }
  };
 
  return (
    <DropdownMenu open={open} onOpenChange={setOpen} modal={false}>
      <DropdownMenuTrigger>
        <ToolbarButton
          aria-label="Export"
          pressed={open}
          tooltip="Export"
          isDropdown
        >
          <ArrowDownToLineIcon className="size-4" />
        </ToolbarButton>
      </DropdownMenuTrigger>
 
      <DropdownMenuContent align="start">
        <DropdownMenuGroup>
          <DropdownMenuItem onSelect={exportToHtml}>
            Export as HTML
          </DropdownMenuItem>
          <DropdownMenuItem onSelect={exportToPdf}>
            Export as PDF
          </DropdownMenuItem>
          <DropdownMenuItem onSelect={exportToImage}>
            Export as Image
          </DropdownMenuItem>
          <DropdownMenuItem onSelect={exportToMarkdown}>
            Export as Markdown
          </DropdownMenuItem>
          <DropdownMenuItem onSelect={exportToWord}>
            Export as Word
          </DropdownMenuItem>
        </DropdownMenuGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}
'use client';
 
import { ArrowDownToLineIcon } from 'lucide-react';
import { createEditor } from 'platejs';
import { MarkdownPlugin } from 'platejs/markdown';
import { useEditor, useModelEditor } from 'platejs/react';
import { renderStaticHtml } from 'platejs/static';
import * as React from 'react';
import { toast } from 'sonner';
 
import {
  DOCX_EXPORT_STYLES,
  DocxExportKit,
} from '@/components/editor/docx-export';
import { useDocxSource } from '@/components/editor/docx-source';





















































































































































































































Export formats

FormatImplementationOutput
HTMLrenderStaticHtml from platejs/static with BaseEditorKit and EditorStaticplate.html
PDFhtml2canvas-pro snapshot converted with pdf-libplate.pdf
Imagehtml2canvas-pro snapshotplate.png
Markdowneditor.api.markdown.serialize() from MarkdownPluginplate.md
WordexportToDocx(model, { projection, editorPlugins, stylesheet: DOCX_EXPORT_STYLES }) from platejs/docx/exportplate.docx

The PDF and image exporters snapshot the current editable DOM. Use them for a quick client-side download, not for paginated print layout.

The copied toolbar gets model from useModelEditor() for Word export. The mounted view from useEditor() remains the owner for DOM, Markdown, and root-scoped command behavior.

DOCX static kit

Word export passes BaseEditorKit, DocxExportKit, and DOCX_EXPORT_STYLES to the package operation.

import { BaseHeadingPlugin, BaseCodeBlockPlugin } from 'platejs';
import { BaseCalloutPlugin } from 'platejs/callout';
import { BaseColumnItemPlugin, BaseColumnPlugin } from 'platejs/layout';
import { BaseEquationPlugin, BaseInlineEquationPlugin } from 'platejs/math';
import { BaseTocPlugin } from 'platejs/toc';
 
import { CalloutElementDocx } from '@/components/editor/callout-static';
import { CodeBlockElementDocx } from '@/components/editor/code-block-static';
import {
  ColumnElementDocx,
  ColumnGroupElementDocx,
} from '@/components/editor/column-static';
import { HeadingElementDocx } from '@/components/editor/heading-static';
import {
  EquationElementDocx,
  InlineEquationElementDocx,
} from '@/components/editor/math-static';
import { TocElementDocx } from '@/components/editor/toc-static';
 
export const DOCX_EXPORT_STYLES = `
body {
  font-family: 'Calibri', 'Arial', sans-serif;
  font-size: 11pt;
  line-height: 1.5;
  color: #000;
  margin: 0;
  padding: 20px;
}
h1 { font-size: 24pt; font-weight: bold; margin: 0 0 12pt 0; }
h2 { font-size: 18pt; font-weight: bold; margin: 0 0 10pt 0; }
h3 { font-size: 14pt; font-weight: bold; margin: 0 0 8pt 0; }
h4 { font-size: 12pt; font-weight: bold; margin: 0 0 6pt 0; }
h5 { font-size: 11pt; font-weight: bold; margin: 0 0 6pt 0; }
h6 { font-size: 10pt; font-weight: bold; margin: 0 0 6pt 0; }
p { margin: 0 0 8pt 0; }
ul, ol { margin: 0 0 8pt 0; padding-left: 20pt; }
li { margin: 0 0 4pt 0; }
strong, b { font-weight: bold; }
em, i { font-style: italic; }
u { text-decoration: underline; }
s, strike, del { text-decoration: line-through; }
code {
  font-family: 'Courier New', Consolas, monospace;
  background-color: #f5f5f5;
  padding: 2px 4px;
  border-radius: 3px;
}
pre {
  font-family: 'Courier New', Consolas, monospace;
  background-color: #f5f5f5;
  padding: 10px;
  margin: 0 0 8pt 0;
  white-space: pre-wrap;
  border-radius: 4px;
}
.hljs-addition, .hljs-name, .hljs-quote, .hljs-selector-pseudo, .hljs-selector-tag { color: #22863a; }
.hljs-attr, .hljs-attribute, .hljs-literal, .hljs-meta, .hljs-number, .hljs-operator,
.hljs-section, .hljs-selector-attr, .hljs-selector-class, .hljs-selector-id, .hljs-variable { color: #005cc5; }
.hljs-built_in, .hljs-symbol { color: #e36209; }
.hljs-bullet { color: #735c0f; }
.hljs-comment, .hljs-formula { color: #6a737d; }
.hljs-deletion { color: #b31d28; }
.hljs-doctag, .hljs-keyword, .hljs-template-tag, .hljs-template-variable, .hljs-type { color: #d73a49; }
.hljs-regexp, .hljs-string { color: #032f62; }
.hljs-title { color: #6f42c1; }
.hljs-emphasis { font-style: italic; }
.hljs-section, .hljs-strong { font-weight: bold; }
blockquote {
  border-left: 3px solid #ccc;
  margin: 0 0 8pt 0;
  padding-left: 10pt;
  color: #666;
  font-style: italic;
}
table {
  border-collapse: collapse;
  width: 100%;
  margin: 0 0 8pt 0;
}
th, td {
  border: 1px solid #ccc;
  padding: 6pt;
  text-align: left;
}
th {
  background-color: #f5f5f5;
  font-weight: bold;
}
a {
  color: #0066cc;
  text-decoration: underline;
}
img {
  max-width: 100%;
  height: auto;
}
hr {
  border: none;
  border-top: 1px solid #ccc;
  margin: 12pt 0;
}
sup { vertical-align: super; font-size: 8pt; }
sub { vertical-align: sub; font-size: 8pt; }
mark { background-color: #ffff00; }
`.trim();
 
/**
 * Editor kit for DOCX export.
 *
 * Uses standard static components for most elements (with juice CSS inlining),
 * but uses docx-specific components for elements that need special handling:
 * - Code blocks (syntax highlighting and preserved whitespace)
 * - Columns (table layout instead of flexbox)
 * - Equations (inline font instead of KaTeX)
 * - Callouts (table layout for icon placement)
 * - Headings (bookmark anchors for TOC links)
 * - TOC (anchor links with paragraph breaks)
 *
 * Tables use base version with juice CSS inlining.
 */
export const DocxExportKit = [
  BaseCodeBlockPlugin.configure({
    component: CodeBlockElementDocx,
  }),
  BaseColumnItemPlugin.configure({
    component: ColumnElementDocx,
  }),
  BaseColumnPlugin.configure({
    component: ColumnGroupElementDocx,
  }),
  BaseEquationPlugin.configure({
    component: EquationElementDocx,
  }),
  BaseInlineEquationPlugin.configure({
    component: InlineEquationElementDocx,
  }),
  BaseCalloutPlugin.configure({
    component: CalloutElementDocx,
  }),
  BaseHeadingPlugin.configure({
    component: HeadingElementDocx,
  }),
  BaseTocPlugin.configure({
    component: TocElementDocx,
  }),
];
import { BaseHeadingPlugin, BaseCodeBlockPlugin } from 'platejs';
import { BaseCalloutPlugin } from 'platejs/callout';
import { BaseColumnItemPlugin, BaseColumnPlugin } from 'platejs/layout';
import { BaseEquationPlugin, BaseInlineEquationPlugin } from 'platejs/math';
import { BaseTocPlugin } from 'platejs/toc';
 
import { CalloutElementDocx } from '@/components/editor/callout-static';
import { CodeBlockElementDocx } from '@/components/editor/code-block-static';
import {
  ColumnElementDocx,
  ColumnGroupElementDocx,
} from '@/components/editor/column-static';
import { HeadingElementDocx } from '@/components/editor/heading-static';




































































































































DocxExportKit overrides code blocks, columns, equations, callouts, and table of contents rendering. DOCX_EXPORT_STYLES defines the copied visual preset.

Plus export

Plate Plus includes a server-side export flow for PDF output with page settings.

Get the code

Related

  • DOCX covers DOCX paste, file import, and export APIs.
  • Markdown covers Markdown serialization.
  • Static Rendering covers static editor rendering and HTML serialization.
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/editor/dropdown-menu';
import { BaseEditorKit } from '@/components/editor/plugins-static';
import { ToolbarButton } from '@/components/editor/toolbar';
import { EditorStatic } from './editor-static';
const siteUrl = 'https://platejs.org';
const downloadFile = async (url: string, filename: string) => {
const response = await fetch(url);
const blob = await response.blob();
const blobUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = blobUrl;
link.download = filename;
document.body.append(link);
link.click();
link.remove();
window.URL.revokeObjectURL(blobUrl);
};
export function ExportToolbarButton() {
const editor = useEditor();
const model = useModelEditor();
const [open, setOpen] = React.useState(false);
const docxSource = useDocxSource();
const getCanvas = async () => {
const { default: html2canvas } = await import('html2canvas-pro');
const editorElement = editor.api.dom.resolveDOMNode(editor);
if (!editorElement) {
throw new Error('Cannot resolve editor DOM node for export.');
}
const canvas = await html2canvas(editorElement, {
onclone: (document: Document) => {
const innerEditorElement = document.querySelector(
'[contenteditable="true"]'
);
if (innerEditorElement) {
Array.from(innerEditorElement.querySelectorAll('*')).forEach(
(element) => {
const existingStyle = element.getAttribute('style') || '';
element.setAttribute(
'style',
`${existingStyle}; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important`
);
}
);
}
},
});
return canvas;
};
const exportToPdf = async () => {
const [canvas, PDFLib] = await Promise.all([
getCanvas(),
import('pdf-lib'),
]);
const pdfDoc = await PDFLib.PDFDocument.create();
const page = pdfDoc.addPage([canvas.width, canvas.height]);
const imageEmbed = await pdfDoc.embedPng(canvas.toDataURL('PNG'));
const { height, width } = imageEmbed.scale(1);
page.drawImage(imageEmbed, {
height,
width,
x: 0,
y: 0,
});
const pdfBase64 = await pdfDoc.saveAsBase64({ dataUri: true });
await downloadFile(pdfBase64, 'plate.pdf');
};
const exportToImage = async () => {
const canvas = await getCanvas();
await downloadFile(canvas.toDataURL('image/png'), 'plate.png');
};
const exportToHtml = async () => {
const editorStatic = createEditor({
plugins: BaseEditorKit,
initialValue: editor.read.children(),
});
const editorHtml = await renderStaticHtml(editorStatic, {
editorComponent: EditorStatic,
props: { style: { padding: '0 calc(50% - 350px)', paddingBottom: '' } },
});
const tailwindCss = `<link rel="stylesheet" href="${siteUrl}/tailwind.css">`;
const katexCss = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.18/dist/katex.css" integrity="sha384-9PvLvaiSKCPkFKB1ZsEoTjgnJn+O3KvEwtsz37/XrkYft3DTk2gHdYvd9oWgW3tV" crossorigin="anonymous">`;
const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="light dark" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400..700&family=JetBrains+Mono:wght@400..700&display=swap"
rel="stylesheet"
/>
${tailwindCss}
${katexCss}
<style>
:root {
--font-sans: 'Inter', 'Inter Fallback';
--font-mono: 'JetBrains Mono', 'JetBrains Mono Fallback';
}
</style>
</head>
<body>
${editorHtml}
</body>
</html>`;
const url = `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
await downloadFile(url, 'plate.html');
};
const exportToMarkdown = async () => {
const md = editor.plugin(MarkdownPlugin).api.serialize();
const url = `data:text/markdown;charset=utf-8,${encodeURIComponent(md)}`;
await downloadFile(url, 'plate.md');
};
const exportToWord = async () => {
const { exportToDocx } = await import('platejs/docx/export');
const result = await exportToDocx(model, {
editorPlugins: [...BaseEditorKit, ...DocxExportKit],
projection: docxSource ? 'review' : 'proposed',
source: docxSource?.source ?? undefined,
stylesheet: DOCX_EXPORT_STYLES,
});
if (!result.ok) {
toast.error(
result.diagnostics.find(({ severity }) => severity === 'error')
?.message ?? 'The Word document could not be exported.'
);
return;
}
const url = URL.createObjectURL(result.blob);
const link = document.createElement('a');
link.href = url;
link.download = 'plate.docx';
document.body.append(link);
link.click();
link.remove();
URL.revokeObjectURL(url);
const warningCount = result.diagnostics.filter(
({ severity }) => severity === 'warning'
).length;
if (warningCount > 0) {
toast.warning(
`Exported with ${warningCount} warning${warningCount === 1 ? '' : 's'}.`
);
}
};
return (
<DropdownMenu open={open} onOpenChange={setOpen} modal={false}>
<DropdownMenuTrigger>
<ToolbarButton
aria-label="Export"
pressed={open}
tooltip="Export"
isDropdown
>
<ArrowDownToLineIcon className="size-4" />
</ToolbarButton>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuGroup>
<DropdownMenuItem onSelect={exportToHtml}>
Export as HTML
</DropdownMenuItem>
<DropdownMenuItem onSelect={exportToPdf}>
Export as PDF
</DropdownMenuItem>
<DropdownMenuItem onSelect={exportToImage}>
Export as Image
</DropdownMenuItem>
<DropdownMenuItem onSelect={exportToMarkdown}>
Export as Markdown
</DropdownMenuItem>
<DropdownMenuItem onSelect={exportToWord}>
Export as Word
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
}
import
{
EquationElementDocx,
InlineEquationElementDocx,
} from '@/components/editor/math-static';
import { TocElementDocx } from '@/components/editor/toc-static';
export const DOCX_EXPORT_STYLES = `
body {
font-family: 'Calibri', 'Arial', sans-serif;
font-size: 11pt;
line-height: 1.5;
color: #000;
margin: 0;
padding: 20px;
}
h1 { font-size: 24pt; font-weight: bold; margin: 0 0 12pt 0; }
h2 { font-size: 18pt; font-weight: bold; margin: 0 0 10pt 0; }
h3 { font-size: 14pt; font-weight: bold; margin: 0 0 8pt 0; }
h4 { font-size: 12pt; font-weight: bold; margin: 0 0 6pt 0; }
h5 { font-size: 11pt; font-weight: bold; margin: 0 0 6pt 0; }
h6 { font-size: 10pt; font-weight: bold; margin: 0 0 6pt 0; }
p { margin: 0 0 8pt 0; }
ul, ol { margin: 0 0 8pt 0; padding-left: 20pt; }
li { margin: 0 0 4pt 0; }
strong, b { font-weight: bold; }
em, i { font-style: italic; }
u { text-decoration: underline; }
s, strike, del { text-decoration: line-through; }
code {
font-family: 'Courier New', Consolas, monospace;
background-color: #f5f5f5;
padding: 2px 4px;
border-radius: 3px;
}
pre {
font-family: 'Courier New', Consolas, monospace;
background-color: #f5f5f5;
padding: 10px;
margin: 0 0 8pt 0;
white-space: pre-wrap;
border-radius: 4px;
}
.hljs-addition, .hljs-name, .hljs-quote, .hljs-selector-pseudo, .hljs-selector-tag { color: #22863a; }
.hljs-attr, .hljs-attribute, .hljs-literal, .hljs-meta, .hljs-number, .hljs-operator,
.hljs-section, .hljs-selector-attr, .hljs-selector-class, .hljs-selector-id, .hljs-variable { color: #005cc5; }
.hljs-built_in, .hljs-symbol { color: #e36209; }
.hljs-bullet { color: #735c0f; }
.hljs-comment, .hljs-formula { color: #6a737d; }
.hljs-deletion { color: #b31d28; }
.hljs-doctag, .hljs-keyword, .hljs-template-tag, .hljs-template-variable, .hljs-type { color: #d73a49; }
.hljs-regexp, .hljs-string { color: #032f62; }
.hljs-title { color: #6f42c1; }
.hljs-emphasis { font-style: italic; }
.hljs-section, .hljs-strong { font-weight: bold; }
blockquote {
border-left: 3px solid #ccc;
margin: 0 0 8pt 0;
padding-left: 10pt;
color: #666;
font-style: italic;
}
table {
border-collapse: collapse;
width: 100%;
margin: 0 0 8pt 0;
}
th, td {
border: 1px solid #ccc;
padding: 6pt;
text-align: left;
}
th {
background-color: #f5f5f5;
font-weight: bold;
}
a {
color: #0066cc;
text-decoration: underline;
}
img {
max-width: 100%;
height: auto;
}
hr {
border: none;
border-top: 1px solid #ccc;
margin: 12pt 0;
}
sup { vertical-align: super; font-size: 8pt; }
sub { vertical-align: sub; font-size: 8pt; }
mark { background-color: #ffff00; }
`.trim();
/**
* Editor kit for DOCX export.
*
* Uses standard static components for most elements (with juice CSS inlining),
* but uses docx-specific components for elements that need special handling:
* - Code blocks (syntax highlighting and preserved whitespace)
* - Columns (table layout instead of flexbox)
* - Equations (inline font instead of KaTeX)
* - Callouts (table layout for icon placement)
* - Headings (bookmark anchors for TOC links)
* - TOC (anchor links with paragraph breaks)
*
* Tables use base version with juice CSS inlining.
*/
export const DocxExportKit = [
BaseCodeBlockPlugin.configure({
component: CodeBlockElementDocx,
}),
BaseColumnItemPlugin.configure({
component: ColumnElementDocx,
}),
BaseColumnPlugin.configure({
component: ColumnGroupElementDocx,
}),
BaseEquationPlugin.configure({
component: EquationElementDocx,
}),
BaseInlineEquationPlugin.configure({
component: InlineEquationElementDocx,
}),
BaseCalloutPlugin.configure({
component: CalloutElementDocx,
}),
BaseHeadingPlugin.configure({
component: HeadingElementDocx,
}),
BaseTocPlugin.configure({
component: TocElementDocx,
}),
];