From 83aa04396c02e3b0e796a5dc40eb417b9dbf9653 Mon Sep 17 00:00:00 2001 From: zsviczian Date: Wed, 2 Aug 2023 22:18:43 +0200 Subject: [PATCH] Support Templater scripts in Embeddable Markdown Documents --- package.json | 2 +- src/ExcalidrawAutomate.ts | 9 ++++--- src/ExcalidrawView.ts | 40 +++++++++++++++++++++++++++++- src/constants.ts | 6 ++--- src/customEmbeddable.tsx | 1 + src/main.ts | 26 ++++++++++++++++++- src/menu/EmbeddableActionsMenu.tsx | 1 + src/utils/CanvasNodeFactory.ts | 2 ++ src/utils/Utils.ts | 10 ++++++++ 9 files changed, 87 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index af4dae5..ddff5db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-excalidraw-plugin", - "version": "1.9.9-2", + "version": "1.9.13", "description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts index 3c901db..4453989 100644 --- a/src/ExcalidrawAutomate.ts +++ b/src/ExcalidrawAutomate.ts @@ -11,7 +11,7 @@ import { StrokeRoundness, RoundnessType, } from "@zsviczian/excalidraw/types/element/types"; -import { normalizePath, Notice, TFile, WorkspaceLeaf } from "obsidian"; +import { normalizePath, Notice, OpenViewState, TFile, WorkspaceLeaf } from "obsidian"; import * as obsidian_module from "obsidian"; import ExcalidrawView, { ExportSettings, TextMode } from "src/ExcalidrawView"; import { ExcalidrawData, getMarkdownDrawingSection } from "src/ExcalidrawData"; @@ -2010,10 +2010,11 @@ export class ExcalidrawAutomate { /** * Open a file in a new workspaceleaf or reuse an existing adjacent leaf depending on Excalidraw Plugin Settings - * @param file + * @param file + * @param openState - if not provided {active: true} will be used * @returns */ - openFileInNewOrAdjacentLeaf(file: TFile): WorkspaceLeaf { + openFileInNewOrAdjacentLeaf(file: TFile, openState?: OpenViewState): WorkspaceLeaf { if (!file || !(file instanceof TFile)) { return null; } @@ -2021,7 +2022,7 @@ export class ExcalidrawAutomate { return null; } const leaf = getNewOrAdjacentLeaf(this.plugin, this.targetView.leaf); - leaf.openFile(file, {active: true}); + leaf.openFile(file, openState ?? {active: true}); return leaf; }; diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts index 254ae09..60bf144 100644 --- a/src/ExcalidrawView.ts +++ b/src/ExcalidrawView.ts @@ -121,7 +121,7 @@ import { InsertPDFModal } from "./dialogs/InsertPDFModal"; import { CustomEmbeddable, renderWebView } from "./customEmbeddable"; import { insertEmbeddableToView, insertImageToView } from "./utils/ExcalidrawViewUtils"; import { imageCache } from "./utils/ImageCache"; -import { CanvasNodeFactory } from "./utils/CanvasNodeFactory"; +import { CanvasNodeFactory, ObsidianCanvasNode } from "./utils/CanvasNodeFactory"; import { EmbeddableMenu } from "./menu/EmbeddableActionsMenu"; import { useDefaultExcalidrawFrame } from "./utils/CustomEmbeddableUtils"; import { UniversalInsertFileModal } from "./dialogs/UniversalInsertFileModal"; @@ -255,6 +255,7 @@ export default class ExcalidrawView extends TextFileView { private draginfoDiv: HTMLDivElement; public canvasNodeFactory: CanvasNodeFactory; private embeddableRefs = new Map(); + private embeddableLeafRefs = new Map(); public semaphores: { popoutUnload: boolean; //the unloaded Excalidraw view was the last leaf in the popout window @@ -1626,6 +1627,7 @@ export default class ExcalidrawView extends TextFileView { clear() { this.canvasNodeFactory.purgeNodes(); this.embeddableRefs.clear(); + this.embeddableLeafRefs.clear(); delete this.exportDialog; const api = this.excalidrawAPI; @@ -4467,6 +4469,42 @@ export default class ExcalidrawView extends TextFileView { public getEmbeddableElementById(id: string): HTMLIFrameElement | HTMLWebViewElement | undefined { return this.embeddableRefs.get(id); } + + public updateEmbeddableLeafRef(id: string, ref: any) { + if(ref) { + this.embeddableLeafRefs.set(id, ref); + } + } + + public getEmbeddableLeafElementById(id: string): {leaf: WorkspaceLeaf; node?: ObsidianCanvasNode} | null { + const ref = this.embeddableLeafRefs.get(id); + if(!ref) { + return null; + } + return ref as {leaf: WorkspaceLeaf; node?: ObsidianCanvasNode}; + } + + getActiveEmbeddable = ():{leaf: WorkspaceLeaf; node?: ObsidianCanvasNode}|null => { + if(!this.excalidrawAPI) return null; + const api = this.excalidrawAPI as ExcalidrawImperativeAPI; + const st = api.getAppState(); + if(!st.activeEmbeddable || st.activeEmbeddable.state !== "active" ) return null; + return this.getEmbeddableLeafElementById(st.activeEmbeddable?.element?.id); + } + + get editor(): any { + const embeddable = this.getActiveEmbeddable(); + if(embeddable) { + if(embeddable.node && embeddable.node.isEditing) { + return embeddable.node.child.editor; + } + if(embeddable.leaf?.view instanceof MarkdownView) { + return embeddable.leaf.view.editor; + } + } + app.workspace.openLinkText + return null; + } } export function getTextMode(data: string): TextMode { diff --git a/src/constants.ts b/src/constants.ts index d0f97de..0a249e5 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -73,9 +73,9 @@ export const SCRIPT_INSTALL_CODEBLOCK = "excalidraw-script-install"; export const SCRIPT_INSTALL_FOLDER = "Downloaded"; export const fileid = customAlphabet("1234567890abcdef", 40); export const REG_LINKINDEX_INVALIDCHARS = /[<>:"\\|?*#]/g; -export const REG_BLOCK_REF_CLEAN = - /[!"#$%&()*+,.:;<=>?@^`{|}~\/\[\]\\]/g; //https://discord.com/channels/686053708261228577/989603365606531104/1000128926619816048 - // /\+|\/|~|=|%|\(|\)|{|}|,|&|\.|\$|!|\?|;|\[|]|\^|#|\*|<|>|&|@|\||\\|"|:|\s/g; +export const REG_BLOCK_REF_CLEAN = /[!"#$%&()*+,.:;<=>?@^`{|}~\/\[\]\\]/g; +// https://discord.com/channels/686053708261228577/989603365606531104/1000128926619816048 +// /\+|\/|~|=|%|\(|\)|{|}|,|&|\.|\$|!|\?|;|\[|]|\^|#|\*|<|>|&|@|\||\\|"|:|\s/g; export const IMAGE_TYPES = ["jpeg", "jpg", "png", "gif", "svg", "webp", "bmp", "ico"]; export const EXPORT_TYPES = ["svg", "dark.svg", "light.svg", "png", "dark.png", "light.png"]; export const MAX_IMAGE_SIZE = 500; diff --git a/src/customEmbeddable.tsx b/src/customEmbeddable.tsx index 8db8ca8..3735a66 100644 --- a/src/customEmbeddable.tsx +++ b/src/customEmbeddable.tsx @@ -195,6 +195,7 @@ function RenderObsidianView( containerRef.current.appendChild(rootSplit.containerEl); } patchMobileView(view); + view.updateEmbeddableLeafRef(element.id, leafRef.current); })(); } diff --git a/src/main.ts b/src/main.ts index 8722b75..729e515 100644 --- a/src/main.ts +++ b/src/main.ts @@ -17,6 +17,7 @@ import { MetadataCache, FrontMatterCache, Command, + Workspace, } from "obsidian"; import { BLANK_DRAWING, @@ -63,7 +64,7 @@ import { search, } from "./ExcalidrawAutomate"; import { Prompt } from "./dialogs/Prompt"; -import { around } from "monkey-around"; +import { around, dedupe } from "monkey-around"; import { t } from "./lang/helpers"; import { checkAndCreateFolder, @@ -82,6 +83,7 @@ import { debug, isVersionNewerThanOther, getExportTheme, + isCallerFromTemplaterPlugin, } from "./utils/Utils"; import { getAttachmentsFolderAndFilePath, getNewOrAdjacentLeaf, getParentOfClass, isObsidianThemeDark } from "./utils/ObsidianUtils"; //import { OneOffs } from "./OneOffs"; @@ -1578,6 +1580,28 @@ export default class ExcalidrawPlugin extends Plugin { } private registerMonkeyPatches() { + const key = "https://github.com/zsviczian/obsidian-excalidraw-plugin/issues"; + this.register( + around(Workspace.prototype, { + getActiveViewOfType(old) { + return dedupe(key, old, function(...args) { + const result = old && old.apply(this, args); + const maybeEAView = app?.workspace?.activeLeaf?.view; + if(!maybeEAView || !(maybeEAView instanceof ExcalidrawView)) return result; + const error = new Error(); + const stackTrace = error.stack; + if(!isCallerFromTemplaterPlugin(stackTrace)) return result; + const leafOrNode = maybeEAView.getActiveEmbeddable(); + if(leafOrNode) { + if(leafOrNode.node && leafOrNode.node.isEditing) { + return {file: leafOrNode.node.file, editor: leafOrNode.node.child.editor}; + } + } + return result; + }); + } + }) + ); //@ts-ignore if(!app.plugins?.plugins?.["obsidian-hover-editor"]) { this.register( //stolen from hover editor diff --git a/src/menu/EmbeddableActionsMenu.tsx b/src/menu/EmbeddableActionsMenu.tsx index 0415a1b..6d82df7 100644 --- a/src/menu/EmbeddableActionsMenu.tsx +++ b/src/menu/EmbeddableActionsMenu.tsx @@ -53,6 +53,7 @@ export class EmbeddableMenu { const view = this.view; const api = view?.excalidrawAPI as ExcalidrawImperativeAPI; if(!api) return null; + if(!view.file) return null; const disableFrameButtons = appState.viewModeEnabled && !view.allowFrameButtonsInViewMode; if(!appState.activeEmbeddable || appState.activeEmbeddable.state !== "active" || disableFrameButtons) { this.menuElementId = null; diff --git a/src/utils/CanvasNodeFactory.ts b/src/utils/CanvasNodeFactory.ts index cdaafec..e7a6d54 100644 --- a/src/utils/CanvasNodeFactory.ts +++ b/src/utils/CanvasNodeFactory.ts @@ -30,6 +30,8 @@ interface ObsidianCanvas { export interface ObsidianCanvasNode { startEditing: Function; child: any; + isEditing: boolean; + file: TFile; } export class CanvasNodeFactory { diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index cc76bfc..3eb0e4d 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -725,4 +725,14 @@ export const getYouTubeThumbnailLink = async (youtubelink: string):Promise { + const lines = stackTrace.split("\n"); + for (const line of lines) { + if (line.trim().startsWith("at Templater.")) { + return true; + } + } + return false; } \ No newline at end of file