From bb2d30f9e3f38dfdb6f5049fd762c515b99ec099 Mon Sep 17 00:00:00 2001 From: zsviczian Date: Sun, 23 Jul 2023 19:09:19 +0200 Subject: [PATCH] 1.9.10 --- manifest.json | 2 +- src/ExcalidrawAutomate.ts | 23 ++++++++++------- src/ExcalidrawData.ts | 7 ++++-- src/ExcalidrawView.ts | 5 ++++ src/constants.ts | 2 +- src/dialogs/InsertImageDialog.ts | 2 +- src/lang/locale/en.ts | 1 + src/utils/ExcalidrawViewUtils.ts | 4 +-- src/utils/FileUtils.ts | 42 +++++++++++++++++++++++++++++--- src/utils/Utils.ts | 13 ++++++++-- 10 files changed, 79 insertions(+), 22 deletions(-) diff --git a/manifest.json b/manifest.json index 37cbdf8..89b6143 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-excalidraw-plugin", "name": "Excalidraw", - "version": "1.9.9", + "version": "1.9.10", "minAppVersion": "1.1.6", "description": "An Obsidian plugin to edit and view Excalidraw drawings", "author": "Zsolt Viczian", diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts index ddc94aa..5d23f90 100644 --- a/src/ExcalidrawAutomate.ts +++ b/src/ExcalidrawAutomate.ts @@ -871,6 +871,7 @@ export class ExcalidrawAutomate { [this.getElement(id)], { x: topX, y: topY }, false, + this.getExcalidrawAPI(), )[0]; return id; }; @@ -1688,7 +1689,7 @@ export class ExcalidrawAutomate { errorMessage("targetView not set", "addElementsToView()"); return false; } - const elements = this.getElements(); + const elements = this.getElements(); return await this.targetView.addElements( elements, repositionToCursor, @@ -2587,6 +2588,7 @@ export function repositionElementsToCursor( elements: ExcalidrawElement[], newPosition: { x: number; y: number }, center: boolean = false, + api: ExcalidrawImperativeAPI, ): ExcalidrawElement[] { const [x1, y1, x2, y2] = estimateBounds(elements); let [offsetX, offsetY] = [0, 0]; @@ -2604,7 +2606,8 @@ export function repositionElementsToCursor( element.x = element.x + offsetX; element.y = element.y + offsetY; }); - return elements; + + return api.restore({elements}).elements; } function errorMessage(message: string, source: string) { @@ -2743,18 +2746,20 @@ export const getFrameElementsMatchingQuery = ( } return m[1] === q.toLowerCase(); } - const text = el.name.toLowerCase().replaceAll("\n", " ").trim(); + const text = el.name + ? el.name.toLowerCase().replaceAll("\n", " ").trim() + : ""; + return text.match(q.toLowerCase()); //to distinguish between "# frame" and "# frame 1" https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530 })); } export const cloneElement = (el: ExcalidrawElement):any => { - return { - ...el, - version: el.version + 1, - updated: Date.now(), - versionNonce: Math.floor(Math.random() * 1000000000), - } + const newEl = JSON.parse(JSON.stringify(el)); + newEl.version = el.version + 1; + newEl.updated = Date.now(); + newEl.versionNonce = Math.floor(Math.random() * 1000000000); + return newEl; } export const verifyMinimumPluginVersion = (requiredVersion: string): boolean => { diff --git a/src/ExcalidrawData.ts b/src/ExcalidrawData.ts index 085094d..2ffada6 100644 --- a/src/ExcalidrawData.ts +++ b/src/ExcalidrawData.ts @@ -1172,9 +1172,12 @@ export class ExcalidrawData { await getAttachmentsFolderAndFilePath(this.app, this.file.path, fname) ).filepath; + const arrayBuffer = await getBinaryFileFromDataURL(dataURL); + if(!arrayBuffer) return null; + const file = await this.app.vault.createBinary( filepath, - getBinaryFileFromDataURL(dataURL), + arrayBuffer, ); const embeddedFile = new EmbeddedFile( @@ -1182,7 +1185,7 @@ export class ExcalidrawData { this.file.path, filepath, ); - + embeddedFile.setImage( dataURL, mimeType, diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts index be3342e..ad168ad 100644 --- a/src/ExcalidrawView.ts +++ b/src/ExcalidrawView.ts @@ -2194,6 +2194,10 @@ export default class ExcalidrawView extends TextFileView { const dataURL = await getDataURLFromURL(link,mimeType,3000); const fileId = await generateIdFromFile((new TextEncoder()).encode(dataURL as string)) const file = await this.excalidrawData.saveDataURLtoVault(dataURL,mimeType,fileId); + if(!file) { + new Notice(t("ERROR_SAVING_IMAGE")); + return; + } await ea.addImage(0,0,file); ea.addElementsToView(true,true,true); } @@ -2648,6 +2652,7 @@ export default class ExcalidrawView extends TextFileView { newElements, this.currentPosition, true, + api, ); } diff --git a/src/constants.ts b/src/constants.ts index 207da8f..073aa63 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -65,7 +65,7 @@ export const ROUNDNESS = { //should at one point publish @zsviczian/excalidraw/t ADAPTIVE_RADIUS: 3, } as const; export const GITHUB_RELEASES = "https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/"; -export const URLFETCHTIMEOUT = 1000; +export const URLFETCHTIMEOUT = 3000; export const PLUGIN_ID = "obsidian-excalidraw-plugin"; export const SCRIPT_INSTALL_CODEBLOCK = "excalidraw-script-install"; export const SCRIPT_INSTALL_FOLDER = "Downloaded"; diff --git a/src/dialogs/InsertImageDialog.ts b/src/dialogs/InsertImageDialog.ts index 28916b6..9ac9b73 100644 --- a/src/dialogs/InsertImageDialog.ts +++ b/src/dialogs/InsertImageDialog.ts @@ -61,7 +61,7 @@ export class InsertImageDialog extends FuzzySuggestModal { const scaleToFullsize = scaleToFullsizeModifier(event); (async () => { await ea.addImage(0, 0, item, !scaleToFullsize); - ea.addElementsToView(true, false, true); + ea.addElementsToView(true, true, true); })(); } diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index 59aa30c..c6db856 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -96,6 +96,7 @@ export default { BACKUP_RESTORED: "Backup restored", CACHE_NOT_READY: "I apologize for the inconvenience, but an error occurred while loading your file.

Having a little patience can save you a lot of time...

The plugin has a backup cache, but it appears that you have just started Obsidian. Initializing the Backup Cache may take some time, usually up to a minute or more depending on your device's performance. You will receive a notification in the top right corner when the cache initialization is complete.

Please press OK to attempt loading the file again and check if the cache has finished initializing. If you see a completely empty file behind this message, I recommend waiting until the backup cache is ready before proceeding. Alternatively, you can choose Cancel to manually correct your file.
", OBSIDIAN_TOOLS_PANEL: "Obsidian Tools Panel", + ERROR_SAVING_IMAGE: "Unknown error occured while fetching the image. It could be that for some reason the image is not available or rejected the fetch request from Obsidian", //settings.ts RELEASE_NOTES_NAME: "Display Release Notes after update", diff --git a/src/utils/ExcalidrawViewUtils.ts b/src/utils/ExcalidrawViewUtils.ts index 3bfd9ba..9a5af99 100644 --- a/src/utils/ExcalidrawViewUtils.ts +++ b/src/utils/ExcalidrawViewUtils.ts @@ -21,7 +21,7 @@ export const insertImageToView = async ( file, scale, ); - await ea.addElementsToView(false, false, true); + await ea.addElementsToView(false, true, true); return id; } @@ -45,7 +45,7 @@ export const insertEmbeddableToView = async ( link, file, ); - await ea.addElementsToView(false, false, true); + await ea.addElementsToView(false, true, true); return id; } } \ No newline at end of file diff --git a/src/utils/FileUtils.ts b/src/utils/FileUtils.ts index b5e4c7d..d143949 100644 --- a/src/utils/FileUtils.ts +++ b/src/utils/FileUtils.ts @@ -169,7 +169,38 @@ export const getMimeType = (extension: string):MimeType => { } } -const getFileFromURL = async (url: string, mimeType: MimeType, timeout: number = URLFETCHTIMEOUT):Promise => { + +// using fetch API +const getFileFromURL = async (url: string, mimeType: MimeType, timeout: number = URLFETCHTIMEOUT): Promise => { + try { + const response = await Promise.race([ + fetch(url), + new Promise((resolve) => setTimeout(() => resolve(null), timeout)) + ]); + + if (!response) { + new Notice(`URL did not load within the timeout period of ${timeout}ms.\n\nTry force-saving again in a few seconds.\n\n${url}`,8000); + throw new Error(`URL did not load within the timeout period of ${timeout}ms`); + } + + const arrayBuffer = await response.arrayBuffer(); + + return { + status: response.status, + headers: Object.fromEntries(response.headers.entries()), + arrayBuffer: arrayBuffer, + json: null, + text: null, + }; + } catch (e) { + errorlog({ where: getFileFromURL, message: e.message, url: url }); + return undefined; + } +}; + +// using Obsidian requestUrl (this failed on a firebase link) +// https://firebasestorage.googleapis.com/v0/b/firescript-577a2.appspot.com/o/imgs%2Fapp%2FJSG%2FfTMP6WGQRC.png?alt=media&token=6d2993b4-e629-46b6-98d1-133af7448c49 +const getFileFromURLFallback = async (url: string, mimeType: MimeType, timeout: number = URLFETCHTIMEOUT):Promise => { try { return await Promise.race([ (async () => new Promise((resolve) => setTimeout(()=>resolve(null), timeout)))(), @@ -181,12 +212,15 @@ const getFileFromURL = async (url: string, mimeType: MimeType, timeout: number = } } -export const getDataURLFromURL = async (url: string, mimeType: MimeType, timeout: number = URLFETCHTIMEOUT):Promise => { - const response = await getFileFromURL(url, mimeType, timeout); +export const getDataURLFromURL = async (url: string, mimeType: MimeType, timeout: number = URLFETCHTIMEOUT): Promise => { + let response = await getFileFromURL(url, mimeType, timeout); + if(response && response.status !== 200) { + response = await getFileFromURLFallback(url, mimeType, timeout); + } return response && response.status === 200 ? await getDataURL(response.arrayBuffer, mimeType) : url as DataURL; -} +}; export const blobToBase64 = async (blob: Blob): Promise => { const arrayBuffer = await blob.arrayBuffer() diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 3d7d14f..9643197 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -24,7 +24,7 @@ import ExcalidrawPlugin from "../main"; import { ExcalidrawElement } from "@zsviczian/excalidraw/types/element/types"; import { ExportSettings } from "../ExcalidrawView"; import { compressToBase64, decompressFromBase64 } from "lz-string"; -import { getIMGFilename } from "./FileUtils"; +import { getDataURLFromURL, getIMGFilename, getMimeType, getURLImageExtension } from "./FileUtils"; import ExcalidrawScene from "../svgToExcalidraw/elements/ExcalidrawScene"; import { IMAGE_TYPES } from "../Constants"; import { generateEmbeddableLink } from "./CustomEmbeddableUtils"; @@ -229,11 +229,20 @@ export const svgToBase64 = (svg: string): string => { )}`; }; -export const getBinaryFileFromDataURL = (dataURL: string): ArrayBuffer => { +export const getBinaryFileFromDataURL = async (dataURL: string): Promise => { if (!dataURL) { return null; } + if(dataURL.match(/^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/i)) { + const hyperlink = dataURL; + const extension = getURLImageExtension(hyperlink) + const mimeType = getMimeType(extension); + dataURL = await getDataURLFromURL(hyperlink, mimeType) + } const parts = dataURL.matchAll(/base64,(.*)/g).next(); + if (!parts.value) { + return null; + } const binary_string = window.atob(parts.value[1]); const len = binary_string.length; const bytes = new Uint8Array(len);