From c7545bf350b55f0ac2944d3075e3ec21a7240d8d Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Mon, 5 Sep 2022 13:11:03 +0200 Subject: [PATCH] Fixes #71: also adds drag & drop support --- packages/common/types.ts | 1 + packages/web/editor.tsx | 4 +- packages/web/editor_paste.ts | 110 ++++++++++++++++++++++------------- packages/web/types.ts | 2 +- website/CHANGELOG.md | 4 +- 5 files changed, 75 insertions(+), 46 deletions(-) diff --git a/packages/common/types.ts b/packages/common/types.ts index f25d8a5..50d13dd 100644 --- a/packages/common/types.ts +++ b/packages/common/types.ts @@ -1,4 +1,5 @@ export const reservedPageNames = ["page", "attachment", "plug"]; +export const maximumAttachmentSize = 100 * 1024 * 1024; // 100 MB export type PageMeta = { name: string; diff --git a/packages/web/editor.tsx b/packages/web/editor.tsx index 5794307..e3a5063 100644 --- a/packages/web/editor.tsx +++ b/packages/web/editor.tsx @@ -47,7 +47,7 @@ import { systemSyscalls } from "./syscalls/system"; import { Panel } from "./components/panel"; import { CommandHook } from "./hooks/command"; import { SlashCommandHook } from "./hooks/slash_command"; -import { pasteAttachmentExtension, pasteLinkExtension } from "./editor_paste"; +import { attachmentExtension, pasteLinkExtension } from "./editor_paste"; import { markdownSyscalls } from "@silverbulletmd/common/syscalls/markdown"; import { clientStoreSyscalls } from "./syscalls/clientStore"; import { @@ -518,7 +518,7 @@ export class Editor { } ), pasteLinkExtension, - pasteAttachmentExtension(this.space), + attachmentExtension(this), closeBrackets(), ], }); diff --git a/packages/web/editor_paste.ts b/packages/web/editor_paste.ts index 36b343c..38ee480 100644 --- a/packages/web/editor_paste.ts +++ b/packages/web/editor_paste.ts @@ -1,6 +1,9 @@ import { EditorView, ViewPlugin, ViewUpdate } from "@codemirror/view"; +import { safeRun } from "@plugos/plugos/util"; import { Space } from "@silverbulletmd/common/spaces/space"; +import { maximumAttachmentSize } from "@silverbulletmd/common/types"; import { createImportSpecifier } from "typescript"; +import { Editor } from "./editor"; const urlRegexp = /^https?:\/\/[-a-zA-Z0-9@:%._\+~#=]{1,256}([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/; @@ -45,50 +48,73 @@ export const pasteLinkExtension = ViewPlugin.fromClass( } ); -export function pasteAttachmentExtension(space: Space) { +export function attachmentExtension(editor: Editor) { return EditorView.domEventHandlers({ - paste: (event: ClipboardEvent, editor) => { + dragover: (event) => { + event.preventDefault(); + }, + drop: (event: DragEvent) => { + // TODO: This doesn't take into account the target cursor position, + // it just drops the attachment wherever the cursor was last. + if (event.dataTransfer) { + let payload = [...event.dataTransfer.items]; + safeRun(async () => { + await processFileTransfer(payload); + }); + } + }, + paste: (event: ClipboardEvent) => { let payload = [...event.clipboardData!.items]; - - if (!payload.length || payload.length === 0) { - return false; - } - let file = payload.find((item) => item.kind === "file"); - if (!file) { - return false; - } - const fileType = file.type; - Promise.resolve() - .then(async () => { - let data = await file!.getAsFile()?.arrayBuffer(); - let ext = fileType.split("/")[1]; - let fileName = new Date() - .toISOString() - .split(".")[0] - .replace("T", "_") - .replaceAll(":", "-"); - let finalFileName = prompt( - "File name for pasted attachment", - `${fileName}.${ext}` - ); - if (!finalFileName) { - return; - } - await space.writeAttachment(finalFileName, data!); - let attachmentMarkdown = `[${finalFileName}](attachment/${finalFileName})`; - if (fileType.startsWith("image/")) { - attachmentMarkdown = `![](attachment/${finalFileName})`; - } - editor.dispatch({ - changes: [ - { - insert: attachmentMarkdown, - from: editor.state.selection.main.from, - }, - ], - }); - }) - .catch(console.error); + safeRun(async () => { + await processFileTransfer(payload); + }); }, }); + + async function processFileTransfer(payload: DataTransferItem[]) { + if (!payload.length || payload.length === 0) { + return false; + } + let file = payload.find((item) => item.kind === "file"); + if (!file) { + return false; + } + const fileType = file.type; + let data = await file!.getAsFile()?.arrayBuffer(); + if (data!.byteLength > maximumAttachmentSize) { + editor.flashNotification( + `Attachment is too large, maximum is ${ + maximumAttachmentSize / 1024 / 1024 + }MB`, + "error" + ); + return; + } + let ext = fileType.split("/")[1]; + let fileName = new Date() + .toISOString() + .split(".")[0] + .replace("T", "_") + .replaceAll(":", "-"); + let finalFileName = prompt( + "File name for pasted attachment", + `${fileName}.${ext}` + ); + if (!finalFileName) { + return; + } + await editor.space.writeAttachment(finalFileName, data!); + let attachmentMarkdown = `[${finalFileName}](attachment/${finalFileName})`; + if (fileType.startsWith("image/")) { + attachmentMarkdown = `![](attachment/${finalFileName})`; + } + editor.editorView!.dispatch({ + changes: [ + { + insert: attachmentMarkdown, + from: editor.editorView!.state.selection.main.from, + }, + ], + }); + } } diff --git a/packages/web/types.ts b/packages/web/types.ts index 68b2bf3..6e89563 100644 --- a/packages/web/types.ts +++ b/packages/web/types.ts @@ -1,4 +1,4 @@ -import { AppCommand, CommandDef } from "./hooks/command"; +import { AppCommand } from "./hooks/command"; import { FilterOption, PageMeta } from "@silverbulletmd/common/types"; export type Notification = { diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md index d183bd3..03cc135 100644 --- a/website/CHANGELOG.md +++ b/website/CHANGELOG.md @@ -1,7 +1,9 @@ -An attempt at documenting of the changes/new features introduced in each (pre) release. +An attempt at documenting of the changes/new features introduced in each --- + ## 0.0.33 +* **Attachments**: you can now copy & paste or drag & drop files (images, PDF, whatever you like) into a page and it will be uploaded and appropriately linked from your page. Attachment size is currently limited to 100mb. * Changed full-text search page prefix from `@search/` to `🔍` for the {[Search Space]} command. * `page`, `plug` and `attachment` are now _reserved page names_, you cannot name your pages these (you will get an error when explicitly navigating to them).