From 334e8130ebf6b0af89bfeeb1622d5783a88cd9af Mon Sep 17 00:00:00 2001 From: Zsolt Viczian Date: Fri, 19 Aug 2022 21:01:11 +0200 Subject: [PATCH] 1.4.13 --- src/EmbeddedFileLoader.ts | 7 +- src/ExcalidrawAutomate.ts | 68 +++++++++- src/ExcalidrawView.ts | 110 +++++++++------- src/MarkdownPostProcessor.ts | 238 ++++++++++++++++------------------- src/Scripts.ts | 23 ++-- src/dialogs/OpenDrawing.ts | 2 +- src/dialogs/Prompt.ts | 3 + src/lang/locale/en.ts | 7 +- src/main.ts | 146 +++++++++++---------- src/menu/ToolsPanel.tsx | 6 +- src/settings.ts | 2 +- src/utils/FileUtils.ts | 3 +- src/utils/ObsidianUtils.ts | 2 +- src/utils/Utils.ts | 11 +- styles.css | 6 +- 15 files changed, 351 insertions(+), 283 deletions(-) diff --git a/src/EmbeddedFileLoader.ts b/src/EmbeddedFileLoader.ts index 62e1a65..bfa6f59 100644 --- a/src/EmbeddedFileLoader.ts +++ b/src/EmbeddedFileLoader.ts @@ -92,7 +92,7 @@ export class EmbeddedFile { if (!this.linkParts.height) { this.linkParts.height = this.plugin.settings.mdSVGmaxHeight; } - this.file = this.plugin.app.metadataCache.getFirstLinkpathDest( + this.file = app.metadataCache.getFirstLinkpathDest( this.linkParts.path, hostPath, ); @@ -108,7 +108,7 @@ export class EmbeddedFile { private fileChanged(): boolean { if (!this.file) { - this.file = this.plugin.app.metadataCache.getFirstLinkpathDest( + this.file = app.metadataCache.getFirstLinkpathDest( this.linkParts.path, this.hostPath, ); // maybe the file has synchronized in the mean time @@ -149,7 +149,7 @@ export class EmbeddedFile { public isLoaded(isDark: boolean): boolean { if (!this.file) { - this.file = this.plugin.app.metadataCache.getFirstLinkpathDest( + this.file = app.metadataCache.getFirstLinkpathDest( this.linkParts.path, this.hostPath, ); // maybe the file has synchronized in the mean time @@ -218,7 +218,6 @@ export class EmbeddedFilesLoader { }; let hasSVGwithBitmap = false; - const app = this.plugin.app; const isExcalidrawFile = this.plugin.isExcalidrawFile(file); if ( !( diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts index 5eac1da..5c88ca3 100644 --- a/src/ExcalidrawAutomate.ts +++ b/src/ExcalidrawAutomate.ts @@ -1141,7 +1141,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { */ setView(view: ExcalidrawView | "first" | "active"): ExcalidrawView { if (view == "active") { - const v = this.plugin.app.workspace.getActiveViewOfType(ExcalidrawView); + const v = app.workspace.getActiveViewOfType(ExcalidrawView); if (!(v instanceof ExcalidrawView)) { return; } @@ -1149,7 +1149,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { } if (view == "first") { const leaves = - this.plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); + app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); if (!leaves || leaves.length == 0) { return; } @@ -1283,7 +1283,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { * @returns */ viewToggleFullScreen(forceViewMode: boolean = false): void { - if (this.plugin.app.isMobile) { + if (app.isMobile) { errorMessage("mobile not supported", "viewToggleFullScreen()"); return; } @@ -1618,7 +1618,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { return null; } const leaf = getNewOrAdjacentLeaf(this.plugin, this.targetView.leaf); - leaf.openFile(file, {active: false}); + leaf.openFile(file, {active: true}); return leaf; }; @@ -2008,7 +2008,9 @@ async function getTemplate( let groupElements:ExcalidrawElement[] = scene.elements; if(filenameParts.hasGroupref) { - const el = scene.elements.filter((el: ExcalidrawElement) => el.id === filenameParts.blockref); + const el = filenameParts.hasSectionref + ? getTextElementsMatchingQuery(scene.elements,["# "+filenameParts.sectionref],true) + : scene.elements.filter((el: ExcalidrawElement)=>el.id===filenameParts.blockref); if(el.length > 0) { groupElements = plugin.ea.getElementsInTheSameGroupWithElement(el[0],scene.elements) } @@ -2094,6 +2096,7 @@ export async function createSVG( : null; let elements = template?.elements ?? []; elements = elements.concat(automateElements); + padding = padding ?? plugin.settings.exportPaddingSVG; const svg = await getSVG( { //createAndOpenDrawing @@ -2113,8 +2116,29 @@ export async function createSVG( exportSettings?.withBackground ?? plugin.settings.exportWithBackground, withTheme: exportSettings?.withTheme ?? plugin.settings.exportWithTheme, }, - padding ?? plugin.settings.exportPaddingSVG, + padding, ); + const filenameParts = getEmbeddedFilenameParts(templatePath); + if( + !filenameParts.hasGroupref && + (filenameParts.hasBlockref || filenameParts.hasSectionref) + ) { + let el = filenameParts.hasSectionref + ? getTextElementsMatchingQuery(elements,["# "+filenameParts.sectionref],true) + : elements.filter((el: ExcalidrawElement)=>el.id===filenameParts.blockref); + if(el.length>0) { + const containerId = el[0].containerId; + if(containerId) { + el = el.concat(elements.filter((el: ExcalidrawElement)=>el.id === containerId)); + } + const elBB = plugin.ea.getBoundingBox(el); + const drawingBB = plugin.ea.getBoundingBox(elements); + svg.viewBox.baseVal.x = elBB.topX - drawingBB.topX; + svg.viewBox.baseVal.y = elBB.topY - drawingBB.topY; + svg.viewBox.baseVal.width = elBB.width + 2*padding; + svg.viewBox.baseVal.height = elBB.height + 2*padding; + } + } if (template?.hasSVGwithBitmap) { svg.setAttribute("hasbitmap", "true"); } @@ -2242,3 +2266,35 @@ export const search = async (view: ExcalidrawView) => { ea.targetView.selectElementsMatchingQuery(elements, query); }; + +/** + * + * @param elements + * @param query + * @param exactMatch - when searching for section header exactMatch should be set to true + * @returns the elements matching the query + */ +export const getTextElementsMatchingQuery = ( + elements: ExcalidrawElement[], + query: string[], + exactMatch: boolean = false, //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530 +): ExcalidrawElement[] => { + if (!elements || elements.length === 0 || !query || query.length === 0) { + return []; + } + + return elements.filter((el: any) => + el.type === "text" && + query.some((q) => { + if (exactMatch) { + const text = el.rawText.toLowerCase().split("\n")[0].trim(); + const m = text.match(/^#*(# .*)/); + if (!m || m.length !== 2) { + return false; + } + return m[1] === q.toLowerCase(); + } + const text = el.rawText.toLowerCase().replaceAll("\n", " ").trim(); + return text.match(q.toLowerCase()); //to distinguish between "# frame" and "# frame 1" https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530 + })); +} diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts index 0d262a1..227df4a 100644 --- a/src/ExcalidrawView.ts +++ b/src/ExcalidrawView.ts @@ -9,6 +9,7 @@ import { MarkdownView, request, Platform, + requireApiVersion, } from "obsidian"; //import * as React from "react"; //import * as ReactDOM from "react-dom"; @@ -42,7 +43,7 @@ import { LOCAL_PROTOCOL, } from "./Constants"; import ExcalidrawPlugin from "./main"; -import { repositionElementsToCursor, ExcalidrawAutomate } from "./ExcalidrawAutomate"; +import { repositionElementsToCursor, ExcalidrawAutomate, getTextElementsMatchingQuery } from "./ExcalidrawAutomate"; import { t } from "./lang/helpers"; import { ExcalidrawData, @@ -57,10 +58,12 @@ import { getNewUniqueFilepath, } from "./utils/FileUtils"; import { + awaitNextAnimationFrame, checkExcalidrawVersion, debug, embedFontsInSVG, errorlog, + getEmbeddedFilenameParts, getExportTheme, getLinkParts, getPNG, @@ -69,6 +72,7 @@ import { getSVGPadding, getWithBackground, hasExportTheme, + isVersionNewerThanOther, scaleLoadedImage, svgToBase64, viewportCoordsToSceneCoords, @@ -89,6 +93,8 @@ import { ToolsPanel } from "./menu/ToolsPanel"; import { ScriptEngine } from "./Scripts"; import { getTextElementAtPointer, getImageElementAtPointer, getElementWithLinkAtPointer } from "./utils/GetElementAtPointer"; import { execArgv } from "process"; +import { findLastIndex } from "@zsviczian/excalidraw/types/utils"; +import { fileOpen } from "@zsviczian/excalidraw/types/data/filesystem"; export enum TextMode { @@ -131,7 +137,7 @@ export const addFiles = async ( } if (s.dirty) { //debug({where:"ExcalidrawView.addFiles",file:view.file.name,dataTheme:view.excalidrawData.scene.appState.theme,before:"updateScene",state:scene.appState}) - view.updateScene({ + await view.updateScene({ elements: s.scene.elements, appState: s.scene.appState, commitToHistory: false, @@ -315,7 +321,7 @@ export default class ExcalidrawView extends TextFileView { } filename = `${filename}.excalidraw`; const folderpath = splitFolderAndFilename(this.file.path).folderpath; - await checkAndCreateFolder(app.vault, folderpath); //create folder if it does not exist + await checkAndCreateFolder(folderpath); //create folder if it does not exist const fname = getNewUniqueFilepath( app.vault, filename, @@ -1024,6 +1030,7 @@ export default class ExcalidrawView extends TextFileView { }); this.setupAutosaveTimer(); + super.onload(); } //this is to solve sliding panes bug @@ -1278,7 +1285,7 @@ export default class ExcalidrawView extends TextFileView { this.clearDirty(); } - async zoomToElementId(id: string) { + async zoomToElementId(id: string, hasGroupref:boolean) { let counter = 0; while (!this.excalidrawAPI && counter++<100) await sleep(50); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/734 const api = this.excalidrawAPI; @@ -1286,17 +1293,14 @@ export default class ExcalidrawView extends TextFileView { return; } const sceneElements = api.getSceneElements(); + let elements = sceneElements.filter((el: ExcalidrawElement) => el.id === id); - if(elements.length === 0) { - if(!id.startsWith("group_")) return; - id = id.split("group_")[1]; - elements = sceneElements.filter((el: ExcalidrawElement) => el.id === id); - if(elements.length === 0) return; + if(elements.length === 0) return; + if(hasGroupref) { const groupElements = this.plugin.ea.getElementsInTheSameGroupWithElement(elements[0],sceneElements) if(groupElements.length>0) { elements = groupElements; } - if(elements.length === 0) return; } this.preventAutozoom(); @@ -1325,16 +1329,13 @@ export default class ExcalidrawView extends TextFileView { ]; } - if (state.subpath && state.subpath.length > 2) { - if (state.subpath[1] === "^") { - const id = state.subpath.substring(2); - setTimeout(() => self.zoomToElementId(id), 300); - } else { - query = [`# ${state.subpath.substring(1)}`]; - } + const filenameParts = getEmbeddedFilenameParts(state.subpath); + if(filenameParts.hasBlockref) { + setTimeout(()=>self.zoomToElementId(filenameParts.blockref, filenameParts.hasGroupref),300); } - - if (state.line && state.line > 0) { + if(filenameParts.hasSectionref) { + query = [`# ${filenameParts.sectionref}`] + } else if (state.line && state.line > 0) { query = [this.data.split("\n")[state.line - 1]]; } @@ -1347,14 +1348,14 @@ export default class ExcalidrawView extends TextFileView { if (!api) { return; } - const elements = api - .getSceneElements() - .filter((el: ExcalidrawElement) => el.type === "text"); + const elements = api.getSceneElements(); + self.selectElementsMatchingQuery( elements, query, !api.getAppState().viewModeEnabled, - state.subpath && state.subpath.length>2 && state.subpath[1]!=="^", + filenameParts.hasSectionref, + filenameParts.hasGroupref ); }, 300); } @@ -1615,7 +1616,7 @@ export default class ExcalidrawView extends TextFileView { if(this.getSceneVersion(inData.scene.elements) !== this.previousSceneVersion) { this.setDirty(3); } - this.excalidrawAPI.updateScene({elements: sceneElements}); + await this.updateScene({elements: sceneElements}); if(reloadFiles) this.loadSceneFiles(); } catch(e) { errorlog({ @@ -1655,7 +1656,7 @@ export default class ExcalidrawView extends TextFileView { //debug({where:"ExcalidrawView.loadDrawing",file:this.file.name,dataTheme:excalidrawData.appState.theme,before:"updateScene"}) api.setLocalFont(this.plugin.settings.experimentalEnableFourthFont); - this.updateScene( + await this.updateScene( { elements: excalidrawData.elements.concat(deletedElements??[]), //need to preserve deleted elements during autosave if images, links, etc. are updated appState: { @@ -1727,6 +1728,12 @@ export default class ExcalidrawView extends TextFileView { //console.log(debug); this.semaphores.dirty = this.file?.path; this.diskIcon.querySelector("svg").addClass("excalidraw-dirty"); + if(!app.isMobile) { + if(requireApiVersion("0.16.0")) { + //@ts-ignore + this.leaf.tabHeaderInnerTitleEl.style.color="var(--color-accent)" + } + } } public clearDirty() { @@ -1740,6 +1747,12 @@ export default class ExcalidrawView extends TextFileView { this.previousSceneVersion = this.getSceneVersion(el); } this.diskIcon.querySelector("svg").removeClass("excalidraw-dirty"); + if(!app.isMobile) { + if(requireApiVersion("0.16.0")) { + //@ts-ignore + this.leaf.tabHeaderInnerTitleEl.style.color="" + } + } } public initializeToolsIconPanelAfterLoading() { @@ -1801,6 +1814,7 @@ export default class ExcalidrawView extends TextFileView { this.plugin.openDrawing( await this.plugin.convertSingleExcalidrawToMD(this.file), "active-pane", + true ); } @@ -2270,12 +2284,13 @@ export default class ExcalidrawView extends TextFileView { const elements = newElementsOnTop ? el.concat(newElements.filter((e) => !removeList.includes(e.id))) : newElements.filter((e) => !removeList.includes(e.id)).concat(el); - this.updateScene({ + + await this.updateScene({ elements, commitToHistory: true, }); - if (images) { + if (images && images !== {}) { const files: BinaryFileData[] = []; Object.keys(images).forEach((k) => { files.push({ @@ -3140,7 +3155,7 @@ export default class ExcalidrawView extends TextFileView { : this.leaf; await leaf.openFile( file, - subpath ? { active: false, eState: { subpath } } : {active:false}, + subpath ? { active: false, eState: { subpath } } : {active:false}, //active false: to avoid taking the focus from ExcaliBrain ); //if file exists open file and jump to reference //app.workspace.setActiveLeaf(leaf, true, true); //0.15.4 ExcaliBrain focus issue } catch (e) { @@ -3300,30 +3315,26 @@ export default class ExcalidrawView extends TextFileView { query: string[], selectResult: boolean = true, exactMatch: boolean = false, //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530 + selectGroup: boolean = false, ) { - if (!elements || elements.length === 0 || !query || query.length === 0) { - return; - } - - const match = elements.filter((el: any) => - query.some((q) => { - if (exactMatch) { - const text = el.rawText.toLowerCase().split("\n")[0].trim(); - const m = text.match(/^#*(# .*)/); - if (!m || m.length !== 2) { - return false; - } - return m[1] === q.toLowerCase(); - } - const text = el.rawText.toLowerCase().replaceAll("\n", " ").trim(); - return text.match(q.toLowerCase()); //to distinguish between "# frame" and "# frame 1" https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530 - }), + let match = getTextElementsMatchingQuery( + elements.filter((el: ExcalidrawElement) => el.type === "text"), + query, + exactMatch ); + if (match.length === 0) { new Notice("I could not find a matching text element"); return; } + if(selectGroup) { + const groupElements = this.plugin.ea.getElementsInTheSameGroupWithElement(match[0],elements) + if(groupElements.length>0) { + match = groupElements; + } + } + this.zoomToElements(selectResult,match); } @@ -3388,7 +3399,7 @@ export default class ExcalidrawView extends TextFileView { .filter((el: ExcalidrawElement) => elementIDs.contains(el.id)); } - public async copyLinkToSelectedElementToClipboard() { + public async copyLinkToSelectedElementToClipboard(groupLink:boolean) { const elements = this.getViewSelectedElements(); if (elements.length < 1) { new Notice(t("INSERT_LINK_TO_ELEMENT_ERROR")); @@ -3405,12 +3416,12 @@ export default class ExcalidrawView extends TextFileView { "", ); navigator.clipboard.writeText( - `[[${this.file.path}#^${elementId}${alias ? `|${alias}` : ``}]]`, + `[[${this.file.path}#^${groupLink?"group=":""}${elementId}${alias ? `|${alias}` : ``}]]`, ); new Notice(t("INSERT_LINK_TO_ELEMENT_READY")); } - public updateScene( + public async updateScene( scene: { elements?: ExcalidrawElement[]; appState?: any; @@ -3428,6 +3439,7 @@ export default class ExcalidrawView extends TextFileView { scene.elements = api.restore(scene).elements; } try { + await awaitNextAnimationFrame(); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/747 api.updateScene(scene); } catch (e) { errorlog({ @@ -3440,6 +3452,7 @@ export default class ExcalidrawView extends TextFileView { if (!shouldRestoreElements) { //second attempt try { + await awaitNextAnimationFrame(); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/747 scene.elements = api.restore(scene).elements; api.updateScene(scene); } catch (e) { @@ -3455,6 +3468,7 @@ export default class ExcalidrawView extends TextFileView { warningUnknowSeriousError(); } } + await awaitNextAnimationFrame() } } diff --git a/src/MarkdownPostProcessor.ts b/src/MarkdownPostProcessor.ts index f3db746..496aad6 100644 --- a/src/MarkdownPostProcessor.ts +++ b/src/MarkdownPostProcessor.ts @@ -22,6 +22,7 @@ import { } from "./utils/Utils"; import { isObsidianThemeDark } from "./utils/ObsidianUtils"; import { splitFolderAndFilename } from "./utils/FileUtils"; +import * as internal from "stream"; interface imgElementAttributes { file?: TFile; @@ -120,10 +121,18 @@ const getIMG = async ( scale = 5; } + //In case of PNG I cannot change the viewBox to select the area of the element + //being referenced. For PNG only the group reference works + const quickPNG = !filenameParts.hasGroupref + ? await getQuickImagePreview(plugin, file.path, "png") + : undefined; + const png = - (await getQuickImagePreview(plugin, file.path, "png")) ?? + quickPNG ?? (await createPNG( - file.path, + filenameParts.hasGroupref + ? filenameParts.filepath + filenameParts.linkpartReference + : file.path, scale, exportSettings, loader, @@ -140,14 +149,17 @@ const getIMG = async ( img.src = URL.createObjectURL(png); return img; } - const quickSVG = await getQuickImagePreview(plugin, file.path, "svg"); - if (quickSVG) { - img.setAttribute("src", svgToBase64(quickSVG)); - return img; + + if(!(filenameParts.hasBlockref || filenameParts.hasSectionref)) { + const quickSVG = await getQuickImagePreview(plugin, file.path, "svg"); + if (quickSVG) { + img.setAttribute("src", svgToBase64(quickSVG)); + return img; + } } const svgSnapshot = ( await createSVG( - filenameParts.hasGroupref + filenameParts.hasGroupref || filenameParts.hasBlockref || filenameParts.hasSectionref ? filenameParts.filepath + filenameParts.linkpartReference : file.path, true, @@ -185,7 +197,7 @@ const createImageDiv = async ( const img = await getIMG(attr); return createDiv(attr.style, (el) => { el.append(img); - el.setAttribute("src", attr.file.path); + el.setAttribute("src", attr.fname); if (attr.fwidth) { el.setAttribute("w", attr.fwidth); } @@ -201,13 +213,17 @@ const createImageDiv = async ( } const src = el.getAttribute("src"); if (src) { + const srcParts = src.match(/([^#]*)(.*)/); + if(!srcParts) return; plugin.openDrawing( - vault.getAbstractFileByPath(src) as TFile, + vault.getAbstractFileByPath(srcParts[1]) as TFile, ev[CTRL_OR_CMD] ? "new-pane" : (ev.metaKey && !app.isMobile) ? "popout-window" : "active-pane", + true, + srcParts[2], ); } //.ctrlKey||ev.metaKey); }); @@ -225,51 +241,56 @@ const createImageDiv = async ( }); }; -const processInternalEmbeds = async ( +const processReadingMode = async ( embeddedItems: NodeListOf | [HTMLElement], ctx: MarkdownPostProcessorContext, ) => { - //if not, then we are processing a non-excalidraw file in reading mode - //in that cases embedded files will be displayed in an .internal-embed container + //We are processing a non-excalidraw file in reading mode + //Embedded files will be displayed in an .internal-embed container + + //Iterating all the containers in the file to check which one is an excalidraw drawing + //This is a for loop instead of embeddedItems.forEach() because processInternalEmbed at the end + //is awaited, otherwise excalidraw images would not display in the Kanban plugin + for (const maybeDrawing of embeddedItems) { + //check to see if the file in the src attribute exists + const fname = maybeDrawing.getAttribute("src")?.split("#")[0]; + if(!fname) continue; + + const file = metadataCache.getFirstLinkpathDest(fname, ctx.sourcePath); + + //if the embeddedFile exits and it is an Excalidraw file + //then lets replace the .internal-embed with the generated PNG or SVG image + if (file && file instanceof TFile && plugin.isExcalidrawFile(file)) { + maybeDrawing.parentElement.replaceChild( + await processInternalEmbed(maybeDrawing,file), + maybeDrawing + ); + } + } +}; + +const processInternalEmbed = async (internalEmbedEl: Element, file: TFile ):Promise => { const attr: imgElementAttributes = { fname: "", fheight: "", fwidth: "", style: "", }; - let alt: string; - let file: TFile; - //Iterating through all the containers to check which one is an excalidraw drawing - //This is a for loop instead of embeddedItems.forEach() because createImageDiv at the end - //is awaited, otherwise excalidraw images would not display in the Kanban plugin - for (const maybeDrawing of embeddedItems) { - //check to see if the file in the src attribute exists - attr.fname = maybeDrawing.getAttribute("src"); - const fname = attr.fname?.split("#")[0]??""; - file = metadataCache.getFirstLinkpathDest(fname, ctx.sourcePath); - - //if the embeddedFile exits and it is an Excalidraw file - //then lets replace the .internal-embed with the generated PNG or SVG image - if (file && file instanceof TFile && plugin.isExcalidrawFile(file)) { - attr.fwidth = maybeDrawing.getAttribute("width") - ? maybeDrawing.getAttribute("width") - : getDefaultWidth(plugin); - attr.fheight = maybeDrawing.getAttribute("height"); - alt = maybeDrawing.getAttribute("alt"); - if (alt === attr.fname) { - alt = ""; - } //when the filename starts with numbers followed by a space Obsidian recognizes the filename as alt-text - attr.style = "excalidraw-svg"; - processAltText(fname,alt,attr); - const fnameParts = getEmbeddedFilenameParts(attr.fname); - attr.fname = file?.path + (fnameParts.hasBlockref?fnameParts.linkpartReference:""); - attr.file = file; - const div = await createImageDiv(attr); - maybeDrawing.parentElement.replaceChild(div, maybeDrawing); - } - } -}; + const src = internalEmbedEl.getAttribute("src"); + if(!src) return; + attr.fwidth = internalEmbedEl.getAttribute("width") + ? internalEmbedEl.getAttribute("width") + : getDefaultWidth(plugin); + attr.fheight = internalEmbedEl.getAttribute("height"); + let alt = internalEmbedEl.getAttribute("alt"); + attr.style = "excalidraw-svg"; + processAltText(src.split("#")[0],alt,attr); + const fnameParts = getEmbeddedFilenameParts(src); + attr.fname = file?.path + (fnameParts.hasBlockref||fnameParts.hasSectionref?fnameParts.linkpartReference:""); + attr.file = file; + return await createImageDiv(attr); +} const processAltText = ( fname: string, @@ -282,7 +303,14 @@ const processAltText = ( attr.fwidth = parts[2] ?? attr.fwidth; attr.fheight = parts[3] ?? attr.fheight; if (parts[4] && !parts[4].startsWith(fname)) { - attr.style = `excalidraw-svg${parts[4] ? `-${parts[4]}` : ""}`; + attr.style = `excalidraw-svg${`-${parts[4]}`}`; + } + if ( + (!parts[4] || parts[4]==="") && + (!parts[2] || parts[2]==="") && + parts[0] && parts[0] !== "" + ) { + attr.style = `excalidraw-svg${`-${parts[0]}`}`; } } } @@ -300,44 +328,9 @@ const tmpObsidianWYSIWYG = async ( return; } - //@ts-ignore - const dataHeading = el.firstChild?.getAttribute("data-heading"); - const blockrefToUnrecognizedTarget = dataHeading?.startsWith("Unable to find section") - - if (!blockrefToUnrecognizedTarget && !el.querySelector(".frontmatter")) { - el.style.display = "none"; - return; - } - - let blockref = ""; - if(blockrefToUnrecognizedTarget) { - const reg = new RegExp(`Unable to find section (.*) in ${file.basename}`); - const m = dataHeading.match(/Unable to find section (.*) in 1test/); - if(m && m.length>0) { - blockref = m[1]; - } - } - - const attr: imgElementAttributes = { - fname: ctx.sourcePath+blockref, - fheight: "", - fwidth: getDefaultWidth(plugin), - style: "excalidraw-svg", - }; - - attr.file = file; - el.empty(); - if (!plugin.settings.experimentalLivePreview) { - el.appendChild(await createImageDiv(attr)); - return; - } - - const div = createDiv(); - el.appendChild(div); - - //The timeout gives time for obsidian to attach el to the displayed document + //The timeout gives time for Obsidian to attach el to the displayed document //Once the element is attached, I can traverse up the dom tree to find .internal-embed //If internal embed is not found, it means the that the excalidraw.md file //is being rendered in "reading" mode. In that case, the image with the default width @@ -350,8 +343,7 @@ const tmpObsidianWYSIWYG = async ( while(!el.parentElement && counter++<=50) await sleep(50); if(!el.parentElement) return; - - let internalEmbedDiv: HTMLElement = div; + let internalEmbedDiv: HTMLElement = el; while ( !internalEmbedDiv.hasClass("internal-embed") && internalEmbedDiv.parentElement @@ -359,57 +351,39 @@ const tmpObsidianWYSIWYG = async ( internalEmbedDiv = internalEmbedDiv.parentElement; } + const attr: imgElementAttributes = { + fname: ctx.sourcePath, + fheight: "", + fwidth: getDefaultWidth(plugin), + style: "excalidraw-svg", + }; + + attr.file = file; + if (!internalEmbedDiv.hasClass("internal-embed")) { + //We are processing the markdown preview of an actual Excalidraw file + //This could be in a hover preview of the file + //Or the file could be in markdown mode and the user switched markdown + //view of the drawing to reading mode + const mdPreviewSection = el.parentElement; + if(!mdPreviewSection.hasClass("markdown-preview-section")) return; + if(mdPreviewSection.hasAttribute("ready")) { + mdPreviewSection.removeChild(el); + return; + } + mdPreviewSection.setAttribute("ready",""); el.empty(); - el.appendChild(await createImageDiv(attr)); + const imgDiv = await createImageDiv(attr); + el.appendChild(imgDiv); return; } + if(internalEmbedDiv.hasAttribute("ready")) return; + internalEmbedDiv.setAttribute("ready",""); + internalEmbedDiv.empty(); - - const basename = splitFolderAndFilename(attr.fname).basename; - const setAttr = () => { - const hasWidth = internalEmbedDiv.getAttribute("width") && (internalEmbedDiv.getAttribute("width") !== ""); - const hasHeight = internalEmbedDiv.getAttribute("height") && (internalEmbedDiv.getAttribute("height") !== ""); - if (hasWidth) { - attr.fwidth = internalEmbedDiv.getAttribute("width"); - } - if (hasHeight) { - attr.fheight = internalEmbedDiv.getAttribute("height"); - } - - if (!hasWidth && !hasHeight) { - attr.fheight = ""; - attr.fwidth = getDefaultWidth(plugin); - attr.style = "excalidraw-svg"; - } - - const alt = internalEmbedDiv.getAttribute("alt"); - processAltText(basename,alt,attr); -/* const hasAttr = - alt && - alt !== "" && - alt !== basename && - alt !== internalEmbedDiv.getAttribute("src") && - !alt.startsWith(attr.file.name + " > "); - if (hasAttr) { - //1:width, 2:height, 3:style 1 2 3 - const parts = alt.match(/(\d*%?)x?(\d*%?)\|?(.*)/); - attr.fwidth = parts[1] ? parts[1] : getDefaultWidth(plugin); - attr.fheight = parts[2]; - if (parts[3] != attr.fname) { - attr.style = `excalidraw-svg${parts[3] ? `-${parts[3]}` : ""}`; - } - }*/ - - }; - - const createImgElement = async () => { - setAttr(); - const imgDiv = await createImageDiv(attr); - internalEmbedDiv.appendChild(imgDiv); - }; - await createImgElement(); + const imgDiv = await processInternalEmbed(internalEmbedDiv,file); + internalEmbedDiv.appendChild(imgDiv); //timer to avoid the image flickering when the user is typing let timer: NodeJS.Timeout = null; @@ -420,11 +394,11 @@ const tmpObsidianWYSIWYG = async ( if (timer) { clearTimeout(timer); } - timer = setTimeout(() => { + timer = setTimeout(async () => { timer = null; - setAttr(); internalEmbedDiv.empty(); - createImgElement(); + const imgDiv = await processInternalEmbed(internalEmbedDiv,file); + internalEmbedDiv.appendChild(imgDiv); }, 500); }); observer.observe(internalEmbedDiv, { @@ -459,7 +433,7 @@ export const markdownPostProcessor = async ( return; } - await processInternalEmbeds(embeddedItems, ctx); + await processReadingMode(embeddedItems, ctx); }; /** diff --git a/src/Scripts.ts b/src/Scripts.ts index 16ad361..b1b5b77 100644 --- a/src/Scripts.ts +++ b/src/Scripts.ts @@ -34,7 +34,7 @@ export class ScriptEngine { if (!path.endsWith(".svg")) { return; } - const scriptFile = this.plugin.app.vault.getAbstractFileByPath( + const scriptFile = app.vault.getAbstractFileByPath( getIMGFilename(path, "md"), ); if (scriptFile && scriptFile instanceof TFile) { @@ -53,7 +53,7 @@ export class ScriptEngine { handleSvgFileChange(file.path); }; this.plugin.registerEvent( - this.plugin.app.vault.on("delete", deleteEventHandler), + app.vault.on("delete", deleteEventHandler), ); const createEventHandler = async (file: TFile) => { @@ -67,7 +67,7 @@ export class ScriptEngine { handleSvgFileChange(file.path); }; this.plugin.registerEvent( - this.plugin.app.vault.on("create", createEventHandler), + app.vault.on("create", createEventHandler), ); const renameEventHandler = async (file: TAbstractFile, oldPath: string) => { @@ -86,7 +86,7 @@ export class ScriptEngine { } }; this.plugin.registerEvent( - this.plugin.app.vault.on("rename", renameEventHandler), + app.vault.on("rename", renameEventHandler), ); } @@ -101,7 +101,6 @@ export class ScriptEngine { } public getListofScripts(): TFile[] { - const app = this.plugin.app; this.scriptPath = this.plugin.settings.scriptFolderPath; if (!app.vault.getAbstractFileByPath(this.scriptPath)) { this.scriptPath = null; @@ -140,10 +139,10 @@ export class ScriptEngine { async addScriptIconToMap(scriptPath: string, name: string) { const svgFilePath = getIMGFilename(scriptPath, "svg"); - const file = this.plugin.app.vault.getAbstractFileByPath(svgFilePath); + const file = app.vault.getAbstractFileByPath(svgFilePath); const svgString: string = file && file instanceof TFile - ? await this.plugin.app.vault.read(file) + ? await app.vault.read(file) : null; this.scriptIconMap = { ...this.scriptIconMap, @@ -168,7 +167,7 @@ export class ScriptEngine { const view = app.workspace.getActiveViewOfType(ExcalidrawView); if (view) { (async()=>{ - const script = await this.plugin.app.vault.read(f); + const script = await app.vault.read(f); if(script) { this.executeScript(view, script, scriptName); } @@ -181,7 +180,6 @@ export class ScriptEngine { } unloadScripts() { - const app = this.plugin.app; const scripts = app.vault .getFiles() .filter((f: TFile) => f.path.startsWith(this.scriptPath)); @@ -198,7 +196,6 @@ export class ScriptEngine { this.scriptIconMap = { ...this.scriptIconMap }; this.updateToolPannels(); - const app = this.plugin.app; const commandId = `${PLUGIN_ID}:${basename}`; // @ts-ignore if (!app.commands.commands[commandId]) { @@ -229,7 +226,7 @@ export class ScriptEngine { buttons?: [{ caption: string; action: Function }], ) => ScriptEngine.inputPrompt( - this.plugin.app, + app, header, placeholder, value, @@ -242,7 +239,7 @@ export class ScriptEngine { instructions?: Instruction[], ) => ScriptEngine.suggester( - this.plugin.app, + app, displayItems, items, hint, @@ -259,7 +256,7 @@ export class ScriptEngine { private updateToolPannels() { const leaves = - this.plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); + app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); leaves.forEach((leaf: WorkspaceLeaf) => { const excalidrawView = leaf.view as ExcalidrawView; excalidrawView.toolsPanelRef?.current?.updateScriptIconMap( diff --git a/src/dialogs/OpenDrawing.ts b/src/dialogs/OpenDrawing.ts index e1bd492..ab6be3f 100644 --- a/src/dialogs/OpenDrawing.ts +++ b/src/dialogs/OpenDrawing.ts @@ -55,7 +55,7 @@ export class OpenFileDialog extends FuzzySuggestModal { onChooseItem(item: TFile): void { switch (this.action) { case openDialogAction.openFile: - this.plugin.openDrawing(item, this.onNewPane?"new-pane":"active-pane"); + this.plugin.openDrawing(item, this.onNewPane?"new-pane":"active-pane",true); break; case openDialogAction.insertLinkToDrawing: this.plugin.embedDrawing(item); diff --git a/src/dialogs/Prompt.ts b/src/dialogs/Prompt.ts index 371e548..b5b2805 100644 --- a/src/dialogs/Prompt.ts +++ b/src/dialogs/Prompt.ts @@ -13,6 +13,7 @@ import ExcalidrawView from "../ExcalidrawView"; import ExcalidrawPlugin from "../main"; import { sleep } from "../utils/Utils"; import { getNewOrAdjacentLeaf } from "../utils/ObsidianUtils"; +import { checkAndCreateFolder, splitFolderAndFilename } from "src/utils/FileUtils"; export class Prompt extends Modal { private promptEl: HTMLInputElement; @@ -434,6 +435,8 @@ export class NewFileActions extends Modal { if (!this.path.match(/\.md$/)) { this.path = `${this.path}.md`; } + const folderpath = splitFolderAndFilename(this.path).folderpath; + checkAndCreateFolder(folderpath); const f = await this.app.vault.create(this.path, data); return f; }; diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index e909be8..9eb1aee 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -28,6 +28,7 @@ export default { "Open an existing drawing - IN THE CURRENT ACTIVE PANE", TRANSCLUDE: "Transclude (embed) a drawing", TRANSCLUDE_MOST_RECENT: "Transclude (embed) the most recently edited drawing", + TOGGLE_LEFTHANDED_MODE: "Toggle left-handed mode", NEW_IN_NEW_PANE: "Create a new drawing - IN A NEW PANE", NEW_IN_ACTIVE_PANE: "Create a new drawing - IN THE CURRENT ACTIVE PANE", NEW_IN_POPOUT_WINDOW: "Create a new drawing - IN A POPOUT WINDOW", @@ -41,7 +42,11 @@ export default { TOGGLE_LOCK: "Toggle Text Element edit RAW/PREVIEW", DELETE_FILE: "Delete selected Image or Markdown file from Obsidian Vault", INSERT_LINK_TO_ELEMENT: - "Copy markdown link for selected element to clipboard", + "Copy markdown link for selected element to clipboard. CTRL/CMD+Click to copy group link.", + INSERT_LINK_TO_ELEMENT_GROUP: + "Copy markdown link for selected element group to clipboard.", + INSERT_LINK_TO_ELEMENT_NORMAL: + "Copy markdown link for selected element to clipboard.", INSERT_LINK_TO_ELEMENT_ERROR: "Select a single element in the scene", INSERT_LINK_TO_ELEMENT_READY: "Link is READY and available on the clipboard", INSERT_LINK: "Insert link to file", diff --git a/src/main.ts b/src/main.ts index 1576dbc..a28e94b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -312,10 +312,10 @@ export default class ExcalidrawPlugin extends Plugin { //@ts-ignore win.MathJax.startup.pagePromise.then(async () => { //https://github.com/xldenis/obsidian-latex/blob/master/main.ts - const file = self.app.vault.getAbstractFileByPath("preamble.sty"); + const file = app.vault.getAbstractFileByPath("preamble.sty"); const preamble: string = file && file instanceof TFile - ? await self.app.vault.read(file) + ? await app.vault.read(file) : null; try { if (preamble) { @@ -348,7 +348,7 @@ export default class ExcalidrawPlugin extends Plugin { const self = this; this.app.workspace.onLayoutReady(() => { let leaf: WorkspaceLeaf; - for (leaf of self.app.workspace.getLeavesOfType("markdown")) { + for (leaf of app.workspace.getLeavesOfType("markdown")) { if ( leaf.view instanceof MarkdownView && self.isExcalidrawFile(leaf.view.file) @@ -455,7 +455,7 @@ export default class ExcalidrawPlugin extends Plugin { if (file) { await this.app.vault.modify(file as TFile, data); } else { - await checkAndCreateFolder(this.app.vault, folder); + await checkAndCreateFolder(folder); file = await this.app.vault.create(localPath, data); } return file; @@ -835,7 +835,7 @@ export default class ExcalidrawPlugin extends Plugin { ).folder; const file = await this.createDrawing(filename, folder); await this.embedDrawing(file); - this.openDrawing(file, location); + this.openDrawing(file, location, true); }; this.addCommand({ @@ -1044,20 +1044,62 @@ export default class ExcalidrawPlugin extends Plugin { this.addCommand({ id: "insert-link-to-element", hotkeys: [{ modifiers: ["Ctrl" || "Meta", "Shift"], key: "k" }], - name: t("INSERT_LINK_TO_ELEMENT"), + name: t("INSERT_LINK_TO_ELEMENT_NORMAL"), checkCallback: (checking: boolean) => { if (checking) { return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView)) } const view = this.app.workspace.getActiveViewOfType(ExcalidrawView); if (view) { - view.copyLinkToSelectedElementToClipboard(); + view.copyLinkToSelectedElementToClipboard(false); return true; } return false; }, }); + this.addCommand({ + id: "insert-link-to-element-group", + name: t("INSERT_LINK_TO_ELEMENT_GROUP"), + checkCallback: (checking: boolean) => { + if (checking) { + return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView)) + } + const view = this.app.workspace.getActiveViewOfType(ExcalidrawView); + if (view) { + view.copyLinkToSelectedElementToClipboard(true); + return true; + } + return false; + }, + }); + + this.addCommand({ + id: "toggle-lefthanded-mode", + name: t("TOGGLE_LEFTHANDED_MODE"), + checkCallback: (checking: boolean) => { + if (checking) { + if(this.app.workspace.getActiveViewOfType(ExcalidrawView)) { + const view = this.app.workspace.getActiveViewOfType(ExcalidrawView); + const api = view?.excalidrawAPI; + if(!api) return false; + const st = api.getAppState(); + if(!st.trayModeEnabled) return false; + return true; + } + return false; + } + const view = this.app.workspace.getActiveViewOfType(ExcalidrawView); + (async()=>{ + await this.loadSettings(); + this.settings.isLeftHanded = !this.settings.isLeftHanded; + this.saveSettings(); + setLeftHandedMode(this.settings.isLeftHanded); + })() + return true; + }, + }); + this.addCommand({ id: "insert-image", name: t("INSERT_IMAGE"), @@ -1358,7 +1400,7 @@ export default class ExcalidrawPlugin extends Plugin { "markdown" ) { // Then check for the excalidraw frontMatterKey - const cache = self.app.metadataCache.getCache(state.state.file); + const cache = app.metadataCache.getCache(state.state.file); if (cache?.frontmatter && cache.frontmatter[FRONTMATTER_KEY]) { // If we have it, force the view type to excalidraw @@ -1379,43 +1421,6 @@ export default class ExcalidrawPlugin extends Plugin { }, }), ); - - // Add a menu item to go back to Excalidraw view - /*this.register( - around(MarkdownView.prototype, { - onPaneMenu(next) { - return function (menu: Menu) { - const file = this.file; - const cache = file - ? self.app.metadataCache.getFileCache(file) - : null; - - if ( - !file || - !cache?.frontmatter || - !cache.frontmatter[FRONTMATTER_KEY] - ) { - return next.call(this, menu); - } - - menu - .addItem((item) => { - item - .setTitle(t("OPEN_AS_EXCALIDRAW")) - .setIcon(ICON_NAME) - .setSection("pane") - .onClick(() => { - self.excalidrawFileModes[this.leaf.id || file.path] = - VIEW_TYPE_EXCALIDRAW; - self.setExcalidrawView(this.leaf); - }); - }) - .addSeparator(); - next.call(this, menu); - }; - }, - }), - );*/ } private popScope: Function = null; @@ -1439,19 +1444,19 @@ export default class ExcalidrawPlugin extends Plugin { } [".svg", ".png", ".excalidraw"].forEach(async (ext: string) => { const oldIMGpath = getIMGPathFromExcalidrawFile(oldPath, ext); - const imgFile = self.app.vault.getAbstractFileByPath( + const imgFile = app.vault.getAbstractFileByPath( normalizePath(oldIMGpath), ); if (imgFile && imgFile instanceof TFile) { const newIMGpath = getIMGPathFromExcalidrawFile(file.path, ext); - await self.app.fileManager.renameFile(imgFile, newIMGpath); + await app.fileManager.renameFile(imgFile, newIMGpath); } }); }; - self.registerEvent(self.app.vault.on("rename", renameEventHandler)); + self.registerEvent(app.vault.on("rename", renameEventHandler)); const modifyEventHandler = async (file: TFile) => { - const leaves = self.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); + const leaves = app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); leaves.forEach(async (leaf: WorkspaceLeaf) => { const excalidrawView = leaf.view as ExcalidrawView; if ( @@ -1484,7 +1489,7 @@ export default class ExcalidrawPlugin extends Plugin { } }); }; - self.registerEvent(self.app.vault.on("modify", modifyEventHandler)); + self.registerEvent(app.vault.on("modify", modifyEventHandler)); //watch file delete and delete corresponding .svg and .png const deleteEventHandler = async (file: TFile) => { @@ -1499,7 +1504,7 @@ export default class ExcalidrawPlugin extends Plugin { } //close excalidraw view where this file is open - const leaves = self.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); + const leaves = app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); for (let i = 0; i < leaves.length; i++) { if ((leaves[i].view as ExcalidrawView).file.path == file.path) { await leaves[i].setViewState({ @@ -1514,27 +1519,27 @@ export default class ExcalidrawPlugin extends Plugin { setTimeout(() => { [".svg", ".png", ".excalidraw"].forEach(async (ext: string) => { const imgPath = getIMGPathFromExcalidrawFile(file.path, ext); - const imgFile = self.app.vault.getAbstractFileByPath( + const imgFile = app.vault.getAbstractFileByPath( normalizePath(imgPath), ); if (imgFile && imgFile instanceof TFile) { - await self.app.vault.delete(imgFile); + await app.vault.delete(imgFile); } }); }, 500); } }; - self.registerEvent(self.app.vault.on("delete", deleteEventHandler)); + self.registerEvent(app.vault.on("delete", deleteEventHandler)); //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 = self.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); + 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(self.app.workspace.on("quit", quitEventHandler));*/ + self.registerEvent(app.workspace.on("quit", quitEventHandler));*/ //save Excalidraw leaf and update embeds when switching to another leaf const activeLeafChangeEventHandler = async (leaf: WorkspaceLeaf) => { @@ -1603,7 +1608,7 @@ export default class ExcalidrawPlugin extends Plugin { } }; self.registerEvent( - self.app.workspace.on( + app.workspace.on( "active-leaf-change", activeLeafChangeEventHandler, ), @@ -1611,7 +1616,7 @@ export default class ExcalidrawPlugin extends Plugin { self.addFileSaveTriggerEventHandlers(); - const metaCache: MetadataCache = self.app.metadataCache; + const metaCache: MetadataCache = app.metadataCache; //@ts-ignore metaCache.getCachedFiles().forEach((filename: string) => { const fm = metaCache.getCache(filename)?.frontmatter; @@ -1620,7 +1625,7 @@ export default class ExcalidrawPlugin extends Plugin { filename.match(/\.excalidraw$/) ) { self.updateFileCache( - self.app.vault.getAbstractFileByPath(filename) as TFile, + app.vault.getAbstractFileByPath(filename) as TFile, fm, ); } @@ -1864,7 +1869,9 @@ export default class ExcalidrawPlugin extends Plugin { public openDrawing( drawingFile: TFile, - location: "active-pane"|"new-pane"|"popout-window" + location: "active-pane"|"new-pane"|"popout-window", + active: boolean = false, + subpath?: string ) { let leaf: WorkspaceLeaf; if(location === "popout-window") { @@ -1878,10 +1885,17 @@ export default class ExcalidrawPlugin extends Plugin { } } - leaf.setViewState({ + leaf.openFile( + drawingFile, + !subpath || subpath === "" + ? {active} + : { active, eState: { subpath } } + ); + +/* leaf.setViewState({ type: VIEW_TYPE_EXCALIDRAW, - state: { file: drawingFile.path }, - }); + state: { file: drawingFile.path, eState: {subpath}}, + });*/ } public async getBlankDrawing(): Promise { @@ -1960,7 +1974,7 @@ export default class ExcalidrawPlugin extends Plugin { const folderpath = normalizePath( foldername ? foldername : this.settings.folder, ); - await checkAndCreateFolder(this.app.vault, folderpath); //create folder if it does not exist + await checkAndCreateFolder(folderpath); //create folder if it does not exist const fname = getNewUniqueFilepath(this.app.vault, filename, folderpath); const file = await this.app.vault.create( fname, @@ -1987,7 +2001,7 @@ export default class ExcalidrawPlugin extends Plugin { initData?: string, ): Promise { const file = await this.createDrawing(filename, foldername, initData); - this.openDrawing(file, location); + this.openDrawing(file, location, true); return file.path; } @@ -2039,7 +2053,7 @@ export default class ExcalidrawPlugin extends Plugin { } filename = `${filename}.excalidrawlib`; const folderpath = normalizePath(this.settings.folder); - await checkAndCreateFolder(this.app.vault, folderpath); //create folder if it does not exist + await checkAndCreateFolder(folderpath); //create folder if it does not exist const fname = getNewUniqueFilepath( this.app.vault, filename, diff --git a/src/menu/ToolsPanel.tsx b/src/menu/ToolsPanel.tsx index af9f327..9b63a6d 100644 --- a/src/menu/ToolsPanel.tsx +++ b/src/menu/ToolsPanel.tsx @@ -3,7 +3,7 @@ import { Notice, TFile } from "obsidian"; import * as React from "react"; import { ActionButton } from "./ActionButton"; import { ICONS } from "./ActionIcons"; -import { SCRIPT_INSTALL_FOLDER } from "../Constants"; +import { SCRIPT_INSTALL_FOLDER, CTRL_OR_CMD } from "../Constants"; import { insertLaTeXToView, search } from "../ExcalidrawAutomate"; import ExcalidrawView, { TextMode } from "../ExcalidrawView"; import { t } from "../lang/helpers"; @@ -464,8 +464,8 @@ export class ToolsPanel extends React.Component { { - this.props.view.copyLinkToSelectedElementToClipboard(); + action={(e:React.MouseEvent) => { + this.props.view.copyLinkToSelectedElementToClipboard(e[CTRL_OR_CMD]); }} icon={ICONS.copyElementLink} view={this.props.view} diff --git a/src/settings.ts b/src/settings.ts index 990862f..f0dd0bd 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -219,7 +219,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab { this.plugin.saveSettings(); if (this.requestReloadDrawings) { const exs = - this.plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); + app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); for (const v of exs) { if (v.view instanceof ExcalidrawView) { await v.view.save(false); diff --git a/src/utils/FileUtils.ts b/src/utils/FileUtils.ts index 3904e38..0516e2c 100644 --- a/src/utils/FileUtils.ts +++ b/src/utils/FileUtils.ts @@ -120,7 +120,8 @@ export function getEmbedFilename( * Open or create a folderpath if it does not exist * @param folderpath */ -export async function checkAndCreateFolder(vault: Vault, folderpath: string) { +export async function checkAndCreateFolder(folderpath: string) { + const vault = app.vault; folderpath = normalizePath(folderpath); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/658 //@ts-ignore diff --git a/src/utils/ObsidianUtils.ts b/src/utils/ObsidianUtils.ts index 152b0cc..2432b51 100644 --- a/src/utils/ObsidianUtils.ts +++ b/src/utils/ObsidianUtils.ts @@ -144,7 +144,7 @@ export const getAttachmentsFolderAndFilePath = async ( if (!folder || folder === "/") { folder = ""; } - await checkAndCreateFolder(app.vault, folder); + await checkAndCreateFolder(folder); return { folder, filepath: normalizePath( diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index f20ebd2..4117a25 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -576,8 +576,8 @@ export const getEmbeddedFilenameParts = (fname:string):{ linkpartReference: string, linkpartAlias: string } => { - // 0 1 23 4 5 6 7 8 - const parts = fname?.match(/([^#\^]*)((#\^)(group_)?([^\|]*)|(#)([^\^\|]*))(.*)/); + // 0 1 23 4 5 6 7 8 9 + const parts = fname?.match(/([^#\^]*)((#\^)(group=)?([^\|]*)|(#)(group=)?([^\^\|]*))(.*)/); if(!parts) { return { filepath: fname, @@ -593,12 +593,12 @@ export const getEmbeddedFilenameParts = (fname:string):{ return { filepath: parts[1], hasBlockref: Boolean(parts[3]), - hasGroupref: Boolean(parts[4]), + hasGroupref: Boolean(parts[4]) || Boolean(parts[7]), blockref: parts[5], hasSectionref: Boolean(parts[6]), - sectionref: parts[7], + sectionref: parts[8], linkpartReference: parts[2], - linkpartAlias: parts[8] + linkpartAlias: parts[9] } } @@ -607,6 +607,7 @@ export const errorlog = (data: {}) => { }; export const sleep = async (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); +export const awaitNextAnimationFrame = async () => new Promise(requestAnimationFrame); export const log = console.log.bind(window.console); export const debug = console.log.bind(window.console); diff --git a/styles.css b/styles.css index 13eda74..5af654e 100644 --- a/styles.css +++ b/styles.css @@ -138,7 +138,11 @@ li[data-testid] { } .excalidraw-prompt-center { - text-align: center; + text-align: center !important; +} + +.excalidraw-prompt-center button { + margin: 0 10px; } .excalidraw-prompt-center.filepath {