diff --git a/package.json b/package.json index 029e452..a7df6ff 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "author": "", "license": "MIT", "dependencies": { - "@zsviczian/excalidraw": "0.11.0-obsidian-11", + "@zsviczian/excalidraw": "0.11.0-obsidian-16", "monkey-around": "^2.3.0", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/src/EmbeddedFileLoader.ts b/src/EmbeddedFileLoader.ts index f7e4099..5f0bc48 100644 --- a/src/EmbeddedFileLoader.ts +++ b/src/EmbeddedFileLoader.ts @@ -162,7 +162,6 @@ export class EmbeddedFile { export class EmbeddedFilesLoader { private plugin: ExcalidrawPlugin; - private processedFiles: Map = new Map(); private isDark: boolean; public terminate = false; public uid: string; @@ -173,7 +172,7 @@ export class EmbeddedFilesLoader { this.uid = nanoid(); } - public async getObsidianImage(inFile: TFile | EmbeddedFile): Promise<{ + public async getObsidianImage(inFile: TFile | EmbeddedFile, depth: number): Promise<{ mimeType: MimeType; fileId: FileId; dataURL: DataURL; @@ -196,15 +195,7 @@ export class EmbeddedFilesLoader { width: this.plugin.settings.mdSVGwidth, height: this.plugin.settings.mdSVGmaxHeight, }; - //to block infinite loop of recursive loading of images - const count = this.processedFiles.has(file.path) - ? this.processedFiles.get(file.path) - : 0; - if (file.extension === "md" && count > 2) { - new Notice(t("INFINITE_LOOP_WARNING") + file.path, 6000); - return null; - } - this.processedFiles.set(file.path, count + 1); + let hasSVGwithBitmap = false; const app = this.plugin.app; const isExcalidrawFile = this.plugin.isExcalidrawFile(file); @@ -240,6 +231,7 @@ export class EmbeddedFilesLoader { null, [], this.plugin, + depth+1, getSVGPadding(this.plugin, file), ); //https://stackoverflow.com/questions/51154171/remove-css-filter-on-child-elements @@ -315,7 +307,12 @@ export class EmbeddedFilesLoader { public async loadSceneFiles( excalidrawData: ExcalidrawData, addFiles: Function, + depth:number ) { + if(depth > 4) { + new Notice(t("INFINITE_LOOP_WARNING")+depth.toString(), 6000); + return; + } const entries = excalidrawData.getFileEntries(); //debug({where:"EmbeddedFileLoader.loadSceneFiles",uid:this.uid,isDark:this.isDark,sceneTheme:excalidrawData.scene.appState.theme}); if (this.isDark === undefined) { @@ -327,7 +324,7 @@ export class EmbeddedFilesLoader { const embeddedFile: EmbeddedFile = entry.value[1]; if (!embeddedFile.isLoaded(this.isDark)) { //debug({where:"EmbeddedFileLoader.loadSceneFiles",uid:this.uid,status:"embedded Files are not loaded"}); - const data = await this.getObsidianImage(embeddedFile); + const data = await this.getObsidianImage(embeddedFile, depth); if (data) { files.push({ mimeType: data.mimeType, diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts index ef1f303..f287b83 100644 --- a/src/ExcalidrawAutomate.ts +++ b/src/ExcalidrawAutomate.ts @@ -6,6 +6,7 @@ import { ExcalidrawElement, ExcalidrawBindableElement, FileId, + NonDeletedExcalidrawElement, } from "@zsviczian/excalidraw/types/element/types"; import { normalizePath, TFile, WorkspaceLeaf } from "obsidian"; import ExcalidrawView, { ExportSettings, TextMode } from "./ExcalidrawView"; @@ -17,6 +18,7 @@ import { MAX_IMAGE_SIZE, PLUGIN_ID, COLOR_NAMES, + fileid, } from "./Constants"; import { getDrawingFilename, } from "./utils/FileUtils"; import { @@ -202,6 +204,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { templatePath, false, new EmbeddedFilesLoader(this.plugin), + 0 ) : null; let elements = template ? template.elements : []; @@ -253,6 +256,11 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { "excalidraw-link-prefix"?: string; "excalidraw-link-brackets"?: boolean; "excalidraw-url-prefix"?: string; + "excalidraw-export-transparent"?: boolean; + "excalidraw-export-dark"?: boolean; + "excalidraw-export-svgpadding"?: number; + "excalidraw-export-pngscale"?: number; + "excalidraw-default-mode"?: "view" | "zen"; }; }): Promise { const template = params?.templatePath @@ -261,6 +269,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { params.templatePath, true, new EmbeddedFilesLoader(this.plugin), + 0 ) : null; let elements = template ? template.elements : []; @@ -341,7 +350,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { return this.plugin.createAndOpenDrawing( params?.filename - ? `${params.filename}.excalidraw.md` + ? params.filename + (params.filename.endsWith(".md") ? "": ".excalidraw.md") : getDrawingFilename(this.plugin.settings), params?.onNewPane ? params.onNewPane : false, params?.foldername ? params.foldername : this.plugin.settings.folder, @@ -400,6 +409,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { this.canvas.viewBackgroundColor, this.getElements(), this.plugin, + 0 ); }; @@ -451,6 +461,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { this.canvas.viewBackgroundColor, this.getElements(), this.plugin, + 0 ); }; @@ -497,6 +508,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { groupIds: [] as any, boundElements: [] as any, link: null as string, + locked: false, }; } @@ -842,13 +854,14 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { this.plugin, this.canvas.theme === "dark", ); - const image = await loader.getObsidianImage(imageFile); + const image = await loader.getObsidianImage(imageFile,0); if (!image) { return null; } - this.imagesDict[image.fileId] = { + const fileId = imageFile.extension === "md" ? fileid() as FileId : image.fileId; + this.imagesDict[fileId] = { mimeType: image.mimeType, - id: image.fileId, + id: fileId, dataURL: image.dataURL, created: image.created, file: imageFile.path, @@ -869,7 +882,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { image.size.width, image.size.height, ); - this.elementsDict[id].fileId = image.fileId; + this.elementsDict[id].fileId = fileId; this.elementsDict[id].scale = [1, 1]; return id; }; @@ -1311,7 +1324,54 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { }; /** - * if set Excalidraw will call this function onDrop events + * Register instance of EA to use for hooks with TargetView + * By default ExcalidrawViews will check window.ExcalidrawAutomate for event hooks. + * Using this event you can set a different instance of Excalidraw Automate for hooks + */ + registerThisAsViewEA() { + //@ts-ignore + if (!this.targetView || !this.targetView?._loaded) { + errorMessage("targetView not set", "addElementsToView()"); + return false; + } + this.targetView.hookServer = this; + } + + + /** + * If set, this callback is triggered, when the user changes the view mode. + * You can use this callback in case you want to do something additional when the user switches to view mode and back. + */ + onViewModeChangeHook: (isViewModeEnabled:boolean) => void = null; + + /** + * If set, this callback is triggered, when the user hovers a link in the scene. + * You can use this callback in case you want to do something additional when the onLinkHover event occurs. + * This callback must return a boolean value. + * In case you want to prevent the excalidraw onLinkHover action you must return false, it will stop the native excalidraw onLinkHover management flow. + */ + onLinkHoverHook: ( + element: NonDeletedExcalidrawElement, + linkText: string, + ) => boolean = null; + + /** + * If set, this callback is triggered, when the user click a link in the scene. + * You can use this callback in case you want to do something additional when the onLinkClick event occurs. + * This callback must return a boolean value. + * In case you want to prevent the excalidraw onLinkClick action you must return false, it will stop the native excalidraw onLinkClick management flow. + */ + onLinkClickHook:( + element: ExcalidrawElement, + linkText: string, + event: MouseEvent + ) => boolean = null; + + /** + * If set, this callback is triggered, when Excalidraw receives an onDrop event. + * You can use this callback in case you want to do something additional when the onDrop event occurs. + * This callback must return a boolean value. + * In case you want to prevent the excalidraw onDrop action you must return false, it will stop the native excalidraw onDrop management flow. */ onDropHook: (data: { ea: ExcalidrawAutomate; @@ -1776,6 +1836,7 @@ async function getTemplate( fileWithPath: string, loadFiles: boolean = false, loader: EmbeddedFilesLoader, + depth: number ): Promise<{ elements: any; appState: any; @@ -1839,7 +1900,7 @@ async function getTemplate( }; } scene = scaleLoadedImage(excalidrawData.scene, fileArray).scene; - }); + }, depth); } return { @@ -1869,12 +1930,13 @@ export async function createPNG( canvasBackgroundColor: string = undefined, automateElements: ExcalidrawElement[] = [], plugin: ExcalidrawPlugin, + depth: number ) { if (!loader) { loader = new EmbeddedFilesLoader(plugin); } const template = templatePath - ? await getTemplate(plugin, templatePath, true, loader) + ? await getTemplate(plugin, templatePath, true, loader, depth) : null; let elements = template?.elements ?? []; elements = elements.concat(automateElements); @@ -1910,13 +1972,14 @@ export async function createSVG( canvasBackgroundColor: string = undefined, automateElements: ExcalidrawElement[] = [], plugin: ExcalidrawPlugin, + depth: number, padding?: number, ): Promise { if (!loader) { loader = new EmbeddedFilesLoader(plugin); } const template = templatePath - ? await getTemplate(plugin, templatePath, true, loader) + ? await getTemplate(plugin, templatePath, true, loader, depth) : null; let elements = template?.elements ?? []; elements = elements.concat(automateElements); diff --git a/src/ExcalidrawData.ts b/src/ExcalidrawData.ts index e325137..e9b2f15 100644 --- a/src/ExcalidrawData.ts +++ b/src/ExcalidrawData.ts @@ -14,6 +14,7 @@ import { FRONTMATTER_KEY_DEFAULT_MODE, fileid, REG_BLOCK_REF_CLEAN, + FRONTMATTER_KEY_LINKBUTTON_OPACITY, } from "./Constants"; import { _measureText } from "./ExcalidrawAutomate"; import ExcalidrawPlugin from "./main"; @@ -1021,6 +1022,35 @@ export class ExcalidrawData { return false; } + //assing new fileId to duplicate equation and markdown files + //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/601 + //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/593 + //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/297 + const processedIds = new Set(); + fileIds.forEach(fileId=>{ + if(processedIds.has(fileId)) { + const file = this.files.get(fileId as FileId); + const equation = this.equations.get(fileId as FileId); + //images should have a single reference, but equations and markdown embeds should have as many as instances of the file in the scene + if(file && file.file.extension !== "md") { + return; + } + const newId = fileid(); + //scene.files[newId] = {...scene.files[fileId]}; + (scene.elements.filter((el:ExcalidrawImageElement)=>el.fileId === fileId)[0] as any).fileId = newId; + dirty = true; + processedIds.add(newId); + if(file) { + this.files.set(newId as FileId,new EmbeddedFile(this.plugin,this.file.path,file.linkParts.original)) + } + if(equation) { + this.equations.set(newId as FileId, equation); + } + } + processedIds.add(fileId); + }); + + for (const key of Object.keys(scene.files)) { if (!(this.hasFile(key as FileId) || this.hasEquation(key as FileId))) { dirty = true; @@ -1069,7 +1099,7 @@ export class ExcalidrawData { } //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/297 - const equations = new Set(); + /*const equations = new Set(); const duplicateEqs = new Set(); for (const key of fileIds) { if (this.hasEquation(key as FileId)) { @@ -1095,7 +1125,7 @@ export class ExcalidrawData { dirty = true; } } - } + }*/ return dirty; } @@ -1225,6 +1255,18 @@ export class ExcalidrawData { } } + public getLinkOpacity(): number { + const fileCache = this.app.metadataCache.getFileCache(this.file); + let opacity = this.plugin.settings.linkOpacity; + if ( + fileCache?.frontmatter && + fileCache.frontmatter[FRONTMATTER_KEY_LINKBUTTON_OPACITY] != null + ) { + opacity = fileCache.frontmatter[FRONTMATTER_KEY_LINKBUTTON_OPACITY]; + } + return opacity; + } + private setLinkPrefix(): boolean { const linkPrefix = this.linkPrefix; const fileCache = this.app.metadataCache.getFileCache(this.file); @@ -1425,11 +1467,17 @@ export const getTransclusion = async ( let startPos: number = null; let lineNum: number = 0; let endPos: number = null; + let depth:number = 1; for (let i = 0; i < headings.length; i++) { if (startPos && !endPos) { + let j = i; + while (jdepth) {j++}; + if(j === headings.length && headings[j-1].node.depth > depth) { + return { contents: "#".repeat(depth)+" "+contents.substring(startPos).trim(), lineNum }; + } endPos = headings[i].node.position.start.offset - 1; return { - contents: contents.substring(startPos, endPos).trim(), + contents: "#".repeat(depth)+" "+contents.substring(startPos, endPos).trim(), lineNum, }; } @@ -1446,11 +1494,12 @@ export const getTransclusion = async ( : false)) ) { startPos = headings[i].node.children[0]?.position.start.offset; // + depth = headings[i].node.depth; lineNum = headings[i].node.children[0]?.position.start.line; // } } if (startPos) { - return { contents: contents.substring(startPos).trim(), lineNum }; + return { contents: "#".repeat(depth)+" "+contents.substring(startPos).trim(), lineNum }; } return { contents: linkParts.original.trim(), lineNum: 0 }; }; diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts index 66fe869..763d6b4 100644 --- a/src/ExcalidrawView.ts +++ b/src/ExcalidrawView.ts @@ -41,7 +41,7 @@ import { LOCAL_PROTOCOL, } from "./Constants"; import ExcalidrawPlugin from "./main"; -import { repositionElementsToCursor } from "./ExcalidrawAutomate"; +import { repositionElementsToCursor, ExcalidrawAutomate } from "./ExcalidrawAutomate"; import { t } from "./lang/helpers"; import { ExcalidrawData, @@ -180,6 +180,7 @@ export default class ExcalidrawView extends TextFileView { public toolsPanelRef: React.MutableRefObject = null; private parentMoveObserver: MutationObserver; public linksAlwaysOpenInANewPane: boolean = false; //override the need for SHIFT+CTRL+click + public hookServer: ExcalidrawAutomate; public semaphores: { //The role of justLoaded is to capture the Excalidraw.onChange event that fires right after the canvas was loaded for the first time to @@ -242,6 +243,7 @@ export default class ExcalidrawView extends TextFileView { super(leaf); this.plugin = plugin; this.excalidrawData = new ExcalidrawData(plugin); + this.hookServer = plugin.ea; } preventAutozoom() { @@ -640,6 +642,19 @@ export default class ExcalidrawView extends TextFileView { return; } linkText = linkText.replaceAll("\n", ""); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/187 + + if(this.hookServer?.onLinkClickHook) { + const id = selectedText.id??selectedElementWithLink.id; + const el = this.excalidrawAPI.getSceneElements().filter((el:ExcalidrawElement)=>el.id === id)[0]; + try { + if(!this.hookServer.onLinkClickHook(el,linkText,ev)) { + return; + } + } catch (e) { + errorlog({where: "ExcalidrawView.handleLinkClick selectedText.id!==null", fn: this.hookServer.onLinkClickHook, error: e}); + } + } + if (linkText.match(REG_LINKINDEX_HYPERLINK)) { window.open(linkText, "_blank"); return; @@ -753,6 +768,18 @@ export default class ExcalidrawView extends TextFileView { return; } + if(this.hookServer?.onLinkClickHook) { + const id = selectedImage.id??selectedText.id??selectedElementWithLink.id; + const el = this.excalidrawAPI.getSceneElements().filter((el:ExcalidrawElement)=>el.id === id)[0]; + try { + if(!this.hookServer.onLinkClickHook(el,linkText,ev)) { + return; + } + } catch (e) { + errorlog({where: "ExcalidrawView.handleLinkClick selectedText.id===null", fn: this.hookServer.onLinkClickHook, error: e}); + } + } + try { if (ev.shiftKey && this.isFullscreen()) { this.exitFullscreen(); @@ -1233,7 +1260,7 @@ export default class ExcalidrawView extends TextFileView { if (this.nextLoader) { runLoader(this.nextLoader); } - }, + },0 ); }; if (!this.activeLoader) { @@ -1277,7 +1304,7 @@ export default class ExcalidrawView extends TextFileView { ...excalidrawData.appState, zenModeEnabled, viewModeEnabled, - linkOpacity: this.plugin.settings.linkOpacity, + linkOpacity: this.excalidrawData.getLinkOpacity(), trayModeEnabled: this.plugin.settings.defaultTrayMode, penMode: penEnabled, penDetected: penEnabled, @@ -1305,7 +1332,7 @@ export default class ExcalidrawView extends TextFileView { ...excalidrawData.appState, zenModeEnabled: om.zenModeEnabled, viewModeEnabled: om.viewModeEnabled, - linkOpacity: this.plugin.settings.linkOpacity, + linkOpacity: this.excalidrawData.getLinkOpacity(), trayModeEnabled: this.plugin.settings.defaultTrayMode, penMode: penEnabled, penDetected: penEnabled, @@ -1764,6 +1791,7 @@ export default class ExcalidrawView extends TextFileView { this.addText = async ( text: string, fontFamily?: 1 | 2 | 3 | 4, + save: boolean = true ): Promise => { const api = this.excalidrawAPI; if (!excalidrawRef?.current || !api) { @@ -1778,7 +1806,7 @@ export default class ExcalidrawView extends TextFileView { ea.style.fontSize = st.currentItemFontSize ?? 20; ea.style.textAlign = st.currentItemTextAlign ?? "left"; const id = ea.addText(currentPosition.x, currentPosition.y, text); - await this.addElements(ea.getElements(), false, true); + await this.addElements(ea.getElements(), false, save); return id; }; @@ -1997,6 +2025,7 @@ export default class ExcalidrawView extends TextFileView { }); this.handleLinkClick(this, event); selectedTextElement = null; + return; } selectedImageElement = getImageElementAtPointer(currentPosition, this); if (selectedImageElement && selectedImageElement.id) { @@ -2008,6 +2037,7 @@ export default class ExcalidrawView extends TextFileView { }); this.handleLinkClick(this, event); selectedImageElement = null; + return; } selectedElementWithLink = getElementWithLinkAtPointer(currentPosition, this); @@ -2020,18 +2050,21 @@ export default class ExcalidrawView extends TextFileView { }); this.handleLinkClick(this, event); selectedElementWithLink = null; + return; } }; let mouseEvent: any = null; - const showHoverPreview = (linktext?: string) => { + const showHoverPreview = (linktext?: string, element?: ExcalidrawElement) => { if (!linktext) { + if(!currentPosition) return; linktext = ""; const selectedElement = getTextElementAtPointer(currentPosition, this); if (!selectedElement || !selectedElement.text) { const selectedImgElement = getImageElementAtPointer(currentPosition, this); + element = this.excalidrawAPI.getSceneElements().filter((el:ExcalidrawElement)=>el.id === selectedImgElement.id)[0]; if (!selectedImgElement || !selectedImgElement.fileId) { return; } @@ -2046,6 +2079,7 @@ export default class ExcalidrawView extends TextFileView { this.excalidrawData.getFile(selectedImgElement.fileId).file.path + ref; } else { + element = this.excalidrawAPI.getSceneElements().filter((el:ExcalidrawElement)=>el.id === selectedElement.id)[0]; const text: string = this.textMode === TextMode.parsed ? this.excalidrawData.getRawText(selectedElement.id) @@ -2069,12 +2103,22 @@ export default class ExcalidrawView extends TextFileView { } } + if(this.hookServer?.onLinkHoverHook) { + try { + if(!this.hookServer.onLinkHoverHook(element,linktext)) { + return; + } + } catch (e) { + errorlog({where: "ExcalidrawView.showHoverPreview", fn: this.hookServer.onLinkHoverHook, error: e}); + } + } + if (this.semaphores.hoverSleep) { return; } const f = this.app.metadataCache.getFirstLinkpathDest( - linktext, + linktext.split("#")[0], this.file.path, ); if (!f) { @@ -2221,7 +2265,10 @@ export default class ExcalidrawView extends TextFileView { if (p.button === "up") { blockOnMouseButtonDown = false; } - if (this.plugin.ctrlKeyDown) { + if (this.plugin.ctrlKeyDown || + (this.excalidrawAPI.getAppState().isViewModeEnabled && + this.plugin.settings.hoverPreviewWithoutCTRL)) { + showHoverPreview(); } }, @@ -2305,11 +2352,11 @@ export default class ExcalidrawView extends TextFileView { files: TFile[], text: string, ): boolean => { - if (this.plugin.ea.onDropHook) { + if (this.hookServer.onDropHook) { try { - return this.plugin.ea.onDropHook({ + return this.hookServer.onDropHook({ //@ts-ignore - ea: this.plugin.ea, //the Excalidraw Automate object + ea: this.hookServer, //the ExcalidrawAutomate object event, //React.DragEvent draggable, //Obsidian draggable object type, //"file"|"text" @@ -2331,6 +2378,8 @@ export default class ExcalidrawView extends TextFileView { } }; + //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/468 + event[CTRL_OR_CMD] = event.shiftKey || event[CTRL_OR_CMD]; switch (draggable?.type) { case "file": if (!onDropHook("file", [draggable.file], null)) { @@ -2339,10 +2388,8 @@ export default class ExcalidrawView extends TextFileView { new Notice(t("FILENAME_INVALID_CHARS"), 4000); return false; } - //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/468 - event[CTRL_OR_CMD] = event.shiftKey || event[CTRL_OR_CMD]; if ( - event[CTRL_OR_CMD] && //.ctrlKey||event.metaKey) + event[CTRL_OR_CMD] && (IMAGE_TYPES.contains(draggable.file.extension) || draggable.file.extension === "md") ) { @@ -2371,16 +2418,38 @@ export default class ExcalidrawView extends TextFileView { return false; case "files": if (!onDropHook("file", draggable.files, null)) { - for (const f of draggable.files) { - this.addText( - `[[${this.app.metadataCache.fileToLinktext( - f, - this.file.path, - true, - )}]]`, - ); - currentPosition.y += st.currentItemFontSize * 2; - } + (async () => { + if (event[CTRL_OR_CMD]) { + const ea = this.plugin.ea; + ea.reset(); + ea.setView(this); + ea.canvas.theme = api.getAppState().theme; + let counter:number = 0; + for (const f of draggable.files) { + if ((IMAGE_TYPES.contains(f.extension) || f.extension === "md")) { + await ea.addImage( + currentPosition.x + counter*50, + currentPosition.y + counter*50, + f, + ); + counter++; + await ea.addElementsToView(false, false, true); + } + } + return; + } + for (const f of draggable.files) { + await this.addText( + `[[${this.app.metadataCache.fileToLinktext( + f, + this.file.path, + true, + )}]]`, undefined,false + ); + currentPosition.y += st.currentItemFontSize * 2; + } + this.save(false); + })(); } return false; } @@ -2418,6 +2487,22 @@ export default class ExcalidrawView extends TextFileView { })(); return false; } + //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/599 + if(text.startsWith("obsidian://open?vault=")) { + const html = event.dataTransfer.getData("text/html"); + if(html) { + const path = html.match(/href="app:\/\/obsidian\.md\/(.*?)"/); + if(path.length === 2) { + this.addText(`[[${decodeURIComponent(path[1])}]]`); + return false; + } + } + const path = text.split("file="); + if(path.length === 2) { + this.addText(`[[${decodeURIComponent(path[1])}]]`); + return false; + } + } this.addText(text.replace(/(!\[\[.*#[^\]]*\]\])/g, "$1{40}")); } return false; @@ -2527,6 +2612,15 @@ export default class ExcalidrawView extends TextFileView { return; } const event = e?.detail?.nativeEvent; + if(this.hookServer?.onLinkClickHook) { + try { + if(!this.hookServer.onLinkClickHook(element,element.link,event)) { + return; + } + } catch (e) { + errorlog({where: "ExcalidrawView.onLinkOpen", fn: this.hookServer.onLinkClickHook, error: e}); + } + } if (link.startsWith(LOCAL_PROTOCOL) || link.startsWith("[[")) { (async () => { const linkMatch = link.match(/(md:\/\/)?\[\[(?.*?)\]\]/); @@ -2617,10 +2711,7 @@ export default class ExcalidrawView extends TextFileView { return; } let linkText = linkMatch.groups.link; - if (linkText.search("#") > -1) { - linkText = linkText.substring(0, linkText.search("#")); - } - showHoverPreview(linkText); + showHoverPreview(linkText, element); } } }, @@ -2628,6 +2719,14 @@ export default class ExcalidrawView extends TextFileView { this.toolsPanelRef?.current?.setExcalidrawViewMode( isViewModeEnabled, ); + if(this.hookServer?.onViewModeChangeHook) { + try { + this.hookServer.onViewModeChangeHook(isViewModeEnabled); + } catch(e) { + errorlog({where: "ExcalidrawView.onViewModeChange", fn: this.hookServer.onViewModeChangeHook, error: e}); + } + + } }, }), React.createElement(ToolsPanel, { diff --git a/src/MarkdownPostProcessor.ts b/src/MarkdownPostProcessor.ts index 37bb7d8..0130db4 100644 --- a/src/MarkdownPostProcessor.ts +++ b/src/MarkdownPostProcessor.ts @@ -129,6 +129,7 @@ const getIMG = async ( null, [], plugin, + 0 )); if (!png) { return null; @@ -152,6 +153,7 @@ const getIMG = async ( null, [], plugin, + 0, getSVGPadding(plugin, file), ) ).outerHTML; diff --git a/src/constants.ts b/src/constants.ts index 5031dcf..6c87c70 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -30,6 +30,7 @@ export const FRONTMATTER_KEY_EXPORT_PNGSCALE = "excalidraw-export-pngscale"; export const FRONTMATTER_KEY_CUSTOM_PREFIX = "excalidraw-link-prefix"; export const FRONTMATTER_KEY_CUSTOM_URL_PREFIX = "excalidraw-url-prefix"; export const FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS = "excalidraw-link-brackets"; +export const FRONTMATTER_KEY_LINKBUTTON_OPACITY = "excalidraw-linkbutton-opacity"; export const FRONTMATTER_KEY_DEFAULT_MODE = "excalidraw-default-mode"; export const FRONTMATTER_KEY_FONT = "excalidraw-font"; export const FRONTMATTER_KEY_FONTCOLOR = "excalidraw-font-color"; diff --git a/src/dialogs/Messages.ts b/src/dialogs/Messages.ts index 5fe9d92..dc54b92 100644 --- a/src/dialogs/Messages.ts +++ b/src/dialogs/Messages.ts @@ -17,6 +17,24 @@ I develop this plugin as a hobby, spending most of my free time doing this. If y
`, +"1.6.26": ` +## Fixed +- Dragging multiple files onto the canvas will now correctly [#589](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/589) + - add multiple links + - or if you hold the CTRL/(SHIFT on Mac) while dropping the files, then adding multiple images +- Dropped images and links were not selectable with the selection tool until the file was saved. This is now fixed. +- Display the linked block/section on link-hover instead of the full page. [#597](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/597) +- Hover preview without CTRL/CMD works again. Requires configuration in plugin settings. [#595](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/595) +- If you embed the same markdown document into a drawing multiple times, you can now display different sections of the documents in each embedded object. [#601](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/601). +- If you make a copy of an equation and edit this copy, the original equation will remain unchanged [#593](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/593) + +## New Features +- When you drag files from dataview results onto the canvas the obsidian urls will be converted into wiki links.[#599](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/599) +- I added one more frontmatter key: ${String.fromCharCode(96)}excalidraw-linkbutton-opacity: 0.5${String.fromCharCode(96)}. This sets the opacity of the blue link-button in the top right corner of the element, overriding the respective setting in plugin settings. Valid values are numbers between 0 and 1, where 0 means the button is fully transparent. + +## New Excalidraw Automate Features +- As part of building the new ExcaliBrain plugin, I've added a number of integration features. See the GitHub [Release Notes](https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/1.6.26) for details. +`, "1.6.25": ` ## Fixed - Pinch-zoom in view mode was broken ([#5001](https://github.com/excalidraw/excalidraw/pull/5001)) diff --git a/src/dialogs/SuggesterInfo.ts b/src/dialogs/SuggesterInfo.ts index 9c49f7d..3f6a3f7 100644 --- a/src/dialogs/SuggesterInfo.ts +++ b/src/dialogs/SuggesterInfo.ts @@ -525,6 +525,13 @@ export const FRONTMATTER_KEYS_INFO: SuggesterInfo[] = [ desc: "Specifies how Excalidraw should open by default. Valid values are: view|zen", after: ": view", }, + { + field: "linkbutton-opacity", + code: null, + desc: "The opacity of the blue link button in the top right of the element overriding the respective setting in plugin settings. "+ + "Valid values are between 0 and 1, where 0 means the button is transparent.", + after: ": 0.5", + }, { field: "font", code: null, diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index f10e25e..09fd8b2 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -210,7 +210,9 @@ export default { }${FRONTMATTER_KEY_CUSTOM_URL_PREFIX}: "🌐 " to the file's frontmatter.`, HOVERPREVIEW_NAME: "Hover preview without CTRL/CMD key", HOVERPREVIEW_DESC: - "Toggle On: Hover preview for [[wiki links]] is shown immediately, without the need to hold the CTRL/CMD key.
Toggle Off: Hover preview is shown only when you hold the CTRL/CMD key while hovering the link.", + "Toggle On: In Exalidraw view mode the hover preview for [[wiki links]] will be shown immediately, without the need to hold the CTRL/CMD key. " + + "In Excalidraw normal mode, the preview will be shown immediately only when hovering the blue link icon in the top right of the element.
" + + "Toggle Off: Hover preview is shown only when you hold the CTRL/CMD key while hovering the link.", LINKOPACITY_NAME: "Opacity of link icon", LINKOPACITY_DESC: "Opacity of the link indicator icon in the top right corner of an element. 1 is opaque, 0 is transparent.", diff --git a/src/settings.ts b/src/settings.ts index 3ebd073..2d82c74 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -539,7 +539,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab { el.style.textAlign = "right"; el.innerText = ` ${this.plugin.settings.zoomToFitMaxLevel.toString()}`; }); - + this.containerEl.createEl("h1", { text: t("LINKS_HEAD") }); this.containerEl.createEl( "span", diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 0ca90bd..ba9455b 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -542,9 +542,7 @@ export const errorlog = (data: {}) => { console.error({ plugin: "Excalidraw", ...data }); }; -export const sleep = async (ms: number) => { - return new Promise((resolve) => setTimeout(resolve, ms)); -}; +export const sleep = async (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); export const log = console.log.bind(window.console); export const debug = console.log.bind(window.console); diff --git a/yarn.lock b/yarn.lock index 8b1cb5e..4d3566c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2215,10 +2215,10 @@ dependencies: "@zerollup/ts-helpers" "^1.7.18" -"@zsviczian/excalidraw@0.11.0-obsidian-11": - "integrity" "sha512-XlT8F7tQfKDXFUQWddGH+w7GTn5doWq3R0mTemuVqL0q5RhwiiNxHc1SNWmKMj/DVF0M3H5bjX/72gvWlRUDLQ==" - "resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.11.0-obsidian-11.tgz" - "version" "0.11.0-obsidian-11" +"@zsviczian/excalidraw@0.11.0-obsidian-16": + "integrity" "sha512-KVCWC7T31tXo6xfXY6AnGsDEl1j7BVwh3eSwoyn4MTS5UbhD5X0rwB8F6Yl1bdxKbrmnqQV98IKLsdgtfuHoVQ==" + "resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.11.0-obsidian-16.tgz" + "version" "0.11.0-obsidian-16" dependencies: "dotenv" "10.0.0"