mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53c27f2a59 | ||
|
|
db80f5c715 |
@@ -103,7 +103,7 @@ const selectAttributesToCopy = () => {
|
||||
|
||||
attributes.forEach(attr => {
|
||||
const attrValue = elements[0][attr.key];
|
||||
if(attrValue || (attr.key === "startArrowhead" && elements[0].type === "arrow") || (attr.key === "endArrowhead" && elements[0].type === "arrow")) {
|
||||
if((typeof attrValue !== "undefined" && attrValue !== null) || (attr.key === "startArrowhead" && elements[0].type === "arrow") || (attr.key === "endArrowhead" && elements[0].type === "arrow")) {
|
||||
let description = '';
|
||||
|
||||
switch(attr.key) {
|
||||
@@ -190,7 +190,9 @@ const selectAttributesToCopy = () => {
|
||||
|
||||
|
||||
configModal.onClose = () => {
|
||||
setTimeout(()=>delete configModal);
|
||||
setTimeout(()=>{
|
||||
delete configModal
|
||||
});
|
||||
}
|
||||
|
||||
configModal.open();
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "2.2.8",
|
||||
"version": "2.2.9",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -127,7 +127,7 @@ import { anyModifierKeysPressed, emulateKeysForLinkClick, webbrowserDragModifier
|
||||
import { setDynamicStyle } from "./utils/DynamicStyling";
|
||||
import { InsertPDFModal } from "./dialogs/InsertPDFModal";
|
||||
import { CustomEmbeddable, renderWebView } from "./customEmbeddable";
|
||||
import { addBackOfTheNoteCard, getExcalidrawFileForwardLinks, getFrameBasedOnFrameNameOrId, getLinkTextFromLink, insertEmbeddableToView, insertImageToView, openExternalLink, openTagSearch, renderContextMenuAction, tmpBruteForceCleanup } from "./utils/ExcalidrawViewUtils";
|
||||
import { addBackOfTheNoteCard, getExcalidrawFileForwardLinks, getFrameBasedOnFrameNameOrId, getLinkTextFromLink, insertEmbeddableToView, insertImageToView, openExternalLink, openTagSearch, parseObsidianLink, renderContextMenuAction, tmpBruteForceCleanup } from "./utils/ExcalidrawViewUtils";
|
||||
import { imageCache } from "./utils/ImageCache";
|
||||
import { CanvasNodeFactory, ObsidianCanvasNode } from "./utils/CanvasNodeFactory";
|
||||
import { EmbeddableMenu } from "./menu/EmbeddableActionsMenu";
|
||||
@@ -640,10 +640,9 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(png);
|
||||
const self = this;
|
||||
reader.onloadend = function () {
|
||||
reader.onloadend = () => {
|
||||
const base64data = reader.result;
|
||||
download(null, base64data, `${self.file.basename}.png`);
|
||||
download(null, base64data, `${this.file.basename}.png`);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -683,12 +682,17 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
|
||||
async save(preventReload: boolean = true, forcesave: boolean = false, overrideEmbeddableIsEditingSelfDebounce: boolean = false) {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.save, "ExcalidrawView.save, enter", preventReload, forcesave);
|
||||
if ((process.env.NODE_ENV === 'development')) {
|
||||
if (DEBUGGING) {
|
||||
debug(this.save, "ExcalidrawView.save, enter", preventReload, forcesave);
|
||||
console.trace();
|
||||
}
|
||||
}
|
||||
/*if(this.semaphores.viewunload && (this.ownerWindow !== window)) {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.save, `ExcalidrawView.save, view is unloading, aborting save`);
|
||||
return;
|
||||
}*/
|
||||
|
||||
|
||||
if(!this.isLoaded) {
|
||||
return;
|
||||
}
|
||||
@@ -759,6 +763,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
|
||||
await super.save();
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
if (DEBUGGING) {
|
||||
debug(this.save, `ExcalidrawView.save, super.save finished`, this.file);
|
||||
console.trace();
|
||||
}
|
||||
}
|
||||
//saving to backup with a delay in case application closes in the meantime, I want to avoid both save and backup corrupted.
|
||||
const path = this.file.path;
|
||||
//@ts-ignore
|
||||
@@ -1026,7 +1036,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
? this.excalidrawData.getRawText(selectedText.id)
|
||||
: selectedText.text);
|
||||
|
||||
const partsArray = REGEX_LINK.getResList(linkText);
|
||||
const maybeObsidianLink = parseObsidianLink(linkText, this.app);
|
||||
if(typeof maybeObsidianLink === "string") {
|
||||
linkText = maybeObsidianLink;
|
||||
}
|
||||
|
||||
const partsArray = REGEX_LINK.getResList(linkText);
|
||||
if (!linkText || partsArray.length === 0) {
|
||||
//the container link takes precedence over the text link
|
||||
if(selectedTextElement?.containerId) {
|
||||
@@ -1074,6 +1089,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if(this.handleLinkHookCall(selectedElement,linkText,ev)) return;
|
||||
if(openExternalLink(linkText, this.app)) return;
|
||||
|
||||
const maybeObsidianLink = parseObsidianLink(linkText,this.app);
|
||||
if (typeof maybeObsidianLink === "boolean" && maybeObsidianLink) return;
|
||||
if (typeof maybeObsidianLink === "string") {
|
||||
linkText = maybeObsidianLink;
|
||||
}
|
||||
|
||||
const result = await linkPrompt(linkText, this.app, this);
|
||||
if(!result) return;
|
||||
[file, linkText, subpath] = result;
|
||||
@@ -1233,6 +1254,8 @@ export default class ExcalidrawView extends TextFileView {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
//if link will open in the same pane I want to save the drawing before opening the link
|
||||
await this.forceSaveIfRequired();
|
||||
const {leaf, promise} = openLeaf({
|
||||
plugin: this.plugin,
|
||||
fnGetLeaf: () => getLeaf(this.plugin,this.leaf,keys),
|
||||
@@ -1470,20 +1493,21 @@ export default class ExcalidrawView extends TextFileView {
|
||||
|
||||
this.offsetLeft = parent.offsetLeft;
|
||||
this.offsetTop = parent.offsetTop;
|
||||
const self = this;
|
||||
|
||||
//triggers when the leaf is moved in the workspace
|
||||
const observerFn = async (m: MutationRecord[]) => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(observerFn, `ExcalidrawView.parentMoveObserver, file:${self.file?.name}`);
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(observerFn, `ExcalidrawView.parentMoveObserver, file:${this.file?.name}`);
|
||||
const target = m[0].target;
|
||||
if (!(target instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
const { offsetLeft, offsetTop } = target;
|
||||
if (offsetLeft !== self.offsetLeft || offsetTop !== self.offsetTop) {
|
||||
if (self.excalidrawAPI) {
|
||||
self.refreshCanvasOffset();
|
||||
if (offsetLeft !== this.offsetLeft || offsetTop !== this.offsetTop) {
|
||||
if (this.excalidrawAPI) {
|
||||
this.refreshCanvasOffset();
|
||||
}
|
||||
self.offsetLeft = offsetLeft;
|
||||
self.offsetTop = offsetTop;
|
||||
this.offsetLeft = offsetLeft;
|
||||
this.offsetTop = offsetTop;
|
||||
}
|
||||
};
|
||||
this.parentMoveObserver = DEBUGGING
|
||||
@@ -1602,9 +1626,8 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.autosaveTimer = null;
|
||||
if (this.excalidrawAPI) {
|
||||
this.semaphores.autosaving = true;
|
||||
const self = this;
|
||||
//changed from await to then to avoid lag during saving of large file
|
||||
this.save().then(()=>self.semaphores.autosaving = false);
|
||||
this.save().then(()=>this.semaphores.autosaving = false);
|
||||
}
|
||||
this.autosaveTimer = window.setTimeout(
|
||||
timer,
|
||||
@@ -1652,9 +1675,16 @@ export default class ExcalidrawView extends TextFileView {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.onUnloadFile,`ExcalidrawView.onUnloadFile, file:${this.file?.name}`);
|
||||
}
|
||||
|
||||
//onClose happens after onunload
|
||||
protected async onClose(): Promise<void> {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.onClose,`ExcalidrawView.onClose, file:${this.file?.name}`);
|
||||
private async forceSaveIfRequired():Promise<boolean> {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.forceSaveIfRequired,`ExcalidrawView.forceSaveIfRequired`);
|
||||
let watchdog = 0;
|
||||
let dirty = false;
|
||||
//if saving was already in progress
|
||||
//the function awaits the save to finish.
|
||||
while (this.semaphores.saving && watchdog++ < 10) {
|
||||
dirty = true;
|
||||
await sleep(20);
|
||||
}
|
||||
if(this.excalidrawAPI) {
|
||||
this.checkSceneVersion(this.excalidrawAPI.getSceneElements());
|
||||
if(this.isDirty()) {
|
||||
@@ -1663,11 +1693,18 @@ export default class ExcalidrawView extends TextFileView {
|
||||
window.setTimeout(() => {
|
||||
plugin.triggerEmbedUpdates(path)
|
||||
},400);
|
||||
|
||||
dirty = true;
|
||||
await this.save(true,true,true);
|
||||
}
|
||||
}
|
||||
return dirty;
|
||||
}
|
||||
|
||||
//onClose happens after onunload
|
||||
protected async onClose(): Promise<void> {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.onClose,`ExcalidrawView.onClose, file:${this.file?.name}`);
|
||||
|
||||
await this.forceSaveIfRequired();
|
||||
if (this.excalidrawRoot) {
|
||||
this.excalidrawRoot.unmount();
|
||||
this.excalidrawRoot = null;
|
||||
@@ -1858,7 +1895,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
const self = this;
|
||||
|
||||
let query: string[] = null;
|
||||
|
||||
if (
|
||||
@@ -1879,10 +1916,10 @@ export default class ExcalidrawView extends TextFileView {
|
||||
const waitForExcalidraw = async () => {
|
||||
let counter = 0;
|
||||
while (
|
||||
(self.semaphores.justLoaded ||
|
||||
!self.isLoaded ||
|
||||
!self.excalidrawAPI ||
|
||||
self.excalidrawAPI?.getAppState()?.isLoading) &&
|
||||
(this.semaphores.justLoaded ||
|
||||
!this.isLoaded ||
|
||||
!this.excalidrawAPI ||
|
||||
this.excalidrawAPI?.getAppState()?.isLoading) &&
|
||||
counter++<100
|
||||
) await sleep(50); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/734
|
||||
}
|
||||
@@ -1898,16 +1935,16 @@ export default class ExcalidrawView extends TextFileView {
|
||||
window.setTimeout(async () => {
|
||||
await waitForExcalidraw();
|
||||
if(filenameParts.blockref && !filenameParts.hasGroupref) {
|
||||
if(!self.getScene()?.elements.find((el:ExcalidrawElement)=>el.id === filenameParts.blockref)) {
|
||||
if(!this.getScene()?.elements.find((el:ExcalidrawElement)=>el.id === filenameParts.blockref)) {
|
||||
const cleanQuery = cleanSectionHeading(filenameParts.blockref).replaceAll(" ","");
|
||||
const blocks = await self.getBackOfTheNoteBlocks();
|
||||
const blocks = await this.getBackOfTheNoteBlocks();
|
||||
if(blocks.includes(cleanQuery)) {
|
||||
this.setMarkdownView(state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
window.setTimeout(()=>self.zoomToElementId(filenameParts.blockref, filenameParts.hasGroupref));
|
||||
window.setTimeout(()=>this.zoomToElementId(filenameParts.blockref, filenameParts.hasGroupref));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1921,7 +1958,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
window.setTimeout(async () => {
|
||||
await waitForExcalidraw();
|
||||
|
||||
const api = self.excalidrawAPI;
|
||||
const api = this.excalidrawAPI;
|
||||
if (!api) return;
|
||||
if (api.getAppState().isLoading) return;
|
||||
|
||||
@@ -1933,17 +1970,17 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if(parts) {
|
||||
const linkText = REGEX_LINK.getLink(parts);
|
||||
if(linkText) {
|
||||
const file = self.plugin.app.metadataCache.getFirstLinkpathDest(linkText, self.file.path);
|
||||
const file = this.plugin.app.metadataCache.getFirstLinkpathDest(linkText, this.file.path);
|
||||
if(file) {
|
||||
let fileId:FileId[] = [];
|
||||
self.excalidrawData.files.forEach((ef,fileID) => {
|
||||
this.excalidrawData.files.forEach((ef,fileID) => {
|
||||
if(ef.file?.path === file.path) fileId.push(fileID);
|
||||
});
|
||||
if(fileId.length>0) {
|
||||
const images = elements.filter(el=>el.type === "image" && fileId.includes(el.fileId));
|
||||
if(images.length>0) {
|
||||
this.preventAutozoom();
|
||||
window.setTimeout(()=>self.zoomToElements(!api.getAppState().viewModeEnabled, images));
|
||||
window.setTimeout(()=>this.zoomToElements(!api.getAppState().viewModeEnabled, images));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1951,24 +1988,24 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
}
|
||||
|
||||
if(!self.selectElementsMatchingQuery(
|
||||
if(!this.selectElementsMatchingQuery(
|
||||
elements,
|
||||
query,
|
||||
!api.getAppState().viewModeEnabled,
|
||||
filenameParts.hasSectionref,
|
||||
filenameParts.hasGroupref
|
||||
)) {
|
||||
const cleanQuery = cleanSectionHeading(query[0]).replaceAll(" ","");
|
||||
const sections = await self.getBackOfTheNoteSections();
|
||||
const cleanQuery = cleanSectionHeading(query[0]);
|
||||
const sections = await this.getBackOfTheNoteSections();
|
||||
if(sections.includes(cleanQuery)) {
|
||||
self.setMarkdownView(state);
|
||||
this.setMarkdownView(state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
super.setEphemeralState(state);
|
||||
//super.setEphemeralState(state);
|
||||
}
|
||||
|
||||
// clear the view content
|
||||
@@ -2021,41 +2058,40 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.lastSaveTimestamp = this.file.stat.mtime;
|
||||
this.lastLoadedFile = this.file;
|
||||
data = this.data = data.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(self.setViewData, `ExcalidrawView.setViewData > app.workspace.onLayoutReady, file:${self.file?.name}, isActiveLeaf:${self?.app?.workspace?.activeLeaf === self.leaf}`);
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.setViewData, `ExcalidrawView.setViewData > app.workspace.onLayoutReady, file:${this.file?.name}, isActiveLeaf:${this?.app?.workspace?.activeLeaf === this.leaf}`);
|
||||
//the leaf moved to a window and ExcalidrawView was destructed
|
||||
//Happens during Obsidian startup if View opens in new window.
|
||||
if(!self?.app) {
|
||||
if(!this?.app) {
|
||||
return;
|
||||
}
|
||||
let counter = 0;
|
||||
while (!self.file && counter++<50) await sleep(50);
|
||||
if(!self.file) return;
|
||||
self.compatibilityMode = self.file.extension === "excalidraw";
|
||||
await self.plugin.loadSettings();
|
||||
if (self.compatibilityMode) {
|
||||
self.plugin.enableLegacyFilePopoverObserver();
|
||||
self.actionButtons['isRaw'].hide();
|
||||
self.actionButtons['isParsed'].hide();
|
||||
self.actionButtons['link'].hide();
|
||||
self.textMode = TextMode.raw;
|
||||
await self.excalidrawData.loadLegacyData(data, self.file);
|
||||
if (!self.plugin.settings.compatibilityMode) {
|
||||
while (!this.file && counter++<50) await sleep(50);
|
||||
if(!this.file) return;
|
||||
this.compatibilityMode = this.file.extension === "excalidraw";
|
||||
await this.plugin.loadSettings();
|
||||
if (this.compatibilityMode) {
|
||||
this.plugin.enableLegacyFilePopoverObserver();
|
||||
this.actionButtons['isRaw'].hide();
|
||||
this.actionButtons['isParsed'].hide();
|
||||
this.actionButtons['link'].hide();
|
||||
this.textMode = TextMode.raw;
|
||||
await this.excalidrawData.loadLegacyData(data, this.file);
|
||||
if (!this.plugin.settings.compatibilityMode) {
|
||||
new Notice(t("COMPATIBILITY_MODE"), 4000);
|
||||
}
|
||||
self.excalidrawData.disableCompression = true;
|
||||
this.excalidrawData.disableCompression = true;
|
||||
} else {
|
||||
self.actionButtons['link'].show();
|
||||
self.excalidrawData.disableCompression = false;
|
||||
this.actionButtons['link'].show();
|
||||
this.excalidrawData.disableCompression = false;
|
||||
const textMode = getTextMode(data);
|
||||
self.changeTextMode(textMode, false);
|
||||
this.changeTextMode(textMode, false);
|
||||
try {
|
||||
if (
|
||||
!(await self.excalidrawData.loadData(
|
||||
!(await this.excalidrawData.loadData(
|
||||
data,
|
||||
self.file,
|
||||
self.textMode,
|
||||
this.file,
|
||||
this.textMode,
|
||||
))
|
||||
) {
|
||||
return;
|
||||
@@ -2063,12 +2099,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
} catch (e) {
|
||||
errorlog({ where: "ExcalidrawView.setViewData", error: e });
|
||||
if(e.message === ERROR_IFRAME_CONVERSION_CANCELED) {
|
||||
self.setMarkdownView();
|
||||
this.setMarkdownView();
|
||||
return;
|
||||
}
|
||||
const file = self.file;
|
||||
const plugin = self.plugin;
|
||||
const leaf = self.leaf;
|
||||
const file = this.file;
|
||||
const plugin = this.plugin;
|
||||
const leaf = this.leaf;
|
||||
(async () => {
|
||||
let confirmation:boolean = true;
|
||||
let counter = 0;
|
||||
@@ -2114,18 +2150,18 @@ export default class ExcalidrawView extends TextFileView {
|
||||
|
||||
|
||||
})();
|
||||
self.setMarkdownView();
|
||||
this.setMarkdownView();
|
||||
return;
|
||||
}
|
||||
}
|
||||
await self.loadDrawing(true);
|
||||
await this.loadDrawing(true);
|
||||
|
||||
if(self.plugin.ea.onFileOpenHook) {
|
||||
if(this.plugin.ea.onFileOpenHook) {
|
||||
const tempEA = getEA(this);
|
||||
try {
|
||||
await self.plugin.ea.onFileOpenHook({
|
||||
await this.plugin.ea.onFileOpenHook({
|
||||
ea: tempEA,
|
||||
excalidrawFile: self.file,
|
||||
excalidrawFile: this.file,
|
||||
view: this,
|
||||
});
|
||||
} catch(e) {
|
||||
@@ -2135,20 +2171,19 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
}
|
||||
|
||||
const script = self.excalidrawData.getOnLoadScript();
|
||||
const script = this.excalidrawData.getOnLoadScript();
|
||||
if(script) {
|
||||
const self = this;
|
||||
const scriptname = self.file.basename+ "-onlaod-script";
|
||||
const scriptname = this.file.basename+ "-onlaod-script";
|
||||
const runScript = () => {
|
||||
if(!self.excalidrawAPI) { //need to wait for Excalidraw to initialize
|
||||
window.setTimeout(runScript,200);
|
||||
if(!this.excalidrawAPI) { //need to wait for Excalidraw to initialize
|
||||
window.setTimeout(runScript.bind(this),200);
|
||||
return;
|
||||
}
|
||||
self.plugin.scriptEngine.executeScript(self,script,scriptname,self.file);
|
||||
this.plugin.scriptEngine.executeScript(this,script,scriptname,this.file);
|
||||
}
|
||||
runScript();
|
||||
}
|
||||
self.isLoaded = true;
|
||||
this.isLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2188,11 +2223,10 @@ export default class ExcalidrawView extends TextFileView {
|
||||
//in case one or more files have not loaded retry later hoping that sync has delivered the file in the mean time.
|
||||
this.excalidrawData.getFiles().some(ef=>{
|
||||
if(ef && !ef.file && ef.attemptCounter<30) {
|
||||
const self = this;
|
||||
const currentFile = this.file.path;
|
||||
window.setTimeout(async ()=>{
|
||||
if(self && self.excalidrawAPI && currentFile === self.file.path) {
|
||||
self.loadSceneFiles();
|
||||
if(this && this.excalidrawAPI && currentFile === this.file.path) {
|
||||
this.loadSceneFiles();
|
||||
}
|
||||
},2000)
|
||||
return true;
|
||||
@@ -2562,8 +2596,17 @@ export default class ExcalidrawView extends TextFileView {
|
||||
return ICON_NAME;
|
||||
}
|
||||
|
||||
setMarkdownView(eState?: any) {
|
||||
async setMarkdownView(eState?: any) {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.setMarkdownView, "ExcalidrawView.setMarkdownView", eState);
|
||||
//save before switching to markdown view.
|
||||
//this would also happen onClose, but it does not hurt to save it here
|
||||
//this way isDirty() will return false in onClose, thuse
|
||||
//saving here will not result in double save
|
||||
//there was a race condition when clicking a link with a section or block reference to the back-of-the-note
|
||||
//that resulted in a call to save after the view has been destroyed
|
||||
//The sleep is required for metadata cache to be updated with the location of the block or section
|
||||
await this.forceSaveIfRequired();
|
||||
await sleep(200); //dirty hack to wait for Obsidian metadata to be updated, note that save may have been triggered elsewhere already
|
||||
this.plugin.excalidrawFileModes[this.id || this.file.path] = "markdown";
|
||||
this.plugin.setMarkdownView(this.leaf, eState);
|
||||
}
|
||||
@@ -3393,8 +3436,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
|
||||
this.semaphores.hoverSleep = true;
|
||||
const self = this;
|
||||
window.setTimeout(() => (self.semaphores.hoverSleep = false), 500);
|
||||
window.setTimeout(() => (this.semaphores.hoverSleep = false), 500);
|
||||
this.plugin.hover.linkText = linktext;
|
||||
this.plugin.hover.sourcePath = this.file.path;
|
||||
this.hoverPreviewTarget = this.contentEl; //e.target;
|
||||
@@ -3408,14 +3450,13 @@ export default class ExcalidrawView extends TextFileView {
|
||||
});
|
||||
this.hoverPoint = this.currentPosition;
|
||||
if (this.isFullscreen()) {
|
||||
const self = this;
|
||||
window.setTimeout(() => {
|
||||
const popover =
|
||||
this.ownerDocument.querySelector(`div.popover-title[data-path="${f.path}"]`)
|
||||
?.parentElement?.parentElement?.parentElement ??
|
||||
this.ownerDocument.body.querySelector("div.popover");
|
||||
if (popover) {
|
||||
self.contentEl.append(popover);
|
||||
this.contentEl.append(popover);
|
||||
}
|
||||
}, 400);
|
||||
}
|
||||
@@ -3719,8 +3760,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
}
|
||||
if (data.elements) {
|
||||
const self = this;
|
||||
window.setTimeout(() => self.save(), 300); //removed prevent reload = false, as reload was triggered when pasted containers were processed and there was a conflict with the new elements
|
||||
window.setTimeout(() => this.save(), 30); //removed prevent reload = false, as reload was triggered when pasted containers were processed and there was a conflict with the new elements
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -4003,7 +4043,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if(link.file.extension === "pdf") {
|
||||
const insertPDFModal = new InsertPDFModal(this.plugin, this);
|
||||
insertPDFModal.open(link.file);
|
||||
return false;
|
||||
//return false;
|
||||
}
|
||||
const ea = getEA(this) as ExcalidrawAutomate;
|
||||
insertImageToView(ea, pos, link.file).then(()=>ea.destroy()) ;
|
||||
@@ -4065,7 +4105,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
//return false;
|
||||
}
|
||||
}
|
||||
if(localFileDragAction === "embeddable" || !IMAGE_TYPES.contains(extension)) {
|
||||
else if(localFileDragAction === "embeddable" || !IMAGE_TYPES.contains(extension)) {
|
||||
const ea = getEA(this) as ExcalidrawAutomate;
|
||||
insertEmbeddableToView(ea, pos, null, link.url).then(()=>ea.destroy());
|
||||
if(localFileDragAction !== "embeddable") {
|
||||
@@ -4216,7 +4256,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.onBeforeTextSubmit, "ExcalidrawView.onBeforeTextSubmit", textElement, nextText, nextOriginalText, isDeleted);
|
||||
const api = this.excalidrawAPI;
|
||||
if (!api) {
|
||||
return {updatedNextOriginalText: null, nextLink: null};
|
||||
return {updatedNextOriginalText: null, nextLink: textElement?.link ?? null};
|
||||
}
|
||||
|
||||
// 1. Set the isEditingText flag to true to prevent autoresize on mobile
|
||||
@@ -4299,7 +4339,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.setDirty(9);
|
||||
}
|
||||
});
|
||||
return {updatedNextOriginalText: null, nextLink: null};
|
||||
return {updatedNextOriginalText: null, nextLink: textElement.link};
|
||||
} else {
|
||||
new Notice(t("USE_INSERT_FILE_MODAL"),5000);
|
||||
}
|
||||
@@ -4372,12 +4412,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
//don't forget the case: link-prefix:"" && link-brackets:true
|
||||
return {updatedNextOriginalText: parseResultOriginal, nextLink: link};
|
||||
}
|
||||
return {updatedNextOriginalText: null, nextLink: null};
|
||||
return {updatedNextOriginalText: null, nextLink: textElement.link};
|
||||
} //There were no links to parse, raw text and parsed text are equivalent
|
||||
api.history.clear();
|
||||
return {updatedNextOriginalText: parseResultOriginal, nextLink:link};
|
||||
}
|
||||
return {updatedNextOriginalText: null, nextLink: null};
|
||||
return {updatedNextOriginalText: null, nextLink: textElement.link};
|
||||
}
|
||||
// even if the text did not change, container sizes might need to be updated
|
||||
if (containerId) {
|
||||
@@ -4387,7 +4427,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
const parseResultOriginal = this.excalidrawData.getParsedText(textElement.id);
|
||||
return {updatedNextOriginalText: parseResultOriginal, nextLink: textElement.link};
|
||||
}
|
||||
return {updatedNextOriginalText: null, nextLink: null};
|
||||
return {updatedNextOriginalText: null, nextLink: textElement.link};
|
||||
}
|
||||
|
||||
private async onLinkOpen(element: ExcalidrawElement, e: any): Promise<void> {
|
||||
@@ -4404,7 +4444,8 @@ export default class ExcalidrawView extends TextFileView {
|
||||
|
||||
let event = e?.detail?.nativeEvent;
|
||||
if(this.handleLinkHookCall(element,element.link,event)) return;
|
||||
if(openExternalLink(element.link, this.app, !isSHIFT(event) && !isWinCTRLorMacCMD(event) && !isWinMETAorMacCTRL(event) && !isWinALTorMacOPT(event) ? element : undefined)) return;
|
||||
//if(openExternalLink(element.link, this.app, !isSHIFT(event) && !isWinCTRLorMacCMD(event) && !isWinMETAorMacCTRL(event) && !isWinALTorMacOPT(event) ? element : undefined)) return;
|
||||
if(openExternalLink(element.link, this.app)) return;
|
||||
|
||||
//if element is type text and element has multiple links, then submit the element text to linkClick to trigger link suggester
|
||||
if(element.type === "text") {
|
||||
@@ -4418,7 +4459,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if (!event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
||||
event = emulateKeysForLinkClick("new-tab");
|
||||
}
|
||||
|
||||
|
||||
this.linkClick(
|
||||
event,
|
||||
null,
|
||||
|
||||
@@ -46,7 +46,9 @@ export class InsertCommandDialog extends FuzzySuggestModal<TFile> {
|
||||
}
|
||||
|
||||
onClose(): void {
|
||||
this.addText = null;
|
||||
window.setTimeout(()=>{
|
||||
this.addText = null;
|
||||
}) //onChooseItem must run first
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,9 @@ export class InsertLinkDialog extends FuzzySuggestModal<TFile> {
|
||||
}
|
||||
|
||||
onClose(): void {
|
||||
this.addText = null;
|
||||
window.setTimeout(()=>{
|
||||
this.addText = null
|
||||
}); //make sure this happens after onChooseItem runs
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,19 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
|
||||
|
||||
<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>
|
||||
`,
|
||||
"2.2.9": `
|
||||
## New
|
||||
- Improved the "Open the back-of-the-note of the selected Excalidraw image" action. It now works with grouped elements and keeps the popout window within the visible screen area when elements are close to the top of the canvas. Note: Due to an Obsidian bug, I do not recommend using this feature with versions 1.6.0 - 1.6.6, if you have Obsidian Sync enabled, because Obsidian may freeze when closing the popout window. It functions properly in Obsidian versions before 1.6.0 and from 1.6.7 onwards.
|
||||
|
||||
## Fixed
|
||||
- Drag and drop from a local folder (outside Obsidian) resulted in duplicate images.
|
||||
- Insert Link Action did not work [#1873](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1873)
|
||||
- Insert Obsidian Command Action did not work
|
||||
- Element link for text element got deleted when editing the text. [#1878](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1878)
|
||||
- When back-of-the-drawing Section Headings have spaces in them, clicking the link opens the drawing side not the markdown side. [#1877](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1877)
|
||||
- obsidian:// links did not work as expected. [#1872](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1872)
|
||||
- copying and moving a rectangle with text, moves the text unexpectedly. The issue should now be resolved (at least much less likely to occur) [#1867](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1867)
|
||||
`,
|
||||
"2.2.8": `
|
||||
While this release may appear modest with no new features, it represents nearly 50 hours of dedicated work. Here's what's been improved:
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import { MAX_IMAGE_SIZE, REG_LINKINDEX_INVALIDCHARS } from "src/constants/constants";
|
||||
import { REGEX_LINK } from "src/ExcalidrawData";
|
||||
import { ScriptEngine } from "src/Scripts";
|
||||
import { openExternalLink, openTagSearch } from "src/utils/ExcalidrawViewUtils";
|
||||
import { openExternalLink, openTagSearch, parseObsidianLink } from "src/utils/ExcalidrawViewUtils";
|
||||
|
||||
export type ButtonDefinition = { caption: string; tooltip?:string; action: Function };
|
||||
|
||||
@@ -708,7 +708,12 @@ export class ConfirmationPrompt extends Modal {
|
||||
}
|
||||
}
|
||||
|
||||
export const linkPrompt = async (linkText:string, app: App, view?: ExcalidrawView, message: string = "Select link to open"):Promise<[file:TFile, linkText:string, subpath: string]> => {
|
||||
export async function linkPrompt (
|
||||
linkText:string,
|
||||
app: App,
|
||||
view?: ExcalidrawView,
|
||||
message: string = "Select link to open",
|
||||
):Promise<[file:TFile, linkText:string, subpath: string]> {
|
||||
const partsArray = REGEX_LINK.getResList(linkText);
|
||||
let subpath: string = null;
|
||||
let file: TFile = null;
|
||||
@@ -737,6 +742,9 @@ export const linkPrompt = async (linkText:string, app: App, view?: ExcalidrawVie
|
||||
|
||||
linkText = REGEX_LINK.getLink(parts);
|
||||
if(openExternalLink(linkText, app)) return;
|
||||
const maybeObsidianLink = parseObsidianLink(linkText, app, false);
|
||||
if (typeof maybeObsidianLink === "boolean" && maybeObsidianLink) return;
|
||||
if (typeof maybeObsidianLink === "string") linkText = maybeObsidianLink;
|
||||
|
||||
if (linkText.search("#") > -1) {
|
||||
const linkParts = getLinkParts(linkText, view ? view.file : undefined);
|
||||
|
||||
104
src/main.ts
104
src/main.ts
@@ -368,11 +368,10 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
|
||||
this.switchToExcalidarwAfterLoad();
|
||||
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(() => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(self.onload,"ExcalidrawPlugin.onload > app.workspace.onLayoutReady");
|
||||
this.scriptEngine = new ScriptEngine(self);
|
||||
imageCache.initializeDB(self);
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.onload,"ExcalidrawPlugin.onload > app.workspace.onLayoutReady");
|
||||
this.scriptEngine = new ScriptEngine(this);
|
||||
imageCache.initializeDB(this);
|
||||
});
|
||||
this.taskbone = new Taskbone(this);
|
||||
}
|
||||
@@ -380,9 +379,8 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
private setPropertyTypes() {
|
||||
if(!this.settings.loadPropertySuggestions) return;
|
||||
const app = this.app;
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(() => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(self.setPropertyTypes, `ExcalidrawPlugin.setPropertyTypes > app.workspace.onLayoutReady`);
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.setPropertyTypes, `ExcalidrawPlugin.setPropertyTypes > app.workspace.onLayoutReady`);
|
||||
Object.keys(FRONTMATTER_KEYS).forEach((key) => {
|
||||
if(FRONTMATTER_KEYS[key].depricated === true) return;
|
||||
const {name, type} = FRONTMATTER_KEYS[key];
|
||||
@@ -392,9 +390,8 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
|
||||
public initializeFonts() {
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(self.initializeFonts,`ExcalidrawPlugin.initializeFonts > app.workspace.onLayoutReady`);
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.initializeFonts,`ExcalidrawPlugin.initializeFonts > app.workspace.onLayoutReady`);
|
||||
const font = await getFontDataURL(
|
||||
this.app,
|
||||
this.settings.experimantalFourthFont,
|
||||
@@ -448,16 +445,15 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
|
||||
private switchToExcalidarwAfterLoad() {
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(() => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(self.switchToExcalidarwAfterLoad, `ExcalidrawPlugin.switchToExcalidarwAfterLoad > app.workspace.onLayoutReady`);
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.switchToExcalidarwAfterLoad, `ExcalidrawPlugin.switchToExcalidarwAfterLoad > app.workspace.onLayoutReady`);
|
||||
let leaf: WorkspaceLeaf;
|
||||
for (leaf of this.app.workspace.getLeavesOfType("markdown")) {
|
||||
if ( leaf.view instanceof MarkdownView && self.isExcalidrawFile(leaf.view.file)) {
|
||||
if (fileShouldDefaultAsExcalidraw(leaf.view.file?.path, self.app)) {
|
||||
self.excalidrawFileModes[(leaf as any).id || leaf.view.file.path] =
|
||||
if ( leaf.view instanceof MarkdownView && this.isExcalidrawFile(leaf.view.file)) {
|
||||
if (fileShouldDefaultAsExcalidraw(leaf.view.file?.path, this.app)) {
|
||||
this.excalidrawFileModes[(leaf as any).id || leaf.view.file.path] =
|
||||
VIEW_TYPE_EXCALIDRAW;
|
||||
self.setExcalidrawView(leaf);
|
||||
this.setExcalidrawView(leaf);
|
||||
} else {
|
||||
foldExcalidrawSection(leaf.view);
|
||||
}
|
||||
@@ -682,16 +678,15 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
initializeMarkdownPostProcessor(this);
|
||||
this.registerMarkdownPostProcessor(markdownPostProcessor);
|
||||
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(() => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(self.addMarkdownPostProcessor, `ExcalidrawPlugin.addMarkdownPostProcessor > app.workspace.onLayoutReady`);
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.addMarkdownPostProcessor, `ExcalidrawPlugin.addMarkdownPostProcessor > app.workspace.onLayoutReady`);
|
||||
|
||||
// internal-link quick preview
|
||||
self.registerEvent(self.app.workspace.on("hover-link", hoverEvent));
|
||||
this.registerEvent(this.app.workspace.on("hover-link", hoverEvent));
|
||||
|
||||
//only add the legacy file observer if there are legacy files in the vault
|
||||
if(self.app.vault.getFiles().some(f=>f.extension === "excalidraw")) {
|
||||
self.enableLegacyFilePopoverObserver();
|
||||
if(this.app.vault.getFiles().some(f=>f.extension === "excalidraw")) {
|
||||
this.enableLegacyFilePopoverObserver();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -722,10 +717,9 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
const darkClass = bodyClassList.contains('theme-dark');
|
||||
if (mutation?.oldValue?.includes('theme-dark') === darkClass) return;
|
||||
|
||||
const self = this;
|
||||
setTimeout(()=>{ //run async to avoid blocking the UI
|
||||
const theme = isObsidianThemeDark() ? "dark" : "light";
|
||||
const leaves = self.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
leaves.forEach((leaf: WorkspaceLeaf) => {
|
||||
const excalidrawView = leaf.view as ExcalidrawView;
|
||||
if (excalidrawView.file && excalidrawView.excalidrawAPI) {
|
||||
@@ -807,13 +801,12 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
? new CustomMutationObserver(fileExplorerObserverFn, "fileExplorerObserver")
|
||||
: new MutationObserver(fileExplorerObserverFn);
|
||||
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(() => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(self.experimentalFileTypeDisplay, `ExcalidrawPlugin.experimentalFileTypeDisplay > app.workspace.onLayoutReady`);
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.experimentalFileTypeDisplay, `ExcalidrawPlugin.experimentalFileTypeDisplay > app.workspace.onLayoutReady`);
|
||||
document.querySelectorAll(".nav-file-title").forEach(insertFiletype); //apply filetype to files already displayed
|
||||
const container = document.querySelector(".nav-files-container");
|
||||
if (container) {
|
||||
self.fileExplorerObserver.observe(container, {
|
||||
this.fileExplorerObserver.observe(container, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
@@ -1650,26 +1643,31 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if(!view) return false;
|
||||
if(!view.excalidrawAPI) return false;
|
||||
const els = view.getViewSelectedElements().filter(el=>el.type==="image");
|
||||
const els = view
|
||||
.getViewSelectedElements()
|
||||
.filter(el=>{
|
||||
if(el.type==="image") {
|
||||
const ef = view.excalidrawData.getFile(el.fileId);
|
||||
if(!ef) {
|
||||
return false;
|
||||
}
|
||||
return this.isExcalidrawFile(ef.file);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if(els.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
if(checking) return true;
|
||||
const el = els[0] as ExcalidrawImageElement;
|
||||
let ef = view.excalidrawData.getFile(el.fileId);
|
||||
if(!ef) {
|
||||
return false;
|
||||
}
|
||||
if(!this.isExcalidrawFile(ef.file)) {
|
||||
return false;
|
||||
}
|
||||
if(checking) return true;
|
||||
this.forceToOpenInMarkdownFilepath = ef.file?.path;
|
||||
const appState = view.excalidrawAPI.getAppState();
|
||||
const {x:centerX,y:centerY} = sceneCoordsToViewportCoords({sceneX:el.x+el.width/2,sceneY:el.y+el.height/2},appState);
|
||||
const {width, height} = {width:600, height:600};
|
||||
const {x,y} = {
|
||||
x:centerX - width/2 + view.ownerWindow.screenX,
|
||||
y:centerY - height/2 + view.ownerWindow.screenY,
|
||||
x:Math.max(0,centerX - width/2 + view.ownerWindow.screenX),
|
||||
y:Math.max(0,centerY - height/2 + view.ownerWindow.screenY),
|
||||
}
|
||||
|
||||
this.openDrawing(ef.file, DEVICE.isMobile ? "new-tab":"popout-window", true, undefined, false, {x,y,width,height});
|
||||
@@ -2555,21 +2553,20 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
if(!this.settings.startupScriptPath || this.settings.startupScriptPath === "") {
|
||||
return;
|
||||
}
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(self.runStartupScript, `ExcalidrawPlugin.runStartupScript > app.workspace.onLayoutReady, scriptPath:${self.settings?.startupScriptPath}`);
|
||||
const path = self.settings.startupScriptPath.endsWith(".md")
|
||||
? self.settings.startupScriptPath
|
||||
: `${self.settings.startupScriptPath}.md`;
|
||||
const f = self.app.vault.getAbstractFileByPath(path);
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.runStartupScript, `ExcalidrawPlugin.runStartupScript > app.workspace.onLayoutReady, scriptPath:${this.settings?.startupScriptPath}`);
|
||||
const path = this.settings.startupScriptPath.endsWith(".md")
|
||||
? this.settings.startupScriptPath
|
||||
: `${this.settings.startupScriptPath}.md`;
|
||||
const f = this.app.vault.getAbstractFileByPath(path);
|
||||
if (!f || !(f instanceof TFile)) {
|
||||
new Notice(`Startup script not found: ${path}`);
|
||||
return;
|
||||
}
|
||||
const script = await self.app.vault.read(f);
|
||||
const script = await this.app.vault.read(f);
|
||||
const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;
|
||||
try {
|
||||
await new AsyncFunction("ea", script)(self.ea);
|
||||
await new AsyncFunction("ea", script)(this.ea);
|
||||
} catch (e) {
|
||||
new Notice(`Error running startup script: ${e}`);
|
||||
}
|
||||
@@ -2619,7 +2616,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
if (previouslyActiveEV.leaf !== leaf) {
|
||||
//if loading new view to same leaf then don't save. Excalidarw view will take care of saving anyway.
|
||||
//avoid double saving
|
||||
if(previouslyActiveEV.semaphores?.dirty && !previouslyActiveEV.semaphores?.viewunload) {
|
||||
if(previouslyActiveEV?.isDirty() && !previouslyActiveEV.semaphores?.viewunload) {
|
||||
await previouslyActiveEV.save(true); //this will update transclusions in the drawing
|
||||
}
|
||||
}
|
||||
@@ -2817,7 +2814,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
await inData.loadData(data,file,getTextMode(data));
|
||||
excalidrawView.synchronizeWithData(inData);
|
||||
inData.destroy();
|
||||
if(excalidrawView.semaphores?.dirty) {
|
||||
if(excalidrawView?.isDirty()) {
|
||||
if(excalidrawView.autosaveTimer && excalidrawView.autosaveFunction) {
|
||||
clearTimeout(excalidrawView.autosaveTimer);
|
||||
}
|
||||
@@ -2874,16 +2871,6 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
};
|
||||
this.registerEvent(this.app.vault.on("delete", (file:TFile) => deleteEventHandler(file)));
|
||||
|
||||
//save open drawings when user quits the application
|
||||
//Removing because it is not guaranteed to run, and frequently gets terminated mid flight, causing file consistency issues
|
||||
/*const quitEventHandler = async () => {
|
||||
const leaves = app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
for (let i = 0; i < leaves.length; i++) {
|
||||
await (leaves[i].view as ExcalidrawView).save(true);
|
||||
}
|
||||
};
|
||||
self.registerEvent(app.workspace.on("quit", quitEventHandler));*/
|
||||
|
||||
//save Excalidraw leaf and update embeds when switching to another leaf
|
||||
this.registerEvent(
|
||||
this.app.workspace.on(
|
||||
@@ -2922,7 +2909,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
const onClickEventSaveActiveDrawing = (e: PointerEvent) => {
|
||||
if (
|
||||
!this.activeExcalidrawView ||
|
||||
!this.activeExcalidrawView.semaphores?.dirty ||
|
||||
!this.activeExcalidrawView?.isDirty() ||
|
||||
//@ts-ignore
|
||||
e.target && (e.target.className === "excalidraw__canvas" ||
|
||||
//@ts-ignore
|
||||
@@ -2941,7 +2928,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(onFileMenuEventSaveActiveDrawing,`ExcalidrawPlugin.onFileMenuEventSaveActiveDrawing`);
|
||||
if (
|
||||
!this.activeExcalidrawView ||
|
||||
!this.activeExcalidrawView.semaphores?.dirty
|
||||
!this.activeExcalidrawView?.isDirty()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -2965,7 +2952,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
if (
|
||||
m[0].oldValue !== "display: none;" ||
|
||||
!this.activeExcalidrawView ||
|
||||
!this.activeExcalidrawView.semaphores?.dirty
|
||||
!this.activeExcalidrawView?.isDirty()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -3012,7 +2999,8 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
(m[0].type !== "childList") ||
|
||||
(m[0].addedNodes.length !== 1) ||
|
||||
(!this.activeExcalidrawView) ||
|
||||
(!this.activeExcalidrawView.semaphores?.dirty)
|
||||
this.activeExcalidrawView?.semaphores?.viewunload ||
|
||||
(!this.activeExcalidrawView?.isDirty())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -64,9 +64,8 @@ export class EmbeddableMenu {
|
||||
};
|
||||
|
||||
private handleMouseLeave () {
|
||||
const self = this;
|
||||
this.menuFadeTimeout = window.setTimeout(() => {
|
||||
self.containerRef.current?.style.setProperty("opacity", "0.2");
|
||||
this.containerRef.current?.style.setProperty("opacity", "0.2");
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@ import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/
|
||||
import { EmbeddableMDCustomProps } from "src/dialogs/EmbeddableSettings";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export const insertImageToView = async (
|
||||
export async function insertImageToView(
|
||||
ea: ExcalidrawAutomate,
|
||||
position: { x: number, y: number },
|
||||
file: TFile | string,
|
||||
scale?: boolean,
|
||||
):Promise<string> => {
|
||||
):Promise<string> {
|
||||
ea.clear();
|
||||
ea.style.strokeColor = "transparent";
|
||||
ea.style.backgroundColor = "transparent";
|
||||
@@ -33,12 +33,12 @@ export const insertImageToView = async (
|
||||
return id;
|
||||
}
|
||||
|
||||
export const insertEmbeddableToView = async (
|
||||
export async function insertEmbeddableToView (
|
||||
ea: ExcalidrawAutomate,
|
||||
position: { x: number, y: number },
|
||||
file?: TFile,
|
||||
link?: string,
|
||||
):Promise<string> => {
|
||||
):Promise<string> {
|
||||
ea.clear();
|
||||
ea.style.strokeColor = "transparent";
|
||||
ea.style.backgroundColor = "transparent";
|
||||
@@ -58,7 +58,7 @@ export const insertEmbeddableToView = async (
|
||||
}
|
||||
}
|
||||
|
||||
export const getLinkTextFromLink = (text: string): string => {
|
||||
export function getLinkTextFromLink (text: string): string {
|
||||
if (!text) return;
|
||||
if (text.match(REG_LINKINDEX_HYPERLINK)) return;
|
||||
|
||||
@@ -71,7 +71,7 @@ export const getLinkTextFromLink = (text: string): string => {
|
||||
return linktext;
|
||||
}
|
||||
|
||||
export const openTagSearch = (link:string, app: App, view?: ExcalidrawView) => {
|
||||
export function openTagSearch (link:string, app: App, view?: ExcalidrawView) {
|
||||
const tags = link
|
||||
.matchAll(/#([\p{Letter}\p{Emoji_Presentation}\p{Number}\/_-]+)/gu)
|
||||
.next();
|
||||
@@ -92,21 +92,70 @@ export const openTagSearch = (link:string, app: App, view?: ExcalidrawView) => {
|
||||
return;
|
||||
}
|
||||
|
||||
export const openExternalLink = (link:string, app: App, element?: ExcalidrawElement):boolean => {
|
||||
function getLinkFromMarkdownLink(link: string): string {
|
||||
const result = /^\[[^\]]*]\(([^\)]*)\)/.exec(link);
|
||||
return result ? result[1] : link;
|
||||
}
|
||||
|
||||
export function openExternalLink (link:string, app: App, element?: ExcalidrawElement):boolean {
|
||||
link = getLinkFromMarkdownLink(link);
|
||||
if (link.match(/^cmd:\/\/.*/)) {
|
||||
const cmd = link.replace("cmd://", "");
|
||||
//@ts-ignore
|
||||
app.commands.executeCommandById(cmd);
|
||||
return true;
|
||||
}
|
||||
if (link.match(REG_LINKINDEX_HYPERLINK)) {
|
||||
window.open(link, "_blank");
|
||||
if (!link.startsWith("obsidian://") && link.match(REG_LINKINDEX_HYPERLINK)) {
|
||||
window.open(link, "_blank");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export const getExcalidrawFileForwardLinks = (app: App, excalidrawFile: TFile, secondOrderLinksSet: Set<string>):string => {
|
||||
/**
|
||||
*
|
||||
* @param link
|
||||
* @param app
|
||||
* @param returnWikiLink
|
||||
* @returns
|
||||
* false if the link is not an obsidian link,
|
||||
* true if the link is an obsidian link and it was opened (i.e. it is a link to another Vault or not a file link e.g. plugin link), or
|
||||
* the link to the file path. By default as a wiki link, or as a file path if returnWikiLink is false.
|
||||
*/
|
||||
export function parseObsidianLink(link: string, app: App, returnWikiLink: boolean = true): boolean | string {
|
||||
link = getLinkFromMarkdownLink(link);
|
||||
if (!link.startsWith("obsidian://")) {
|
||||
return false;
|
||||
}
|
||||
const url = new URL(link);
|
||||
const action = url.pathname.slice(2); // Remove leading '//'
|
||||
|
||||
const props: {[key: string]: string} = {};
|
||||
url.searchParams.forEach((value, key) => {
|
||||
props[key] = decodeURIComponent(value);
|
||||
});
|
||||
|
||||
if (action === "open" && props.vault === app.vault.getName()) {
|
||||
const file = props.file;
|
||||
const f = app.metadataCache.getFirstLinkpathDest(file, "");
|
||||
if (f && f instanceof TFile) {
|
||||
if (returnWikiLink) {
|
||||
return `[[${f.path}]]`;
|
||||
} else {
|
||||
return f.path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.open(link, "_blank");
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getExcalidrawFileForwardLinks (
|
||||
app: App, excalidrawFile: TFile,
|
||||
secondOrderLinksSet: Set<string>,
|
||||
):string {
|
||||
let secondOrderLinks = "";
|
||||
const forwardLinks = app.metadataCache.getLinks()[excalidrawFile.path];
|
||||
if(forwardLinks && forwardLinks.length > 0) {
|
||||
@@ -125,7 +174,10 @@ export const getExcalidrawFileForwardLinks = (app: App, excalidrawFile: TFile, s
|
||||
return secondOrderLinks;
|
||||
}
|
||||
|
||||
export const getFrameBasedOnFrameNameOrId = (frameName: string, elements: ExcalidrawElement[]): ExcalidrawFrameElement | null => {
|
||||
export function getFrameBasedOnFrameNameOrId(
|
||||
frameName: string,
|
||||
elements: ExcalidrawElement[],
|
||||
): ExcalidrawFrameElement | null {
|
||||
const frames = elements
|
||||
.filter((el: ExcalidrawElement)=>el.type==="frame")
|
||||
.map((el: ExcalidrawFrameElement, idx: number)=>{
|
||||
@@ -136,7 +188,13 @@ export const getFrameBasedOnFrameNameOrId = (frameName: string, elements: Excali
|
||||
return frames.length === 1 ? frames[0] : null;
|
||||
}
|
||||
|
||||
export const addBackOfTheNoteCard = async (view: ExcalidrawView, title: string, activate: boolean = true, cardBody?: string, embeddableCustomData?: EmbeddableMDCustomProps):Promise<string> => {
|
||||
export async function addBackOfTheNoteCard(
|
||||
view: ExcalidrawView,
|
||||
title: string,
|
||||
activate: boolean = true,
|
||||
cardBody?: string,
|
||||
embeddableCustomData?: EmbeddableMDCustomProps,
|
||||
):Promise<string> {
|
||||
const data = view.data;
|
||||
const header = getExcalidrawMarkdownHeaderSection(data);
|
||||
const body = data.split(header)[1];
|
||||
@@ -186,7 +244,12 @@ export const addBackOfTheNoteCard = async (view: ExcalidrawView, title: string,
|
||||
return el.id;
|
||||
}
|
||||
|
||||
export const renderContextMenuAction = (React: any, label: string, action: Function, onClose: (callback?: () => void) => void) => {
|
||||
export function renderContextMenuAction(
|
||||
React: any,
|
||||
label: string,
|
||||
action: Function,
|
||||
onClose: (callback?: () => void) => void,
|
||||
) {
|
||||
return React.createElement (
|
||||
"li",
|
||||
{
|
||||
@@ -218,7 +281,7 @@ export const renderContextMenuAction = (React: any, label: string, action: Funct
|
||||
);
|
||||
}
|
||||
|
||||
export const tmpBruteForceCleanup = (view: ExcalidrawView) => {
|
||||
export function tmpBruteForceCleanup (view: ExcalidrawView) {
|
||||
window.setTimeout(()=>{
|
||||
if(!view) return;
|
||||
// const cleanupHTMLElement = (el: Element) => {
|
||||
|
||||
@@ -37,23 +37,22 @@ export class StylesManager {
|
||||
|
||||
constructor(plugin: ExcalidrawPlugin) {
|
||||
this.plugin = plugin;
|
||||
const self = this;
|
||||
plugin.app.workspace.onLayoutReady(async () => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(undefined, "StylesManager.constructor > app.workspace.onLayoutReady", self);
|
||||
await self.harvestStyles();
|
||||
getAllWindowDocuments(plugin.app).forEach(doc => self.copyPropertiesToTheme(doc));
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(undefined, "StylesManager.constructor > app.workspace.onLayoutReady", this);
|
||||
await this.harvestStyles();
|
||||
getAllWindowDocuments(plugin.app).forEach(doc => this.copyPropertiesToTheme(doc));
|
||||
|
||||
//initialize
|
||||
plugin.registerEvent(
|
||||
plugin.app.workspace.on("css-change", ()=>self.onCSSChange()),
|
||||
plugin.app.workspace.on("css-change", ()=>this.onCSSChange()),
|
||||
)
|
||||
|
||||
plugin.registerEvent(
|
||||
plugin.app.workspace.on("window-open", (win)=>self.onWindowOpen(win)),
|
||||
plugin.app.workspace.on("window-open", (win)=>this.onWindowOpen(win)),
|
||||
)
|
||||
|
||||
plugin.registerEvent(
|
||||
plugin.app.workspace.on("window-close", (win)=>self.onWindowClose(win)),
|
||||
plugin.app.workspace.on("window-close", (win)=>this.onWindowClose(win)),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user