From 51cf3a92198545c0dc1a3132cd6edcd9e0a4b3a8 Mon Sep 17 00:00:00 2001 From: zsviczian Date: Wed, 10 Jan 2024 22:12:02 +0100 Subject: [PATCH] 2.0.17 --- manifest.json | 2 +- src/EmbeddedFileLoader.ts | 3 ++- src/ExcalidrawAutomate.ts | 6 +++++- src/ExcalidrawView.ts | 30 ++++++++++++++++++++++++------ src/dialogs/ImportSVGDialog.ts | 7 ++++--- src/dialogs/Messages.ts | 10 ++++++++++ src/lang/locale/en.ts | 1 + src/main.ts | 4 ++-- src/utils/CarveOut.ts | 9 +++++++-- src/utils/CropImage.ts | 12 +++++++++--- 10 files changed, 65 insertions(+), 19 deletions(-) diff --git a/manifest.json b/manifest.json index 6d2def9..b32b790 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-excalidraw-plugin", "name": "Excalidraw", - "version": "2.0.16", + "version": "2.0.17", "minAppVersion": "1.1.6", "description": "An Obsidian plugin to edit and view Excalidraw drawings", "author": "Zsolt Viczian", diff --git a/src/EmbeddedFileLoader.ts b/src/EmbeddedFileLoader.ts index c831bce..6549c65 100644 --- a/src/EmbeddedFileLoader.ts +++ b/src/EmbeddedFileLoader.ts @@ -350,6 +350,7 @@ export class EmbeddedFilesLoader { elements?: ExcalidrawElement[]; }) : Promise<{dataURL: DataURL, hasSVGwithBitmap:boolean}> { //debug({where:"EmbeddedFileLoader.getExcalidrawSVG",uid:this.uid,file:file.name}); + const isMask = isMaskFile(this.plugin, file); const forceTheme = hasExportTheme(this.plugin, file) ? getExportTheme(this.plugin, file, "light") : undefined; @@ -358,7 +359,7 @@ export class EmbeddedFilesLoader { ? getWithBackground(this.plugin, file) : false, withTheme: !!forceTheme, - isMask: isMaskFile(this.plugin,file), + isMask, }; const svg = replaceSVGColors( await createSVG( diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts index 88563d3..c98ec85 100644 --- a/src/ExcalidrawAutomate.ts +++ b/src/ExcalidrawAutomate.ts @@ -682,7 +682,11 @@ export class ExcalidrawAutomate { outString += `${key}: $$${item.latex}$$\n`; } else { if(item.file) { - outString += `${key}: [[${item.file}]]\n`; + if(item.file instanceof TFile) { + outString += `${key}: [[${item.file.path}]]\n`; + } else { + outString += `${key}: [[${item.file}]]\n`; + } } else { outString += `${key}: ${item.hyperlink}\n`; } diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts index 35bd7f8..85bea80 100644 --- a/src/ExcalidrawView.ts +++ b/src/ExcalidrawView.ts @@ -4014,9 +4014,9 @@ export default class ExcalidrawView extends TextFileView { } } - } + } - public getSingleSelectedImageWithURL(): {imageEl: ExcalidrawImageElement, embeddedFile: EmbeddedFile} { + public getSingleSelectedImage(): {imageEl: ExcalidrawImageElement, embeddedFile: EmbeddedFile} { if(!this.excalidrawAPI) return null; const els = this.getViewSelectedElements().filter(el=>el.type==="image"); if(els.length !== 1) { @@ -4024,7 +4024,6 @@ export default class ExcalidrawView extends TextFileView { } const el = els[0] as ExcalidrawImageElement; const imageFile = this.excalidrawData.getFile(el.fileId); - if(!imageFile?.isHyperLink) return null; return {imageEl: el, embeddedFile: imageFile}; } @@ -4163,19 +4162,38 @@ export default class ExcalidrawView extends TextFileView { } } - const img = this.getSingleSelectedImageWithURL(); - if(img) { + const img = this.getSingleSelectedImage(); + if(img && img.embeddedFile?.isHyperLink) { contextMenuActions.push([ renderContextMenuAction( t("CONVERT_URL_TO_FILE"), () => { - this.convertImageElWithURLToLocalFile(img); + setTimeout(()=>this.convertImageElWithURLToLocalFile(img)); }, onClose ), ]); } + if(img && img.embeddedFile && img.embeddedFile.mimeType === "image/svg+xml") { + contextMenuActions.push([ + renderContextMenuAction( + t("IMPORT_SVG_CONTEXTMENU"), + () => { + const base64Content = img.embeddedFile.getImage(false).split(',')[1]; + // Decoding the base64 content + const svg = atob(base64Content); + if(!svg || svg === "") return; + const ea = getEA(this) as ExcalidrawAutomate; + ea.importSVG(svg); + ea.addToGroup(ea.getElements().map(el=>el.id)); + ea.addElementsToView(true, true, true,true); + }, + onClose + ), + ]); + } + contextMenuActions.push([ renderContextMenuAction( t("UNIVERSAL_ADD_FILE"), diff --git a/src/dialogs/ImportSVGDialog.ts b/src/dialogs/ImportSVGDialog.ts index a3b64ea..405f090 100644 --- a/src/dialogs/ImportSVGDialog.ts +++ b/src/dialogs/ImportSVGDialog.ts @@ -3,6 +3,8 @@ import { REG_LINKINDEX_INVALIDCHARS } from "../constants/constants"; import ExcalidrawView from "../ExcalidrawView"; import { t } from "../lang/helpers"; import ExcalidrawPlugin from "../main"; +import { getEA } from "src"; +import { ExcalidrawAutomate } from "src/ExcalidrawAutomate"; export class ImportSVGDialog extends FuzzySuggestModal { public app: App; @@ -38,12 +40,11 @@ export class ImportSVGDialog extends FuzzySuggestModal { async onChooseItem(item: TFile, event: KeyboardEvent): Promise { if(!item) return; - const ea = this.plugin.ea; - ea.reset(); - ea.setView(this.view); + const ea = getEA(this.view) as ExcalidrawAutomate; const svg = await app.vault.read(item); if(!svg || svg === "") return; ea.importSVG(svg); + ea.addToGroup(ea.getElements().map(el=>el.id)); ea.addElementsToView(true, true, true,true); } diff --git a/src/dialogs/Messages.ts b/src/dialogs/Messages.ts index 6305119..5280ec5 100644 --- a/src/dialogs/Messages.ts +++ b/src/dialogs/Messages.ts @@ -17,6 +17,16 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
`, +"2.0.17":` +## Fixed +- Image cropping now supports dark mode +- Image cropping/carve out was not working reliably in some cases [#1546](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1546) +- Masking a mirrored image resulted in an off-positioned mask + +## New +- Context menu action to convert SVG to Excalidraw strokes +- Updated Chinese translation (Thank you @tswwe) +`, "2.0.16":` ## Fixed - Image cropping did not work consistently with large image files on lower-powered devices [#1538](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1538). diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index 5393454..ba94633 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -66,6 +66,7 @@ export default { INSERT_COMMAND: "Insert Obsidian Command as a link", INSERT_IMAGE: "Insert image or Excalidraw drawing from your vault", IMPORT_SVG: "Import an SVG file as Excalidraw strokes (limited SVG support, TEXT currently not supported)", + IMPORT_SVG_CONTEXTMENU: "Convert SVG to strokes - with limitations", INSERT_MD: "Insert markdown file from vault", INSERT_PDF: "Insert PDF file from vault", UNIVERSAL_ADD_FILE: "Insert ANY file", diff --git a/src/main.ts b/src/main.ts index 7fd9d94..52fa45d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -808,8 +808,8 @@ export default class ExcalidrawPlugin extends Plugin { checkCallback: (checking: boolean) => { const view = this.app.workspace.getActiveViewOfType(ExcalidrawView); if(!view) return false; - const img = view.getSingleSelectedImageWithURL(); - if(!img) return false; + const img = view.getSingleSelectedImage(); + if(!img || !img.embeddedFile?.isHyperLink) return false; if(checking) return true; view.convertImageElWithURLToLocalFile(img); }, diff --git a/src/utils/CarveOut.ts b/src/utils/CarveOut.ts index 83adf39..cc1e16a 100644 --- a/src/utils/CarveOut.ts +++ b/src/utils/CarveOut.ts @@ -25,6 +25,8 @@ export const carveOutImage = async (sourceEA: ExcalidrawAutomate, viewImageEl: E newImage.y = 0; newImage.width = width; newImage.height = height; + const scale = newImage.scale; + newImage.scale = [1,1]; const ef = sourceEA.targetView.excalidrawData.getFile(viewImageEl.fileId); let imageLink = ""; @@ -46,6 +48,7 @@ export const carveOutImage = async (sourceEA: ExcalidrawAutomate, viewImageEl: E const file = await createImageCropperFile(targetEA, newImage.id, imageLink, foldername, filename); if(!file) return; + //console.log(await app.vault.read(file)); sourceEA.clear(); sourceEA.copyViewElementsToEAforEditing([viewImageEl]); const sourceImageEl = sourceEA.getElement(viewImageEl.id) as Mutable; @@ -55,6 +58,7 @@ export const carveOutImage = async (sourceEA: ExcalidrawAutomate, viewImageEl: E const replacingImage = sourceEA.getElement(replacingImageID) as Mutable; replacingImage.width = sourceImageEl.width; replacingImage.height = sourceImageEl.height; + replacingImage.scale = scale; sourceEA.addElementsToView(false, true, true); } @@ -108,7 +112,7 @@ export const createImageCropperFile = async (targetEA: ExcalidrawAutomate, image //wait for file to be created/indexed by Obsidian let file = vault.getAbstractFileByPath(newPath); let counter = 0; - while(!file && counter < 50) { + while((!file || !targetEA.isExcalidrawFile(file as TFile)) && counter < 50) { await sleep(100); file = vault.getAbstractFileByPath(newPath); counter++; @@ -119,6 +123,7 @@ export const createImageCropperFile = async (targetEA: ExcalidrawAutomate, image return; } + /* //wait for the new ExcalidrawView to open and initialize counter = 0; let newView = workspace.getActiveViewOfType(ExcalidrawView) as ExcalidrawView; @@ -151,7 +156,7 @@ export const createImageCropperFile = async (targetEA: ExcalidrawAutomate, image new Notice("Image did not load to the view. NewExcalidraw Drawing is taking too long to load. Please try again."); return; } - +*/ //console.log({counter, path: workspace.getActiveFile()?.path, newView, files: api.getFiles()}); return file; diff --git a/src/utils/CropImage.ts b/src/utils/CropImage.ts index 9a5fdfb..0282634 100644 --- a/src/utils/CropImage.ts +++ b/src/utils/CropImage.ts @@ -91,13 +91,17 @@ export class CropImage { return {style, mask:maskSVG.innerHTML}; } - private async getImageSVG() { + private async getImage() { const exportSettings:ExportSettings = { withBackground: false, withTheme: false, isMask: false, } - + const images = Object.values(this.imageEA.imagesDict); + if(images.length === 1) { + return images[0].dataURL; + } + return await this.imageEA.createPNGBase64(null,1,exportSettings,null,null,0); const imageSVG = await this.imageEA.createSVG(null,false,exportSettings,null,null,0); const svgData = new XMLSerializer().serializeToString(imageSVG); return `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svgData)))}`; @@ -111,12 +115,14 @@ export class CropImage { return; } const maskID = nanoid(); + const imageID = nanoid(); const {viewBox, vbWidth, vbHeight, width, height} = this.getViewBoxAndSize(); const parser = new DOMParser(); const {style, mask} = await this.getMaskSVG(); const svgString = `\n` + + `\n` + `${style}\n\n${mask}\n\n\n` + - `\n`; + `\n`; return parser.parseFromString( svgString, "image/svg+xml",