This commit is contained in:
Zsolt Viczian
2022-03-27 19:33:49 +02:00
parent 1aa092fcc4
commit a25eebfa28
8 changed files with 228 additions and 53 deletions

View File

@@ -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",

View File

@@ -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),

View File

@@ -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);

View File

@@ -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();
}
}
}
}

View File

@@ -17,6 +17,13 @@ I develop this plugin as a hobby, spending most of my free time doing this. If y
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://cdn.ko-fi.com/cdn/kofi3.png?v=3" height=45></a></div>
`,
"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": `
<div class="excalidraw-videoWrapper"><div>
<iframe src="https://www.youtube.com/embed/U2LkBRBk4LY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

View File

@@ -119,7 +119,7 @@ export default class ExcalidrawPlugin extends Plugin {
public insertLinkDialog: InsertLinkDialog;
public insertImageDialog: InsertImageDialog;
public insertMDDialog: InsertMDDialog;
private activeExcalidrawView: ExcalidrawView = null;
public activeExcalidrawView: ExcalidrawView = null;
public lastActiveExcalidrawFilePath: string = null;
public hover: { linkText: string; sourcePath: string } = {
linkText: null,
@@ -128,6 +128,9 @@ export default class ExcalidrawPlugin extends Plugin {
private observer: MutationObserver;
private themeObserver: MutationObserver;
private fileExplorerObserver: MutationObserver;
private modalContainerObserver: MutationObserver;
private workspaceDrawerLeftObserver: MutationObserver;
private workspaceDrawerRightObserver: MutationObserver;
public opencount: number = 0;
public ea: ExcalidrawAutomate;
//A master list of fileIds to facilitate copy / paste
@@ -1524,6 +1527,8 @@ export default class ExcalidrawPlugin extends Plugin {
),
);
self.addFileSaveTriggerEventHandlers();
const metaCache: MetadataCache = self.app.metadataCache;
//@ts-ignore
metaCache.getCachedFiles().forEach((filename: string) => {
@@ -1546,6 +1551,83 @@ export default class ExcalidrawPlugin extends Plugin {
});
}
addFileSaveTriggerEventHandlers() {
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/551
const onClickEventSaveActiveDrawing = (e:PointerEvent) => {
if(
!this.activeExcalidrawView ||
!this.activeExcalidrawView.semaphores.dirty ||
//@ts-ignore
e.target?.className === "excalidraw__canvas"
) {
return;
}
this.activeExcalidrawView.save();
}
this.registerEvent(
this.app.workspace.on("click",onClickEventSaveActiveDrawing)
);
const onFileMenuEventSaveActiveDrawing = () => {
if(
!this.activeExcalidrawView ||
!this.activeExcalidrawView.semaphores.dirty
) {
return;
}
this.activeExcalidrawView.save();
}
this.registerEvent(
this.app.workspace.on("file-menu",onFileMenuEventSaveActiveDrawing)
);
//The user clicks settings, or "open another vault", or the command palette
this.modalContainerObserver = new MutationObserver(async (m: MutationRecord[]) => {
if (
m.length !== 1 ||
m[0].type !== "childList" ||
m[0].addedNodes.length !== 1 ||
!this.activeExcalidrawView ||
!this.activeExcalidrawView.semaphores.dirty
) {
return;
}
this.activeExcalidrawView.save();
});
this.modalContainerObserver.observe(document.body, {
childList: true,
});
//when the user activates the sliding drawers on Obsidian Mobile
const leftWorkspaceDrawer = document.querySelector(".workspace-drawer.mod-left");
const rightWorkspaceDrawer = document.querySelector(".workspace-drawer.mod-right");
if(leftWorkspaceDrawer || rightWorkspaceDrawer) {
const action = async (m: MutationRecord[]) => {
if(m[0].oldValue !== "display: none;" ||
!this.activeExcalidrawView ||
!this.activeExcalidrawView.semaphores.dirty
) {
return;
}
this.activeExcalidrawView.save();
};
const options = {
attributeOldValue: true,
attributeFilter: ["style"],
}
if(leftWorkspaceDrawer) {
this.workspaceDrawerLeftObserver = new MutationObserver(action);
this.workspaceDrawerLeftObserver.observe(leftWorkspaceDrawer, options);
}
if(rightWorkspaceDrawer) {
this.workspaceDrawerRightObserver = new MutationObserver(action);
this.workspaceDrawerRightObserver.observe(rightWorkspaceDrawer, options);
}
}
}
updateFileCache(
file: TFile,
frontmatter?: FrontMatterCache,
@@ -1573,6 +1655,13 @@ export default class ExcalidrawPlugin extends Plugin {
}
this.observer.disconnect();
this.themeObserver.disconnect();
this.modalContainerObserver.disconnect();
if(this.workspaceDrawerLeftObserver) {
this.workspaceDrawerLeftObserver.disconnect();
}
if(this.workspaceDrawerRightObserver) {
this.workspaceDrawerRightObserver.disconnect();
}
if (this.fileExplorerObserver) {
this.fileExplorerObserver.disconnect();
}
@@ -1636,6 +1725,8 @@ export default class ExcalidrawPlugin extends Plugin {
public async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
setLeftHandedMode(this.settings.isLeftHanded);
this.settings.autosave = true;
this.settings.autosaveInterval= 10000;
}
async saveSettings() {

View File

@@ -402,7 +402,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}),
);
let autosaveDropdown: DropdownComponent;
/* let autosaveDropdown: DropdownComponent;
new Setting(containerEl)
.setName(t("AUTOSAVE_NAME"))
@@ -443,7 +443,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
this.applySettingsUpdate(true);
},
);
});
});*/
this.containerEl.createEl("h1", { text: t("DISPLAY_HEAD") });

View File

@@ -1,4 +1,4 @@
{
"1.6.20": "0.12.16",
"1.6.21": "0.12.16",
"1.4.2": "0.11.13"
}