From 287b833838c316c37433270fd88b1412c7f4ff3a Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Sat, 4 Jan 2025 16:57:36 +0000 Subject: [PATCH] feat: support pasting markdown (#606) --- .../features/editor/extensions/extensions.ts | 4 ++ apps/server/package.json | 1 - .../src/integrations/import/import.service.ts | 2 +- package.json | 1 + packages/editor-ext/src/index.ts | 2 +- packages/editor-ext/src/lib/markdown/index.ts | 2 + .../src/lib/markdown/markdown-clipboard.ts | 43 +++++++++++++++++++ .../src/lib/markdown}/utils/callout.marked.ts | 0 .../src/lib/markdown}/utils/marked.utils.ts | 2 +- .../lib/markdown}/utils/math-block.marked.ts | 0 .../lib/markdown}/utils/math-inline.marked.ts | 0 pnpm-lock.yaml | 6 +-- 12 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 packages/editor-ext/src/lib/markdown/index.ts create mode 100644 packages/editor-ext/src/lib/markdown/markdown-clipboard.ts rename {apps/server/src/integrations/import => packages/editor-ext/src/lib/markdown}/utils/callout.marked.ts (100%) rename {apps/server/src/integrations/import => packages/editor-ext/src/lib/markdown}/utils/marked.utils.ts (93%) rename {apps/server/src/integrations/import => packages/editor-ext/src/lib/markdown}/utils/math-block.marked.ts (100%) rename {apps/server/src/integrations/import => packages/editor-ext/src/lib/markdown}/utils/math-inline.marked.ts (100%) diff --git a/apps/client/src/features/editor/extensions/extensions.ts b/apps/client/src/features/editor/extensions/extensions.ts index 40b648b0..83da4bde 100644 --- a/apps/client/src/features/editor/extensions/extensions.ts +++ b/apps/client/src/features/editor/extensions/extensions.ts @@ -36,6 +36,7 @@ import { Drawio, Excalidraw, Embed, + MarkdownClipboard, } from "@docmost/editor-ext"; import { randomElement, @@ -186,6 +187,9 @@ export const mainExtensions = [ Embed.configure({ view: EmbedView, }), + MarkdownClipboard.configure({ + transformPastedText: true, + }), ] as any; type CollabExtensions = (provider: HocuspocusProvider, user: IUser) => any[]; diff --git a/apps/server/package.json b/apps/server/package.json index 9219210a..224cf615 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -58,7 +58,6 @@ "happy-dom": "^15.11.6", "kysely": "^0.27.4", "kysely-migration-cli": "^0.4.2", - "marked": "^13.0.3", "mime-types": "^2.1.35", "nanoid": "^5.0.9", "nestjs-kysely": "^1.0.0", diff --git a/apps/server/src/integrations/import/import.service.ts b/apps/server/src/integrations/import/import.service.ts index a4eb8051..f77df0dc 100644 --- a/apps/server/src/integrations/import/import.service.ts +++ b/apps/server/src/integrations/import/import.service.ts @@ -11,9 +11,9 @@ import { InjectKysely } from 'nestjs-kysely'; import { KyselyDB } from '@docmost/db/types/kysely.types'; import { generateSlugId } from '../../common/helpers'; import { generateJitteredKeyBetween } from 'fractional-indexing-jittered'; -import { markdownToHtml } from './utils/marked.utils'; import { TiptapTransformer } from '@hocuspocus/transformer'; import * as Y from 'yjs'; +import { markdownToHtml } from "@docmost/editor-ext"; @Injectable() export class ImportService { diff --git a/package.json b/package.json index 9bda7c13..3f0daaf5 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "fractional-indexing-jittered": "^0.9.1", "ioredis": "^5.4.1", "jszip": "^3.10.1", + "marked": "^13.0.3", "uuid": "^11.0.3", "y-indexeddb": "^9.0.12", "yjs": "^13.6.20" diff --git a/packages/editor-ext/src/index.ts b/packages/editor-ext/src/index.ts index a9095ce2..49f0fdd1 100644 --- a/packages/editor-ext/src/index.ts +++ b/packages/editor-ext/src/index.ts @@ -15,4 +15,4 @@ export * from "./lib/custom-code-block" export * from "./lib/drawio"; export * from "./lib/excalidraw"; export * from "./lib/embed"; - +export * from "./lib/markdown"; diff --git a/packages/editor-ext/src/lib/markdown/index.ts b/packages/editor-ext/src/lib/markdown/index.ts new file mode 100644 index 00000000..64b3a727 --- /dev/null +++ b/packages/editor-ext/src/lib/markdown/index.ts @@ -0,0 +1,2 @@ +export * from "./markdown-clipboard"; +export * from "./utils/marked.utils"; diff --git a/packages/editor-ext/src/lib/markdown/markdown-clipboard.ts b/packages/editor-ext/src/lib/markdown/markdown-clipboard.ts new file mode 100644 index 00000000..764daf08 --- /dev/null +++ b/packages/editor-ext/src/lib/markdown/markdown-clipboard.ts @@ -0,0 +1,43 @@ +// adapted from: https://github.com/aguingand/tiptap-markdown/blob/main/src/extensions/tiptap/clipboard.js - MIT +import { Extension } from "@tiptap/core"; +import { Plugin, PluginKey } from "@tiptap/pm/state"; +import { DOMParser } from "@tiptap/pm/model"; +import { markdownToHtml } from "./utils/marked.utils"; + +export const MarkdownClipboard = Extension.create({ + name: "markdownClipboard", + addOptions() { + return { + transformPastedText: false, + }; + }, + addProseMirrorPlugins() { + return [ + new Plugin({ + key: new PluginKey("markdownClipboard"), + props: { + clipboardTextParser: (text, context, plainText) => { + if (plainText || !this.options.transformPastedText) { + return null; // pasting with shift key prevents formatting + } + const parsed = markdownToHtml(text); + return DOMParser.fromSchema(this.editor.schema).parseSlice( + elementFromString(parsed), + { + preserveWhitespace: true, + context, + }, + ); + }, + }, + }), + ]; + }, +}); + +function elementFromString(value) { + // add a wrapper to preserve leading and trailing whitespace + const wrappedValue = `${value}`; + + return new window.DOMParser().parseFromString(wrappedValue, "text/html").body; +} diff --git a/apps/server/src/integrations/import/utils/callout.marked.ts b/packages/editor-ext/src/lib/markdown/utils/callout.marked.ts similarity index 100% rename from apps/server/src/integrations/import/utils/callout.marked.ts rename to packages/editor-ext/src/lib/markdown/utils/callout.marked.ts diff --git a/apps/server/src/integrations/import/utils/marked.utils.ts b/packages/editor-ext/src/lib/markdown/utils/marked.utils.ts similarity index 93% rename from apps/server/src/integrations/import/utils/marked.utils.ts rename to packages/editor-ext/src/lib/markdown/utils/marked.utils.ts index cfc7e6a9..c2b67db1 100644 --- a/apps/server/src/integrations/import/utils/marked.utils.ts +++ b/packages/editor-ext/src/lib/markdown/utils/marked.utils.ts @@ -30,7 +30,7 @@ marked.use({ marked.use({ extensions: [calloutExtension, mathBlockExtension, mathInlineExtension] }); -export async function markdownToHtml(markdownInput: string): Promise { +export function markdownToHtml(markdownInput: string): string | Promise { const YAML_FONT_MATTER_REGEX = /^\s*---[\s\S]*?---\s*/; const markdown = markdownInput diff --git a/apps/server/src/integrations/import/utils/math-block.marked.ts b/packages/editor-ext/src/lib/markdown/utils/math-block.marked.ts similarity index 100% rename from apps/server/src/integrations/import/utils/math-block.marked.ts rename to packages/editor-ext/src/lib/markdown/utils/math-block.marked.ts diff --git a/apps/server/src/integrations/import/utils/math-inline.marked.ts b/packages/editor-ext/src/lib/markdown/utils/math-inline.marked.ts similarity index 100% rename from apps/server/src/integrations/import/utils/math-inline.marked.ts rename to packages/editor-ext/src/lib/markdown/utils/math-inline.marked.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b2add82d..581be32e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -160,6 +160,9 @@ importers: jszip: specifier: ^3.10.1 version: 3.10.1 + marked: + specifier: ^13.0.3 + version: 13.0.3 uuid: specifier: ^11.0.3 version: 11.0.3 @@ -474,9 +477,6 @@ importers: kysely-migration-cli: specifier: ^0.4.2 version: 0.4.2 - marked: - specifier: ^13.0.3 - version: 13.0.3 mime-types: specifier: ^2.1.35 version: 2.1.35