diff --git a/manifest.json b/manifest.json index a41a023..b358264 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-excalidraw-plugin", "name": "Excalidraw", - "version": "1.6.20", + "version": "1.6.21", "minAppVersion": "0.12.16", "description": "An Obsidian plugin to edit and view Excalidraw drawings", "author": "Zsolt Viczian", diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts index d7318dd..07df11b 100644 --- a/src/ExcalidrawAutomate.ts +++ b/src/ExcalidrawAutomate.ts @@ -727,7 +727,7 @@ export async function initExcalidrawAutomate( fontFamily: this.style.fontFamily, textAlign: formatting?.textAlign ? formatting.textAlign - : this.style.textAlign, + : (this.style.textAlign ?? "left"), verticalAlign: this.style.verticalAlign, baseline, ...boxedElement(id, "text", topX, topY, width, height), diff --git a/src/ExcalidrawData.ts b/src/ExcalidrawData.ts index f1595c3..f7fc4d3 100644 --- a/src/ExcalidrawData.ts +++ b/src/ExcalidrawData.ts @@ -406,7 +406,11 @@ export class ExcalidrawData { if (textEl) { if (textEl.type !== "text") { //markdown link attached to elements - textEl.link = text; + if(textEl.link!==text) { + textEl.link = text; + textEl.version++; + textEl.versionNonce++; + } this.elementLinks.set(id, text); } else { const wrapAt = estimateMaxLineLen(textEl.text, textEl.originalText); diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts index 67ab6e6..b87709d 100644 --- a/src/ExcalidrawView.ts +++ b/src/ExcalidrawView.ts @@ -40,6 +40,7 @@ import { REG_LINKINDEX_INVALIDCHARS, KEYCODE, LOCAL_PROTOCOL, + REG_BLOCK_REF_CLEAN, } from "./Constants"; import ExcalidrawPlugin from "./main"; import { repositionElementsToCursor } from "./ExcalidrawAutomate"; @@ -109,7 +110,8 @@ export const addFiles = async ( if (!files || files.length === 0 || !view) { return; } - files = files.filter((f) => f.size.height > 0 && f.size.width > 0); //height will be zero when file does not exisig in case of broken embedded file links + //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/544 + files = files.filter((f) => f && f.size && f.size.height > 0 && f.size.width > 0); //height will be zero when file does not exisig in case of broken embedded file links if (files.length === 0) { return; } @@ -145,6 +147,17 @@ export const addFiles = async ( view.excalidrawAPI.addFiles(files); }; +const warningUnknowSeriousError = () => { + new Notice( + "WARNING: Excalidraw ran into an unknown problem!!!\n\n" + + "There is a risk that your most recent changes cannot be saved.\n\n" + + "1) Please select your drawing using CTRL/CMD+A and make a copy with CTRL/CMD+C. " + + "2) Then create an empty drawing in a new pane by CTRL/CMD+clicking the Excalidraw ribbon button, " + + "3) and paste your work to the new document with CTRL/CMD+V.", + 60000, + ); +} + export default class ExcalidrawView extends TextFileView { public excalidrawData: ExcalidrawData; public getScene: Function = null; @@ -190,7 +203,7 @@ export default class ExcalidrawView extends TextFileView { preventAutozoom: false, autosaving: false, dirty: null, - preventReload: true, + preventReload: false, isEditingText: false, saving: false, forceSaving: false, @@ -389,42 +402,51 @@ export default class ExcalidrawView extends TextFileView { return; //file was recently deleted } - //reload() is triggered indirectly when saving by the modifyEventHandler in main.ts - //prevent reload is set here to override reload when not wanted: typically when the user is editing - //and we do not want to interrupt the flow by reloading the drawing into the canvas. - this.semaphores.preventReload = preventReload; - const allowSave = - (this.semaphores.dirty !== null && this.semaphores.dirty) || - this.semaphores.autosaving || forcesave; //dirty == false when view.file == null; - const scene = this.getScene(); + try { + const allowSave = + (this.semaphores.dirty !== null && this.semaphores.dirty) || + this.semaphores.autosaving || forcesave; //dirty == false when view.file == null; + const scene = this.getScene(); - if (this.compatibilityMode) { - await this.excalidrawData.syncElements(scene); - } else if ( - (await this.excalidrawData.syncElements(scene)) && - !this.semaphores.autosaving - ) { - await this.loadDrawing(false); - } - - if (allowSave) { - await super.save(); - this.clearDirty(); - } - - if (!this.semaphores.autosaving) { - if (this.plugin.settings.autoexportSVG) { - await this.saveSVG(); - } - if (this.plugin.settings.autoexportPNG) { - await this.savePNG(); - } - if ( - !this.compatibilityMode && - this.plugin.settings.autoexportExcalidraw + if (this.compatibilityMode) { + await this.excalidrawData.syncElements(scene); + } else if ( + (await this.excalidrawData.syncElements(scene)) && + !this.semaphores.autosaving ) { - this.saveExcalidraw(); + await this.loadDrawing(false); } + + if (allowSave) { + //reload() is triggered indirectly when saving by the modifyEventHandler in main.ts + //prevent reload is set here to override reload when not wanted: typically when the user is editing + //and we do not want to interrupt the flow by reloading the drawing into the canvas. + this.semaphores.preventReload = preventReload; + await super.save(); + this.clearDirty(); + } + + if (!this.semaphores.autosaving) { + if (this.plugin.settings.autoexportSVG) { + await this.saveSVG(); + } + if (this.plugin.settings.autoexportPNG) { + await this.savePNG(); + } + if ( + !this.compatibilityMode && + this.plugin.settings.autoexportExcalidraw + ) { + this.saveExcalidraw(); + } + } + } catch(e) { + errorlog({ + where:"ExcalidrawView.save", + fn:this.save, + error: e + }); + warningUnknowSeriousError(); } this.semaphores.saving = false; } @@ -866,19 +888,31 @@ export default class ExcalidrawView extends TextFileView { this.plugin.settings.autosave && !this.semaphores.forceSaving ) { + this.autosaveTimer = null; this.semaphores.autosaving = true; if (this.excalidrawRef) { await this.save(); } this.semaphores.autosaving = false; + this.autosaveTimer = setTimeout( + timer, + this.plugin.settings.autosaveInterval, + ); + } else { + this.autosaveTimer = setTimeout( + timer, + this.isLoaded && this.plugin.activeExcalidrawView === this + ? 1000 //try again in 1 second + : this.plugin.settings.autosaveInterval, + ); } }; if (this.autosaveTimer) { - clearInterval(this.autosaveTimer); + clearTimeout(this.autosaveTimer); this.autosaveTimer = null; } // clear previous timer if one exists if (this.plugin.settings.autosave) { - this.autosaveTimer = setInterval( + this.autosaveTimer = setTimeout( timer, this.plugin.settings.autosaveInterval, ); @@ -932,7 +966,7 @@ export default class ExcalidrawView extends TextFileView { } const loadOnModifyTrigger = file && file === this.file; if (loadOnModifyTrigger) { - this.data = await this.app.vault.cachedRead(file); + this.data = await this.app.vault.read(file); this.preventAutozoom(); } if (fullreload) { @@ -1006,7 +1040,8 @@ export default class ExcalidrawView extends TextFileView { self.selectElementsMatchingQuery( elements, query, - !this.excalidrawAPI.getAppState().viewModeEnabled + !this.excalidrawAPI.getAppState().viewModeEnabled, + true ); },300); } @@ -1949,7 +1984,7 @@ export default class ExcalidrawView extends TextFileView { event: mouseEvent, source: VIEW_TYPE_EXCALIDRAW, hoverParent: hoverPreviewTarget, - targetEl: hoverPreviewTarget, + targetEl: null,//hoverPreviewTarget, linktext: this.plugin.hover.linkText, sourcePath: this.plugin.hover.sourcePath, }); @@ -2581,15 +2616,26 @@ export default class ExcalidrawView extends TextFileView { this.plugin.saveSettings(); } - public selectElementsMatchingQuery(elements:ExcalidrawElement[], query:string[], selectResult:boolean = true) { + public selectElementsMatchingQuery( + elements:ExcalidrawElement[], + query:string[], + selectResult:boolean = true, + exactMatch: boolean = false //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530 + ) { if(!elements || elements.length === 0 || !query || query.length === 0) { return; } const match = elements.filter((el: any) => - query.some((q) => - el.rawText.toLowerCase().replaceAll("\n", " ").match(q.toLowerCase()), - ), + query.some((q) => { + const text = el.rawText.toLowerCase().replaceAll("\n", " ").trim(); + if(exactMatch) { + const m = text.match(/^#*(# .*)/); + if(!m || m.length!==2) return false; + return m[1] === q.toLowerCase(); + } + return text.match(q.toLowerCase()); //to distinguish between "# frame" and "# frame 1" + }) ); if (match.length === 0) { new Notice("I could not find a matching text element"); @@ -2670,10 +2716,37 @@ export default class ExcalidrawView extends TextFileView { commitToHistory?: boolean, }, restore: boolean = false) { if(!this.excalidrawAPI) return; - if(scene.elements && restore) { + const shouldRestoreElements = scene.elements && restore; + if(shouldRestoreElements) { scene.elements = this.excalidrawAPI.restore(scene).elements; } - this.excalidrawAPI.updateScene(scene); + try { + this.excalidrawAPI.updateScene(scene); + } catch(e) { + errorlog({ + where:"ExcalidrawView.updateScene 1st attempt", + fn:this.updateScene, + error: e, + scene: scene, + willDoSecondAttempt: !shouldRestoreElements + }); + if(!shouldRestoreElements) { //second attempt + try { + scene.elements = this.excalidrawAPI.restore(scene).elements; + this.excalidrawAPI.updateScene(scene); + } catch (e) { + errorlog({ + where:"ExcalidrawView.updateScene 2nd attempt", + fn:this.updateScene, + error: e, + scene: scene + }); + warningUnknowSeriousError(); + } + } else { + warningUnknowSeriousError(); + } + } } } diff --git a/src/Messages.ts b/src/Messages.ts index cf8707b..722f3ee 100644 --- a/src/Messages.ts +++ b/src/Messages.ts @@ -17,6 +17,13 @@ I develop this plugin as a hobby, spending most of my free time doing this. If y
`, +"1.6.21": ` +Before I move on to implementing further features, I spent this week with further stabilizing and debugging the plugin. Hopefully this will result in a smoother, better experince for you all. + +## Fixed +- Links in drawings (e.g. text elements or embedded images) were sometimes not updating when the source file was moved or renamed in your Vault. The issue happend when you had the drawing and the linked file open in panes next to each other. This has led to broken links. ([#546](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/546)) +- To remove complexity and potential error, I have hidden the autosave settings. From now, autosave is now always enabled. Excalidraw will attempt to save your drawing every 10 seconds, or if you are actively engaged in drawing a shape at that very moment (e.g. you are busy with a freedraw line), then autosave will save the drawing at the earliest next opportunity. I imlemented further triggers to save the drawing when there are changes in the drawing and you click outside the drawing canvas. There was a rare error involving text elements, that when happened blocked saving of the file. This error is now properly handeled. Also from now, you will receive a warning message if for any reason save encountered problems. +- If you have two heading sections in your drawing, e.g. ${String.fromCharCode(96)}# Section abc${String.fromCharCode(96)} and ${String.fromCharCode(96)}# Section abc def${String.fromCharCode(96)}, then referencing ${String.fromCharCode(96)}[[#Section abc]]${String.fromCharCode(96)} in a link will highlight both text elements when clicking the link. These section references now work as expected. ([#530](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530))`, "1.6.20": `