fix: support native clipboard image paste on mobile (#181)

Co-authored-by: Khushvendra <khushvendras99@gmail.com>
Co-committed-by: Khushvendra <khushvendras99@gmail.com>
This commit is contained in:
2026-04-11 16:38:06 +00:00
committed by hodlbod
parent 90f86b833d
commit f6d9e52c6e
4 changed files with 100 additions and 0 deletions
+81
View File
@@ -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
},
},
}),
]
},
})
+2
View File
@@ -15,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"
@@ -137,6 +138,7 @@ export const makeEditor = async ({
},
},
}),
NativeClipboardPasteExtension,
],
onUpdate({editor}) {
wordCount?.set(editor.storage.wordCount.words)