diff --git a/src/main/frontend/handler/paste.cljs b/src/main/frontend/handler/paste.cljs index bb3f20666a..33d20ffe42 100644 --- a/src/main/frontend/handler/paste.cljs +++ b/src/main/frontend/handler/paste.cljs @@ -64,16 +64,29 @@ (try (js/JSON.parse text) (catch :default _ #js{}))) +(defn- get-whiteboard-tldr-from-text + [text] + (when-let [matched-text (util/safe-re-find #"(.*)" text)] + (try-parse-as-json (second matched-text)))) + +(defn- get-whiteboard-shape-refs-text + [text] + (when-let [tldr (get-whiteboard-tldr-from-text text)] + (->> (gobj/get tldr "shapes") + (mapv (fn [shape] + (let [shape-id (gobj/get shape "id")] + (block-ref/->block-ref shape-id)))) + (string/join "\n")))) + (defn- paste-copied-blocks-or-text + ;; todo: logseq/whiteboard-shapes is now text/html [text e html] (util/stop e) (let [copied-blocks (state/get-copied-blocks) input (state/get-input) + input-id (state/get-edit-input-id) text (string/replace text "\r\n" "\n") ;; Fix for Windows platform - whiteboard-shape? (= "logseq/whiteboard-shapes" (gobj/get (try-parse-as-json text) "type")) - text (if whiteboard-shape? - (block-ref/->block-ref (gobj/getValueByKeys (try-parse-as-json text) "shapes" 0 "id")) - text) + shape-refs-text (when-not (string/blank? html) (get-whiteboard-shape-refs-text html)) internal-paste? (and (seq (:copy/blocks copied-blocks)) ;; not copied from the external clipboard @@ -85,6 +98,9 @@ (editor-handler/paste-blocks blocks {}))) (let [{:keys [value]} (editor-handler/get-selection-and-format)] (cond + (not (string/blank? shape-refs-text)) + (commands/simple-insert! input-id shape-refs-text nil) + (and (or (gp-util/url? text) (and value (gp-util/url? (string/trim value)))) (not (string/blank? (util/get-selected-text)))) @@ -92,7 +108,7 @@ (and (block-ref/block-ref? text) (editor-handler/wrapped-by? input block-ref/left-parens block-ref/right-parens)) - (commands/simple-insert! (state/get-edit-input-id) (block-ref/get-block-ref-id text) nil) + (commands/simple-insert! input-id (block-ref/get-block-ref-id text) nil) :else ;; from external @@ -105,7 +121,6 @@ nil)))] (if (string/blank? result) nil result)) text (or html-text text) - input-id (state/get-edit-input-id) replace-text-f (fn [] (commands/delete-selection! input-id) (commands/simple-insert! input-id text nil))] @@ -172,7 +187,7 @@ (let [clipboard-data (gobj/get e "clipboardData") html (when-not raw-paste? (.getData clipboard-data "text/html")) text (.getData clipboard-data "text")] - (if-not (string/blank? text) + (if-not (and (string/blank? text) (string/blank? html)) (paste-text-or-blocks-aux input e text html) (when id (let [_handled diff --git a/tldraw/apps/tldraw-logseq/src/hooks/usePaste.ts b/tldraw/apps/tldraw-logseq/src/hooks/usePaste.ts index a4b93f4e11..e8ada77447 100644 --- a/tldraw/apps/tldraw-logseq/src/hooks/usePaste.ts +++ b/tldraw/apps/tldraw-logseq/src/hooks/usePaste.ts @@ -41,6 +41,13 @@ const safeParseJson = (json: string) => { } } +const getWhiteboardsTldrFromText = (text: string) => { + const innerText = text.match(/(.*)<\/whiteboard-tldr>/)?.[1] + if (innerText) { + return safeParseJson(innerText) + } +} + interface VideoImageAsset extends TLAsset { size?: number[] } @@ -139,11 +146,13 @@ export function usePaste() { } function createHTMLShape(text: string) { - return { - ...HTMLShape.defaultProps, - html: text, - point: [point[0], point[1]], - } + return [ + { + ...HTMLShape.defaultProps, + html: text, + point: [point[0], point[1]], + }, + ] } async function tryCreateShapesFromDataTransfer(dataTransfer: DataTransfer) { @@ -208,7 +217,7 @@ export function usePaste() { } const rawText = await getDataFromType(item, 'text/html') if (rawText) { - return [createHTMLShape(rawText)] + return tryCreateShapeHelper(tryCreateClonedShapesFromJSON, createHTMLShape)(rawText) } return null } @@ -246,7 +255,6 @@ export function usePaste() { return tryCreateShapeHelper( tryCreateShapeFromURL, tryCreateShapeFromIframeString, - tryCreateClonedShapesFromJSON, tryCreateLogseqPortalShapesFromString )(text) } @@ -255,9 +263,9 @@ export function usePaste() { } function tryCreateClonedShapesFromJSON(rawText: string) { - const data = safeParseJson(rawText) + const data = getWhiteboardsTldrFromText(rawText) try { - if (data?.type === 'logseq/whiteboard-shapes') { + if (data) { const shapes = data.shapes as TLShapeModel[] assetsToClone = data.assets as TLAsset[] const commonBounds = BoundsUtils.getCommonBounds( @@ -270,6 +278,7 @@ export function usePaste() { maxY: (shape.point?.[1] ?? point[1]) + (shape.size?.[1] ?? 4), })) ) + const bindings = data.bindings as Record const shapesToCreate = shapes.map(shape => { return { ...shape, @@ -289,9 +298,7 @@ export function usePaste() { return } // try to bind the new shape - const binding = app.currentPage.bindings[h.bindingId] - // FIXME: if copy from a different whiteboard, the binding info - // will not be available + const binding = bindings[h.bindingId] if (binding) { // if the copied binding from/to is in the source const oldFromIdx = shapes.findIndex(s => s.id === binding.fromId) @@ -322,7 +329,7 @@ export function usePaste() { } async function tryCreateShapeFromURL(rawText: string) { - if (isValidURL(rawText)) { + if (isValidURL(rawText) && !(shiftKey || fromDrop)) { const isYoutubeUrl = (url: string) => { const youtubeRegex = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/ diff --git a/tldraw/packages/core/src/lib/TLApp/TLApp.ts b/tldraw/packages/core/src/lib/TLApp/TLApp.ts index b6b0905231..6fbeca3814 100644 --- a/tldraw/packages/core/src/lib/TLApp/TLApp.ts +++ b/tldraw/packages/core/src/lib/TLApp/TLApp.ts @@ -3,7 +3,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { TLBounds } from '@tldraw/intersect' import { Vec } from '@tldraw/vec' -import { action, computed, makeObservable, observable, transaction } from 'mobx' +import { action, computed, makeObservable, observable, toJS, transaction } from 'mobx' import { GRID_SIZE } from '../../constants' import type { TLAsset, @@ -474,17 +474,21 @@ export class TLApp< copy = () => { if (this.selectedShapesArray.length > 0 && !this.editingShape) { - const tldrawString = JSON.stringify({ - type: 'logseq/whiteboard-shapes', + const jsonString = JSON.stringify({ shapes: this.selectedShapesArray.map(shape => shape.serialized), - // pasting into other whiteboard may require this if any shape uses asset + // pasting into other whiteboard may require this if any shape uses the assets assets: this.getCleanUpAssets().filter(asset => { return this.selectedShapesArray.some(shape => shape.props.assetId === asset.id) }), + // convey the bindings to maintain the new links after pasting + bindings: toJS(this.currentPage.bindings), }) + const tldrawString = `${jsonString}` + // FIXME: use `writeClipboard` in frontend.utils navigator.clipboard.write([ new ClipboardItem({ - 'text/plain': new Blob([tldrawString], { type: 'text/plain' }), + 'text/html': new Blob([tldrawString], { type: 'text/html' }), + // ??? what plain text should be used here? }), ]) }