From ba3c83bc1ded5d2c4a7579d11465aab3019384fa Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Fri, 19 Jun 2026 13:10:55 +0100 Subject: [PATCH] feat(ee): docx word export --- .../src/components/common/export-modal.tsx | 90 ++++- apps/client/src/ee/features.ts | 1 + .../features/page/services/page-service.ts | 19 ++ .../src/features/page/types/page.types.ts | 1 + apps/client/src/lib/api-client.ts | 6 +- apps/client/tsconfig.json | 4 +- apps/server/package.json | 41 +-- apps/server/src/common/features.ts | 1 + apps/server/src/ee | 2 +- package.json | 43 +-- packages/editor-ext/package.json | 1 + packages/editor-ext/src/index.ts | 4 + .../src/lib/prosemirror-docx/index.ts | 5 +- .../src/lib/prosemirror-docx/schema.ts | 314 ++++++++++-------- .../src/lib/prosemirror-docx/serializer.ts | 8 +- .../src/lib/prosemirror-docx/test/build.ts | 28 -- .../lib/prosemirror-docx/test/word.spec.ts | 109 ------ .../src/lib/prosemirror-docx/utils.ts | 18 +- pnpm-lock.yaml | 123 ++++--- 19 files changed, 431 insertions(+), 387 deletions(-) delete mode 100644 packages/editor-ext/src/lib/prosemirror-docx/test/build.ts delete mode 100644 packages/editor-ext/src/lib/prosemirror-docx/test/word.spec.ts diff --git a/apps/client/src/components/common/export-modal.tsx b/apps/client/src/components/common/export-modal.tsx index 2a83debf9..bbd58b64d 100644 --- a/apps/client/src/components/common/export-modal.tsx +++ b/apps/client/src/components/common/export-modal.tsx @@ -6,13 +6,21 @@ import { Select, Switch, Divider, + Tooltip, + Badge, } from "@mantine/core"; -import { exportPage } from "@/features/page/services/page-service.ts"; +import { + exportPage, + exportPageToDocx, +} from "@/features/page/services/page-service.ts"; import { useState } from "react"; import { ExportFormat } from "@/features/page/types/page.types.ts"; import { notifications } from "@mantine/notifications"; import { exportSpace } from "@/features/space/services/space-service"; import { useTranslation } from "react-i18next"; +import { Feature } from "@/ee/features"; +import { useHasFeature } from "@/ee/hooks/use-feature"; +import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label"; interface ExportModalProps { id: string; @@ -32,17 +40,25 @@ export default function ExportModal({ const [includeAttachments, setIncludeAttachments] = useState(false); const [isExporting, setIsExporting] = useState(false); const { t } = useTranslation(); + const upgradeLabel = useUpgradeLabel(); + const isDocx = format === ExportFormat.Docx; + const docxEntitled = useHasFeature(Feature.DOCX_EXPORT); + const blockedByLicense = isDocx && !docxEntitled; const handleExport = async () => { setIsExporting(true); try { if (type === "page") { - await exportPage({ - pageId: id, - format, - includeChildren, - includeAttachments, - }); + if (format === ExportFormat.Docx) { + await exportPageToDocx({ pageId: id }); + } else { + await exportPage({ + pageId: id, + format, + includeChildren, + includeAttachments, + }); + } } if (type === "space") { await exportSpace({ spaceId: id, format, includeAttachments }); @@ -88,10 +104,15 @@ export default function ExportModal({
{t("Format")}
- + - {type === "page" && ( + {type === "page" && !isDocx && ( <> @@ -143,7 +164,16 @@ export default function ExportModal({ - + + + @@ -154,23 +184,49 @@ export default function ExportModal({ interface ExportFormatSelection { format: ExportFormat; onChange: (value: string) => void; + includeDocx?: boolean; + docxEntitled?: boolean; } -function ExportFormatSelection({ format, onChange }: ExportFormatSelection) { +function ExportFormatSelection({ + format, + onChange, + includeDocx, + docxEntitled, +}: ExportFormatSelection) { const { t } = useTranslation(); + const data = [ + { value: "markdown", label: "Markdown" }, + { value: "html", label: "HTML" }, + ...(includeDocx + ? [{ value: "docx", label: "Word (.docx)", disabled: !docxEntitled }] + : []), + ]; + return (