diff --git a/src/EmbeddedFileLoader.ts b/src/EmbeddedFileLoader.ts index dee6370..a639946 100644 --- a/src/EmbeddedFileLoader.ts +++ b/src/EmbeddedFileLoader.ts @@ -39,12 +39,13 @@ import { svgToBase64, isMaskFile, embedFontsInSVG, + getEmbeddedFilenameParts, } from "./utils/Utils"; import { ValueOf } from "./types"; import { getMermaidImageElements, getMermaidText, shouldRenderMermaid } from "./utils/MermaidUtils"; import { mermaidToExcalidraw } from "src/constants/constants"; import { ImageKey, imageCache } from "./utils/ImageCache"; -import { PreviewImageType } from "./utils/UtilTypes"; +import { FILENAMEPARTS, PreviewImageType } from "./utils/UtilTypes"; //An ugly workaround for the following situation. //File A is a markdown file that has an embedded Excalidraw file B @@ -145,8 +146,6 @@ const replaceSVGColors = (svg: SVGSVGElement | string, colorMap: ColorMap | null return svg; } - - export class EmbeddedFile { public file: TFile = null; public isSVGwithBitmap: boolean = false; @@ -157,6 +156,7 @@ export class EmbeddedFile { public mimeType: MimeType = "application/octet-stream"; public size: Size = { height: 0, width: 0 }; public linkParts: LinkParts; + public filenameparts: FILENAMEPARTS private hostPath: string; public attemptCounter: number = 0; public isHyperLink: boolean = false; @@ -204,7 +204,7 @@ export class EmbeddedFile { if (!this.linkParts.height) { this.linkParts.height = this.plugin.settings.mdSVGmaxHeight; } - this.file = app.metadataCache.getFirstLinkpathDest( + this.file = this.plugin.app.metadataCache.getFirstLinkpathDest( this.linkParts.path, hostPath, ); @@ -215,6 +215,9 @@ export class EmbeddedFile { 5000, ); } + } else { + this.filenameparts = getEmbeddedFilenameParts(imgPath); + this.filenameparts.filepath = this.file.path; } } @@ -364,22 +367,26 @@ export class EmbeddedFilesLoader { const hasColorMap = Boolean(inFile instanceof EmbeddedFile ? inFile.colorMap : null); const shouldUseCache = !hasColorMap && this.plugin.settings.allowImageCacheInScene && file && imageCache.isReady(); + const hasFilenameParts = Boolean((inFile instanceof EmbeddedFile) && inFile.filenameparts); + const filenameParts = hasFilenameParts ? (inFile as EmbeddedFile).filenameparts : null; const cacheKey:ImageKey = { - filepath: file.path, - blockref: null, - sectionref: null, + ...hasFilenameParts? filenameParts : { + filepath: file.path, + hasBlockref: false, + hasGroupref: false, + hasTaskbone: false, + hasArearef: false, + hasFrameref: false, + hasSectionref: false, + blockref: null, + sectionref: null, + linkpartReference: null, + linkpartAlias: null, + }, isDark, previewImageType: PreviewImageType.SVG, scale: 1, isTransparent: !exportSettings.withBackground, - hasBlockref: false, - hasGroupref: false, - hasTaskbone: false, - hasArearef: false, - hasFrameref: false, - hasSectionref: false, - linkpartReference: null, - linkpartAlias: null, } const maybeSVG = shouldUseCache @@ -390,7 +397,12 @@ export class EmbeddedFilesLoader { ? maybeSVG : replaceSVGColors( await createSVG( - file?.path, + hasFilenameParts + ? (filenameParts.hasGroupref || filenameParts.hasBlockref || + filenameParts.hasSectionref || filenameParts.hasFrameref + ? filenameParts.filepath + filenameParts.linkpartReference + : file.path) + : file?.path, false, //false exportSettings, this, diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts index 450f232..69ee064 100644 --- a/src/ExcalidrawView.ts +++ b/src/ExcalidrawView.ts @@ -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, parseObsidianLink, renderContextMenuAction, tmpBruteForceCleanup } from "./utils/ExcalidrawViewUtils"; +import { addBackOfTheNoteCard, getExcalidrawFileForwardLinks, getFrameBasedOnFrameNameOrId, getLinkTextFromLink, insertEmbeddableToView, insertImageToView, isTextImageTransclusion, openExternalLink, openTagSearch, parseObsidianLink, renderContextMenuAction, tmpBruteForceCleanup } from "./utils/ExcalidrawViewUtils"; import { imageCache } from "./utils/ImageCache"; import { CanvasNodeFactory, ObsidianCanvasNode } from "./utils/CanvasNodeFactory"; import { EmbeddableMenu } from "./menu/EmbeddableActionsMenu"; @@ -3731,6 +3731,23 @@ export default class ExcalidrawView extends TextFileView { return false; } + if(isTextImageTransclusion(data.text,this, async (link, file)=>{ + const ea = getEA(this) as ExcalidrawAutomate; + if(IMAGE_TYPES.contains(file.extension)) { + ea.selectElementsInView([await insertImageToView (ea, this.currentPosition, file)]); + ea.destroy(); + } else if(file.extension !== "pdf") { + ea.selectElementsInView([await insertEmbeddableToView (ea, this.currentPosition, file, link)]); + ea.destroy(); + } else { + const modal = new UniversalInsertFileModal(this.plugin, this); + modal.open(file, this.currentPosition); + } + this.setDirty(9); + })) { + return false; + } + const quoteWithRef = obsidianPDFQuoteWithRef(data.text); if(quoteWithRef) { const ea = getEA(this) as ExcalidrawAutomate; @@ -4308,42 +4325,33 @@ export default class ExcalidrawView extends TextFileView { // If the link is an image or a PDF file, replace the text element with the image or the PDF. // If the link is an embedded markdown file, then display a message, but otherwise transclude the text step 5. // 1 2 - const REG_TRANSCLUSION = /^!\[\[([^|\]]*)?.*?]]$|^!\[[^\]]*?]\((.*?)\)$/g; - const match = nextOriginalText.trim().matchAll(REG_TRANSCLUSION).next(); //reset the iterator - if(match?.value?.[0]) { - const link = match.value[1] ?? match.value[2]; - const file = this.app.metadataCache.getFirstLinkpathDest(link, this.file.path); - if(file && file instanceof TFile) { - if (file.extension !== "md" || this.plugin.isExcalidrawFile(file)) { - window.setTimeout(async ()=>{ - const elements = this.excalidrawAPI.getSceneElements(); - const el = elements.filter((el:ExcalidrawElement)=>el.id === textElement.id) as ExcalidrawTextElement[]; - if(el.length === 1) { - const center = {x: el[0].x, y: el[0].y }; - const clone = cloneElement(el[0]); - clone.isDeleted = true; - this.excalidrawData.deleteTextElement(clone.id); - elements[elements.indexOf(el[0])] = clone; - this.updateScene({elements}); - const ea:ExcalidrawAutomate = getEA(this); - if(IMAGE_TYPES.contains(file.extension)) { - ea.selectElementsInView([await insertImageToView (ea, center, file)]); - ea.destroy(); - } else if(file.extension !== "pdf") { - ea.selectElementsInView([await insertEmbeddableToView (ea, center, file)]); - ea.destroy(); - } else { - const modal = new UniversalInsertFileModal(this.plugin, this); - modal.open(file, center); - } - this.setDirty(9); - } - }); - return {updatedNextOriginalText: null, nextLink: textElement.link}; - } else { - new Notice(t("USE_INSERT_FILE_MODAL"),5000); + if(isTextImageTransclusion(nextOriginalText, this, (link, file)=>{ + window.setTimeout(async ()=>{ + const elements = this.excalidrawAPI.getSceneElements(); + const el = elements.filter((el:ExcalidrawElement)=>el.id === textElement.id) as ExcalidrawTextElement[]; + if(el.length === 1) { + const center = {x: el[0].x, y: el[0].y }; + const clone = cloneElement(el[0]); + clone.isDeleted = true; + this.excalidrawData.deleteTextElement(clone.id); + elements[elements.indexOf(el[0])] = clone; + this.updateScene({elements}); + const ea:ExcalidrawAutomate = getEA(this); + if(IMAGE_TYPES.contains(file.extension)) { + ea.selectElementsInView([await insertImageToView (ea, center, file)]); + ea.destroy(); + } else if(file.extension !== "pdf") { + ea.selectElementsInView([await insertEmbeddableToView (ea, center, file, link)]); + ea.destroy(); + } else { + const modal = new UniversalInsertFileModal(this.plugin, this); + modal.open(file, center); + } + this.setDirty(9); } - } + }); + })) { + return {updatedNextOriginalText: null, nextLink: textElement.link}; } // 5. Check if the user made changes to the text, or diff --git a/src/utils/ExcalidrawViewUtils.ts b/src/utils/ExcalidrawViewUtils.ts index 9e5ded5..3897549 100644 --- a/src/utils/ExcalidrawViewUtils.ts +++ b/src/utils/ExcalidrawViewUtils.ts @@ -1,6 +1,6 @@ import { MAX_IMAGE_SIZE, IMAGE_TYPES, ANIMATED_IMAGE_TYPES, MD_EX_SECTIONS } from "src/constants/constants"; -import { App, TFile, WorkspaceLeaf } from "obsidian"; +import { App, Notice, TFile, WorkspaceLeaf } from "obsidian"; import { ExcalidrawAutomate } from "src/ExcalidrawAutomate"; import { REGEX_LINK, REG_LINKINDEX_HYPERLINK, getExcalidrawMarkdownHeaderSection } from "src/ExcalidrawData"; import ExcalidrawView from "src/ExcalidrawView"; @@ -11,6 +11,7 @@ import { getEA } from "src"; import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types"; import { EmbeddableMDCustomProps } from "src/dialogs/EmbeddableSettings"; import { nanoid } from "nanoid"; +import { t } from "src/lang/helpers"; export async function insertImageToView( ea: ExcalidrawAutomate, @@ -43,7 +44,7 @@ export async function insertEmbeddableToView ( ea.style.strokeColor = "transparent"; ea.style.backgroundColor = "transparent"; if(file && (IMAGE_TYPES.contains(file.extension) || ea.isExcalidrawFile(file)) && !ANIMATED_IMAGE_TYPES.contains(file.extension)) { - return await insertImageToView(ea, position, file); + return await insertImageToView(ea, position, link??file); } else { const id = ea.addEmbeddable( position.x, @@ -345,4 +346,34 @@ export function tmpBruteForceCleanup (view: ExcalidrawView) { delete view[key]; }); }, 500); +} + +/** +* Check if the text matches the transclusion pattern and if so, + * check if the link in the transclusion can be resolved to a file in the vault. + * if yes, call the callback function with the link and the file. + * @param text + * @param callback + * @returns true if text is a transclusion and the link can be resolved to a file in the vault, false otherwise. + */ +export function isTextImageTransclusion ( + text: string, + view: ExcalidrawView, + callback: (link: string, file: TFile)=>void +): boolean { + const REG_TRANSCLUSION = /^!\[\[([^|\]]*)?.*?]]$|^!\[[^\]]*?]\((.*?)\)$/g; + const match = text.trim().matchAll(REG_TRANSCLUSION).next(); //reset the iterator + if(match?.value?.[0]) { + const link = match.value[1] ?? match.value[2]; + const file = view.app.metadataCache.getFirstLinkpathDest(link?.split("#")[0], view.file.path); + if(file && file instanceof TFile) { + if (file.extension !== "md" || view.plugin.isExcalidrawFile(file)) { + callback(link, file); + return true; + } else { + new Notice(t("USE_INSERT_FILE_MODAL"),5000); + } + } + } + return false; } \ No newline at end of file diff --git a/src/utils/UtilTypes.ts b/src/utils/UtilTypes.ts index 40027c9..323523d 100644 --- a/src/utils/UtilTypes.ts +++ b/src/utils/UtilTypes.ts @@ -5,8 +5,8 @@ export type FILENAMEPARTS = { hasTaskbone: boolean, hasArearef: boolean, hasFrameref: boolean, - blockref: string, hasSectionref: boolean, + blockref: string, sectionref: string, linkpartReference: string, linkpartAlias: string