From 77784ffff513faf542113313c0e478d6e350be69 Mon Sep 17 00:00:00 2001 From: Khushvendra Date: Sat, 11 Apr 2026 14:32:11 +0530 Subject: [PATCH] refactor: move editor clipboard helpers into module --- src/app/editor/clipboard.ts | 81 ++++++++++++++++++++++++++++++++++++ src/app/editor/index.ts | 82 +------------------------------------ 2 files changed, 82 insertions(+), 81 deletions(-) create mode 100644 src/app/editor/clipboard.ts diff --git a/src/app/editor/clipboard.ts b/src/app/editor/clipboard.ts new file mode 100644 index 00000000..fa3e8ed5 --- /dev/null +++ b/src/app/editor/clipboard.ts @@ -0,0 +1,81 @@ +import {Clipboard} from "@capacitor/clipboard" +import {Capacitor} from "@capacitor/core" +import {Extension} from "@tiptap/core" +import {Plugin, PluginKey} from "@tiptap/pm/state" + +const nativeClipboardAvailable = () => + Capacitor.isNativePlatform() && Capacitor.isPluginAvailable("Clipboard") + +const hasStandardPastePayload = (event: ClipboardEvent) => { + const clipboardData = event.clipboardData + + if (!clipboardData) { + return false + } + + if (Array.from(clipboardData.items).some(item => item.kind === "file")) { + return true + } + + if (clipboardData.types.includes("text/html")) { + return true + } + + return clipboardData.getData("text/plain") !== "" +} + +const getNativeClipboardImage = async () => { + try { + const {type, value} = await Clipboard.read() + + if (!type.startsWith("image/") || value === "") { + return undefined + } + + const imageData = value.startsWith("data:") ? value : `data:${type};base64,${value}` + const blob = await fetch(imageData).then(res => res.blob()) + + if (!blob.type.startsWith("image/")) { + return undefined + } + + const extension = type.split("/")[1]?.split("+")[0] || "png" + + return new File([blob], `clipboard-image.${extension}`, {type: blob.type || type}) + } catch { + return undefined + } +} + +export const NativeClipboardPasteExtension = Extension.create({ + name: "nativeClipboardPaste", + + addProseMirrorPlugins() { + const editor = this.editor + + return [ + new Plugin({ + key: new PluginKey("nativeClipboardPaste"), + props: { + handlePaste: (_view, event) => { + if (!nativeClipboardAvailable() || hasStandardPastePayload(event)) { + return false + } + + event.preventDefault() + + void getNativeClipboardImage().then(file => { + if (!file) { + return + } + + editor.commands.addFile(file, editor.state.selection.from + 1) + }) + + return true + }, + }, + }), + ] + }, +}) diff --git a/src/app/editor/index.ts b/src/app/editor/index.ts index 27b5f077..b3a8c858 100644 --- a/src/app/editor/index.ts +++ b/src/app/editor/index.ts @@ -1,10 +1,6 @@ import {mount} from "svelte" import type {Writable} from "svelte/store" import {get, derived} from "svelte/store" -import {Clipboard} from "@capacitor/clipboard" -import {Capacitor} from "@capacitor/core" -import {Extension} from "@tiptap/core" -import {Plugin, PluginKey} from "@tiptap/pm/state" import {Router} from "@welshman/router" import {dec, inc} from "@welshman/lib" import {throttled} from "@welshman/store" @@ -19,6 +15,7 @@ import { } from "@welshman/app" import type {FileAttributes} from "@welshman/editor" import {Editor, MentionSuggestion, WelshmanExtension, editorProps} from "@welshman/editor" +import {NativeClipboardPasteExtension} from "@app/editor/clipboard" import {escapeHtml} from "@lib/html" import {makeMentionNodeView} from "@app/editor/MentionNodeView" import ProfileSuggestion from "@app/editor/ProfileSuggestion.svelte" @@ -26,83 +23,6 @@ import {uploadFile} from "@app/core/commands" import {deriveSpaceMembers} from "@app/core/state" import {pushToast} from "@app/util/toast" -const nativeClipboardAvailable = () => - Capacitor.isNativePlatform() && Capacitor.isPluginAvailable("Clipboard") - -const hasStandardPastePayload = (event: ClipboardEvent) => { - const clipboardData = event.clipboardData - - if (!clipboardData) { - return false - } - - if (Array.from(clipboardData.items).some(item => item.kind === "file")) { - return true - } - - if (clipboardData.types.includes("text/html")) { - return true - } - - return clipboardData.getData("text/plain") !== "" -} - -const getNativeClipboardImage = async () => { - try { - const {type, value} = await Clipboard.read() - - if (!type.startsWith("image/") || value === "") { - return undefined - } - - const imageData = value.startsWith("data:") ? value : `data:${type};base64,${value}` - const blob = await fetch(imageData).then(res => res.blob()) - - if (!blob.type.startsWith("image/")) { - return undefined - } - - const extension = type.split("/")[1]?.split("+")[0] || "png" - - return new File([blob], `clipboard-image.${extension}`, {type: blob.type || type}) - } catch { - return undefined - } -} - -const NativeClipboardPasteExtension = Extension.create({ - name: "nativeClipboardPaste", - - addProseMirrorPlugins() { - const editor = this.editor - - return [ - new Plugin({ - key: new PluginKey("nativeClipboardPaste"), - props: { - handlePaste: (_view, event) => { - if (!nativeClipboardAvailable() || hasStandardPastePayload(event)) { - return false - } - - event.preventDefault() - - void getNativeClipboardImage().then(file => { - if (!file) { - return - } - - editor.commands.addFile(file, editor.state.selection.from + 1) - }) - - return true - }, - }, - }), - ] - }, -}) - export const makeEditor = async ({ encryptFiles = false, aggressive = false,