diff --git a/ea-scripts/directory-info.json b/ea-scripts/directory-info.json index af72e20..076ee5c 100644 --- a/ea-scripts/directory-info.json +++ b/ea-scripts/directory-info.json @@ -1 +1 @@ -[{"fname":"Mindmap connector.md","mtime":1658686599427},{"fname":"Mindmap connector.svg","mtime":1658686599427},{"fname":"Add Connector Point.md","mtime":1645305706000},{"fname":"Add Connector Point.svg","mtime":1645944722000},{"fname":"Add Link to Existing File and Open.md","mtime":1647807918345},{"fname":"Add Link to Existing File and Open.svg","mtime":1645964261000},{"fname":"Add Link to New Page and Open.md","mtime":1654168862138},{"fname":"Add Link to New Page and Open.svg","mtime":1645960639000},{"fname":"Add Next Step in Process.md","mtime":1687192028831},{"fname":"Add Next Step in Process.svg","mtime":1645960639000},{"fname":"Box Each Selected Groups.md","mtime":1645305706000},{"fname":"Box Each Selected Groups.svg","mtime":1645967510000},{"fname":"Box Selected Elements.md","mtime":1645305706000},{"fname":"Box Selected Elements.svg","mtime":1645960639000},{"fname":"Change shape of selected elements.md","mtime":1652701169236},{"fname":"Change shape of selected elements.svg","mtime":1645960775000},{"fname":"Connect elements.md","mtime":1645305706000},{"fname":"Connect elements.svg","mtime":1645960639000},{"fname":"Convert freedraw to line.md","mtime":1645305706000},{"fname":"Convert freedraw to line.svg","mtime":1645960639000},{"fname":"Convert selected text elements to sticky notes.md","mtime":1670169501383},{"fname":"Convert selected text elements to sticky notes.svg","mtime":1645960639000},{"fname":"Convert text to link with folder and alias.md","mtime":1641639819000},{"fname":"Convert text to link with folder and alias.svg","mtime":1645960639000},{"fname":"Copy Selected Element Styles to Global.md","mtime":1642232088000},{"fname":"Copy Selected Element Styles to Global.svg","mtime":1645960639000},{"fname":"Create new markdown file and embed into active drawing.md","mtime":1640866935000},{"fname":"Create new markdown file and embed into active drawing.svg","mtime":1645960639000},{"fname":"Darken background color.md","mtime":1663059051059},{"fname":"Darken background color.svg","mtime":1645960639000},{"fname":"Elbow connectors.md","mtime":1671126911490},{"fname":"Elbow connectors.svg","mtime":1645960639000},{"fname":"Expand rectangles horizontally keep text centered.md","mtime":1646563692000},{"fname":"Expand rectangles horizontally keep text centered.svg","mtime":1645967510000},{"fname":"Expand rectangles horizontally.md","mtime":1644950235000},{"fname":"Expand rectangles horizontally.svg","mtime":1645967510000},{"fname":"Expand rectangles vertically keep text centered.md","mtime":1646563692000},{"fname":"Expand rectangles vertically keep text centered.svg","mtime":1645967510000},{"fname":"Expand rectangles vertically.md","mtime":1658686599427},{"fname":"Expand rectangles vertically.svg","mtime":1645967510000},{"fname":"Fixed horizontal distance between centers.md","mtime":1646743234000},{"fname":"Fixed horizontal distance between centers.svg","mtime":1645960639000},{"fname":"Fixed inner distance.md","mtime":1646743234000},{"fname":"Fixed inner distance.svg","mtime":1645960639000},{"fname":"Fixed spacing.md","mtime":1646743234000},{"fname":"Fixed spacing.svg","mtime":1645967510000},{"fname":"Fixed vertical distance between centers.md","mtime":1646743234000},{"fname":"Fixed vertical distance between centers.svg","mtime":1645967510000},{"fname":"Fixed vertical distance.md","mtime":1646743234000},{"fname":"Fixed vertical distance.svg","mtime":1645967510000},{"fname":"Lighten background color.md","mtime":1663059051059},{"fname":"Lighten background color.svg","mtime":1645959546000},{"fname":"Modify background color opacity.md","mtime":1644924415000},{"fname":"Modify background color opacity.svg","mtime":1645944722000},{"fname":"Normalize Selected Arrows.md","mtime":1670403743278},{"fname":"Normalize Selected Arrows.svg","mtime":1645960639000},{"fname":"Organic Line.md","mtime":1672920172531},{"fname":"Organic Line.svg","mtime":1645964261000},{"fname":"README.md","mtime":1645175700000},{"fname":"Repeat Elements.md","mtime":1663059051059},{"fname":"Repeat Elements.svg","mtime":1645960639000},{"fname":"Reverse arrows.md","mtime":1645305706000},{"fname":"Reverse arrows.svg","mtime":1645960639000},{"fname":"Scribble Helper.md","mtime":1682228345043},{"fname":"Scribble Helper.svg","mtime":1645944722000},{"fname":"Select Elements of Type.md","mtime":1643464321000},{"fname":"Select Elements of Type.svg","mtime":1645960639000},{"fname":"Set Dimensions.md","mtime":1645305706000},{"fname":"Set Dimensions.svg","mtime":1645944722000},{"fname":"Set Font Family.md","mtime":1645305706000},{"fname":"Set Font Family.svg","mtime":1645944722000},{"fname":"Set Grid.md","mtime":1674326971324},{"fname":"Set Grid.svg","mtime":1645960639000},{"fname":"Set Link Alias.md","mtime":1645305706000},{"fname":"Set Link Alias.svg","mtime":1645960639000},{"fname":"Set Stroke Width of Selected Elements.md","mtime":1645305706000},{"fname":"Set Stroke Width of Selected Elements.svg","mtime":1645960639000},{"fname":"Set Text Alignment.md","mtime":1645305706000},{"fname":"Set Text Alignment.svg","mtime":1645960639000},{"fname":"Set background color of unclosed line object by adding a shadow clone.md","mtime":1681665030892},{"fname":"Set background color of unclosed line object by adding a shadow clone.svg","mtime":1645960639000},{"fname":"Split text by lines.md","mtime":1645305706000},{"fname":"Split text by lines.svg","mtime":1645944722000},{"fname":"Zoom to Fit Selected Elements.md","mtime":1640770602000},{"fname":"Zoom to Fit Selected Elements.svg","mtime":1645960639000},{"fname":"directory-info.json","mtime":1646583437000},{"fname":"index-new.md","mtime":1645986149000},{"fname":"index.md","mtime":1645175700000},{"fname":"Grid Selected Images.md","mtime":1649614401982},{"fname":"Grid Selected Images.svg","mtime":1649614401982},{"fname":"Palette loader.md","mtime":1686511890942},{"fname":"Palette loader.svg","mtime":1649614401982},{"fname":"Rename Image.md","mtime":1663678478785},{"fname":"Rename Image.svg","mtime":1663678478785},{"fname":"Text Arch.md","mtime":1664095143846},{"fname":"Text Arch.svg","mtime":1670403743278},{"fname":"Deconstruct selected elements into new drawing.md","mtime":1672672112439},{"fname":"Deconstruct selected elements into new drawing.svg","mtime":1668541145255},{"fname":"Slideshow.md","mtime":1685276730052},{"fname":"Slideshow.svg","mtime":1670017348333},{"fname":"Auto Layout.md","mtime":1670403743278},{"fname":"Auto Layout.svg","mtime":1670175947081},{"fname":"Uniform size.md","mtime":1670175947081},{"fname":"Uniform size.svg","mtime":1670175947081},{"fname":"Alternative Pens.md", "mtime":1672942234020},{"fname":"Alternative Pens.svg", "mtime":1672942234020},{"fname":"Mindmap format.md","mtime":1684484694228},{"fname":"Mindmap format.svg","mtime":1674944958059},{"fname":"Text to Sticky Notes.md","mtime":1678537561724},{"fname":"Text to Sticky Notes.svg","mtime":1678537561724},{"fname":"Folder Note Core - Make Current Drawing a Folder.md","mtime":1678973697470},{"fname":"Folder Note Core - Make Current Drawing a Folder.svg","mtime":1678973697470},{"fname":"Invert colors.md","mtime":1678973697470},{"fname":"Invert colors.svg","mtime":1678973697470},{"fname":"Auto Draw for Pen.md","mtime":1680418321236},{"fname":"Auto Draw for Pen.svg","mtime":1680418321236},{"fname":"Hardware Eraser Support.md","mtime":1680418321236},{"fname":"Hardware Eraser Support.svg","mtime":1680418321236},{"fname":"PDF Page Text to Clipboard.md","mtime":1683984041712},{"fname":"PDF Page Text to Clipboard.svg","mtime":1680418321236},{"fname":"Excalidraw Collaboration Frame.md","mtime":1687881495985},{"fname":"Excalidraw Collaboration Frame.svg","mtime":1687881495985},{"fname":"Create DrawIO file.md","mtime":1688243858267},{"fname":"Create DrawIO file.svg","mtime":1688243858267}] +[{"fname":"Mindmap connector.md","mtime":1658686599427},{"fname":"Mindmap connector.svg","mtime":1658686599427},{"fname":"Add Connector Point.md","mtime":1645305706000},{"fname":"Add Connector Point.svg","mtime":1645944722000},{"fname":"Add Link to Existing File and Open.md","mtime":1647807918345},{"fname":"Add Link to Existing File and Open.svg","mtime":1645964261000},{"fname":"Add Link to New Page and Open.md","mtime":1654168862138},{"fname":"Add Link to New Page and Open.svg","mtime":1645960639000},{"fname":"Add Next Step in Process.md","mtime":1688304760357},{"fname":"Add Next Step in Process.svg","mtime":1645960639000},{"fname":"Box Each Selected Groups.md","mtime":1645305706000},{"fname":"Box Each Selected Groups.svg","mtime":1645967510000},{"fname":"Box Selected Elements.md","mtime":1645305706000},{"fname":"Box Selected Elements.svg","mtime":1645960639000},{"fname":"Change shape of selected elements.md","mtime":1652701169236},{"fname":"Change shape of selected elements.svg","mtime":1645960775000},{"fname":"Connect elements.md","mtime":1645305706000},{"fname":"Connect elements.svg","mtime":1645960639000},{"fname":"Convert freedraw to line.md","mtime":1645305706000},{"fname":"Convert freedraw to line.svg","mtime":1645960639000},{"fname":"Convert selected text elements to sticky notes.md","mtime":1670169501383},{"fname":"Convert selected text elements to sticky notes.svg","mtime":1645960639000},{"fname":"Convert text to link with folder and alias.md","mtime":1641639819000},{"fname":"Convert text to link with folder and alias.svg","mtime":1645960639000},{"fname":"Copy Selected Element Styles to Global.md","mtime":1642232088000},{"fname":"Copy Selected Element Styles to Global.svg","mtime":1645960639000},{"fname":"Create new markdown file and embed into active drawing.md","mtime":1640866935000},{"fname":"Create new markdown file and embed into active drawing.svg","mtime":1645960639000},{"fname":"Darken background color.md","mtime":1663059051059},{"fname":"Darken background color.svg","mtime":1645960639000},{"fname":"Elbow connectors.md","mtime":1671126911490},{"fname":"Elbow connectors.svg","mtime":1645960639000},{"fname":"Expand rectangles horizontally keep text centered.md","mtime":1646563692000},{"fname":"Expand rectangles horizontally keep text centered.svg","mtime":1645967510000},{"fname":"Expand rectangles horizontally.md","mtime":1644950235000},{"fname":"Expand rectangles horizontally.svg","mtime":1645967510000},{"fname":"Expand rectangles vertically keep text centered.md","mtime":1646563692000},{"fname":"Expand rectangles vertically keep text centered.svg","mtime":1645967510000},{"fname":"Expand rectangles vertically.md","mtime":1658686599427},{"fname":"Expand rectangles vertically.svg","mtime":1645967510000},{"fname":"Fixed horizontal distance between centers.md","mtime":1646743234000},{"fname":"Fixed horizontal distance between centers.svg","mtime":1645960639000},{"fname":"Fixed inner distance.md","mtime":1646743234000},{"fname":"Fixed inner distance.svg","mtime":1645960639000},{"fname":"Fixed spacing.md","mtime":1646743234000},{"fname":"Fixed spacing.svg","mtime":1645967510000},{"fname":"Fixed vertical distance between centers.md","mtime":1646743234000},{"fname":"Fixed vertical distance between centers.svg","mtime":1645967510000},{"fname":"Fixed vertical distance.md","mtime":1646743234000},{"fname":"Fixed vertical distance.svg","mtime":1645967510000},{"fname":"Lighten background color.md","mtime":1663059051059},{"fname":"Lighten background color.svg","mtime":1645959546000},{"fname":"Modify background color opacity.md","mtime":1644924415000},{"fname":"Modify background color opacity.svg","mtime":1645944722000},{"fname":"Normalize Selected Arrows.md","mtime":1670403743278},{"fname":"Normalize Selected Arrows.svg","mtime":1645960639000},{"fname":"Organic Line.md","mtime":1672920172531},{"fname":"Organic Line.svg","mtime":1645964261000},{"fname":"README.md","mtime":1645175700000},{"fname":"Repeat Elements.md","mtime":1663059051059},{"fname":"Repeat Elements.svg","mtime":1645960639000},{"fname":"Reverse arrows.md","mtime":1645305706000},{"fname":"Reverse arrows.svg","mtime":1645960639000},{"fname":"Scribble Helper.md","mtime":1682228345043},{"fname":"Scribble Helper.svg","mtime":1645944722000},{"fname":"Select Elements of Type.md","mtime":1643464321000},{"fname":"Select Elements of Type.svg","mtime":1645960639000},{"fname":"Set Dimensions.md","mtime":1645305706000},{"fname":"Set Dimensions.svg","mtime":1645944722000},{"fname":"Set Font Family.md","mtime":1645305706000},{"fname":"Set Font Family.svg","mtime":1645944722000},{"fname":"Set Grid.md","mtime":1674326971324},{"fname":"Set Grid.svg","mtime":1645960639000},{"fname":"Set Link Alias.md","mtime":1645305706000},{"fname":"Set Link Alias.svg","mtime":1645960639000},{"fname":"Set Stroke Width of Selected Elements.md","mtime":1645305706000},{"fname":"Set Stroke Width of Selected Elements.svg","mtime":1645960639000},{"fname":"Set Text Alignment.md","mtime":1645305706000},{"fname":"Set Text Alignment.svg","mtime":1645960639000},{"fname":"Set background color of unclosed line object by adding a shadow clone.md","mtime":1681665030892},{"fname":"Set background color of unclosed line object by adding a shadow clone.svg","mtime":1645960639000},{"fname":"Split text by lines.md","mtime":1645305706000},{"fname":"Split text by lines.svg","mtime":1645944722000},{"fname":"Zoom to Fit Selected Elements.md","mtime":1640770602000},{"fname":"Zoom to Fit Selected Elements.svg","mtime":1645960639000},{"fname":"directory-info.json","mtime":1646583437000},{"fname":"index-new.md","mtime":1645986149000},{"fname":"index.md","mtime":1645175700000},{"fname":"Grid Selected Images.md","mtime":1649614401982},{"fname":"Grid Selected Images.svg","mtime":1649614401982},{"fname":"Palette loader.md","mtime":1686511890942},{"fname":"Palette loader.svg","mtime":1649614401982},{"fname":"Rename Image.md","mtime":1663678478785},{"fname":"Rename Image.svg","mtime":1663678478785},{"fname":"Text Arch.md","mtime":1664095143846},{"fname":"Text Arch.svg","mtime":1670403743278},{"fname":"Deconstruct selected elements into new drawing.md","mtime":1672672112439},{"fname":"Deconstruct selected elements into new drawing.svg","mtime":1668541145255},{"fname":"Slideshow.md","mtime":1685276730052},{"fname":"Slideshow.svg","mtime":1670017348333},{"fname":"Auto Layout.md","mtime":1670403743278},{"fname":"Auto Layout.svg","mtime":1670175947081},{"fname":"Uniform size.md","mtime":1670175947081},{"fname":"Uniform size.svg","mtime":1670175947081},{"fname":"Alternative Pens.md", "mtime":1672942234020},{"fname":"Alternative Pens.svg", "mtime":1672942234020},{"fname":"Mindmap format.md","mtime":1684484694228},{"fname":"Mindmap format.svg","mtime":1674944958059},{"fname":"Text to Sticky Notes.md","mtime":1678537561724},{"fname":"Text to Sticky Notes.svg","mtime":1678537561724},{"fname":"Folder Note Core - Make Current Drawing a Folder.md","mtime":1678973697470},{"fname":"Folder Note Core - Make Current Drawing a Folder.svg","mtime":1678973697470},{"fname":"Invert colors.md","mtime":1678973697470},{"fname":"Invert colors.svg","mtime":1678973697470},{"fname":"Auto Draw for Pen.md","mtime":1680418321236},{"fname":"Auto Draw for Pen.svg","mtime":1680418321236},{"fname":"Hardware Eraser Support.md","mtime":1680418321236},{"fname":"Hardware Eraser Support.svg","mtime":1680418321236},{"fname":"PDF Page Text to Clipboard.md","mtime":1683984041712},{"fname":"PDF Page Text to Clipboard.svg","mtime":1680418321236},{"fname":"Excalidraw Collaboration Frame.md","mtime":1687881495985},{"fname":"Excalidraw Collaboration Frame.svg","mtime":1687881495985},{"fname":"Create DrawIO file.md","mtime":1688243858267},{"fname":"Create DrawIO file.svg","mtime":1688243858267}] diff --git a/manifest.json b/manifest.json index 89e9414..326bf3d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-excalidraw-plugin", "name": "Excalidraw", - "version": "1.9.6", + "version": "1.9.7", "minAppVersion": "1.1.6", "description": "An Obsidian plugin to edit and view Excalidraw drawings", "author": "Zsolt Viczian", diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts index 4f02108..23b333b 100644 --- a/src/ExcalidrawAutomate.ts +++ b/src/ExcalidrawAutomate.ts @@ -1878,6 +1878,17 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { return elements.filter(el=>el.groupIds.some(id=>element.groupIds.includes(id))); } + /** + * Gets all the elements from elements[] that are contained in the frame. + * @param element + * @param elements - typically all the non-deleted elements in the scene + * @returns + */ + getElementsInFrame(frameElement: ExcalidrawElement, elements: ExcalidrawElement[]): ExcalidrawElement[] { + if(!frameElement || !elements || frameElement.type !== "frame") return []; + return elements.filter(el=>el.frameId === frameElement.id); + } + /** * @param element * @param a @@ -2315,6 +2326,13 @@ async function getTemplate( groupElements = plugin.ea.getElementsInTheSameGroupWithElement(el[0],scene.elements) } } + if(filenameParts.hasFrameref) { + const el = scene.elements.filter((el: ExcalidrawElement)=>el.id===filenameParts.blockref) + if(el.length === 1) { + groupElements = plugin.ea.getElementsInFrame(el[0],scene.elements) + } + } + if(filenameParts.hasTaskbone) { groupElements = groupElements.filter( el => @@ -2446,7 +2464,7 @@ export async function createSVG( ); const filenameParts = getEmbeddedFilenameParts(templatePath); if( - !filenameParts.hasGroupref && + !(filenameParts.hasGroupref || filenameParts.hasFrameref) && (filenameParts.hasBlockref || filenameParts.hasSectionref) ) { let el = filenameParts.hasSectionref @@ -2568,7 +2586,7 @@ export const search = async (view: ExcalidrawView) => { const ea = view.plugin.ea; ea.reset(); ea.setView(view); - const elements = ea.getViewElements().filter((el) => el.type === "text"); + const elements = ea.getViewElements().filter((el) => el.type === "text" || el.type === "frame"); if (elements.length === 0) { return; } @@ -2627,6 +2645,38 @@ export const getTextElementsMatchingQuery = ( })); } +/** + * + * @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 getFrameElementsMatchingQuery = ( + 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 === "frame" && + query.some((q) => { + if (exactMatch) { + const text = el.name.toLowerCase().split("\n")[0].trim(); + const m = text.match(/^#*(# .*)/); + if (!m || m.length !== 2) { + return false; + } + return m[1] === q.toLowerCase(); + } + const text = el.name.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 + })); +} + export const cloneElement = (el: ExcalidrawElement):any => { return { ...el, diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts index d514a7c..c718231 100644 --- a/src/ExcalidrawView.ts +++ b/src/ExcalidrawView.ts @@ -48,7 +48,13 @@ import { EXPORT_IMG_ICON_NAME, } from "./Constants"; import ExcalidrawPlugin from "./main"; -import { repositionElementsToCursor, ExcalidrawAutomate, getTextElementsMatchingQuery, cloneElement } from "./ExcalidrawAutomate"; +import { + repositionElementsToCursor, + ExcalidrawAutomate, + getTextElementsMatchingQuery, + cloneElement, + getFrameElementsMatchingQuery +} from "./ExcalidrawAutomate"; import { t } from "./lang/helpers"; import { ExcalidrawData, @@ -90,7 +96,7 @@ import { } from "./utils/Utils"; import { getLeaf, getParentOfClass } from "./utils/ObsidianUtils"; import { splitFolderAndFilename } from "./utils/FileUtils"; -import { NewFileActions, Prompt } from "./dialogs/Prompt"; +import { ConfirmationPrompt, NewFileActions, Prompt } from "./dialogs/Prompt"; import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard"; import { updateEquation } from "./LaTeX"; import { @@ -105,16 +111,14 @@ import { ToolsPanel } from "./menu/ToolsPanel"; import { ScriptEngine } from "./Scripts"; import { getTextElementAtPointer, getImageElementAtPointer, getElementWithLinkAtPointer } from "./utils/GetElementAtPointer"; import { ICONS, saveIcon } from "./menu/ActionIcons"; -//import { MainMenu } from "@zsviczian/excalidraw"; -//import {WelcomeScreen} from "@zsviczian/excalidraw"; import { ExportDialog } from "./dialogs/ExportDialog"; import { getEA } from "src"; import { emulateCTRLClickForLinks, externalDragModifierType, internalDragModifierType, isALT, isCTRL, isMETA, isSHIFT, linkClickModifierType, mdPropModifier, ModifierKeys } from "./utils/ModifierkeyHelper"; import { setDynamicStyle } from "./utils/DynamicStyling"; -import { MenuLinks } from "./menu/MenuLinks"; import { InsertPDFModal } from "./dialogs/InsertPDFModal"; import { CustomIFrame, renderWebView, useDefaultExcalidrawFrame } from "./customIFrame"; import { insertIFrameToView, insertImageToView } from "./utils/ExcalidrawViewUtils"; +import { imageCache } from "./utils/ImageCache"; declare const PLUGIN_VERSION:string; @@ -231,6 +235,7 @@ export default class ExcalidrawView extends TextFileView { public linksAlwaysOpenInANewPane: boolean = false; //override the need for SHIFT+CTRL+click (used by ExcaliBrain) private hookServer: ExcalidrawAutomate; public lastSaveTimestamp: number = 0; //used to validate if incoming file should sync with open file + private lastLoadedFile: TFile = null; private onKeyUp: (e: KeyboardEvent) => void; private onKeyDown:(e: KeyboardEvent) => void; //store key state for view mode link resolution @@ -299,7 +304,6 @@ export default class ExcalidrawView extends TextFileView { private linkAction_Element: HTMLElement; public compatibilityMode: boolean = false; private obsidianMenu: ObsidianMenu; - private menuLinks: MenuLinks; //https://stackoverflow.com/questions/27132796/is-there-any-javascript-event-fired-when-the-on-screen-keyboard-on-mobile-safari private isEditingTextResetTimer: NodeJS.Timeout = null; @@ -618,6 +622,11 @@ export default class ExcalidrawView extends TextFileView { this.semaphores.preventReload = preventReload; await super.save(); + //saving to backup with a delay in case application closes in the meantime, I want to avoid both save and backup corrupted. + const path = this.file.path; + //@ts-ignore + const data = this.lastSavedData; + setTimeout(()=>imageCache.addBAKToCache(path,data),50); triggerReload = (this.lastSaveTimestamp === this.file.stat.mtime) && !preventReload && forcesave; this.lastSaveTimestamp = this.file.stat.mtime; @@ -671,18 +680,20 @@ export default class ExcalidrawView extends TextFileView { // get the new file content // if drawing is in Text Element Edit Lock, then everything should be parsed and in sync // if drawing is in Text Element Edit Unlock, then everything is raw and parse and so an async function is not required here + getViewData() { - //debug({where:"getViewData",semaphores:this.semaphores}); - if (!this.getScene) { + if (!this.getScene || !this.excalidrawData.loaded) { return this.data; } - if (!this.excalidrawData.loaded) { + + const scene = this.getScene(); + if(!scene) { return this.data; } + //include deleted elements in save in case saving in markdown mode //deleted elements are only used if sync modifies files while Excalidraw is open //otherwise deleted elements are discarded when loading the scene - const scene = this.getScene(); if (!this.compatibilityMode) { let trimLocation = this.data.search(/(^%%\n)?# Text Elements\n/m); if (trimLocation == -1) { @@ -703,7 +714,11 @@ export default class ExcalidrawView extends TextFileView { : [ [FRONTMATTER_KEY, this.textMode === TextMode.raw ? "raw" : "parsed"] ]; - if(this.exportDialog?.dirty) this.exportDialog.dirty = false; + + if(this.exportDialog?.dirty) { + this.exportDialog.dirty = false; + } + let header = updateFrontmatterInString(this.data.substring(0, trimLocation),keys); //this should be removed at a later time. Left it here to remediate 1.4.9 mistake const REG_IMG = /(^---[\w\W]*?---\n)(!\[\[.*?]]\n(%%\n)?)/m; //(%%\n)? because of 1.4.8-beta... to be backward compatible with anyone who installed that version @@ -715,15 +730,16 @@ export default class ExcalidrawView extends TextFileView { this.excalidrawData.disableCompression = this.isEditedAsMarkdownInOtherView(); } - const reuslt = header + this.excalidrawData.generateMD( + const result = header + this.excalidrawData.generateMD( this.excalidrawAPI.getSceneElementsIncludingDeleted().filter((el:ExcalidrawElement)=>el.isDeleted) //will be concatenated to scene.elements ); this.excalidrawData.disableCompression = false; - return reuslt; + return result; } if (this.compatibilityMode) { return JSON.stringify(scene, null, "\t"); } + return this.data; } @@ -1339,6 +1355,7 @@ export default class ExcalidrawView extends TextFileView { this.blockTextModeChange = false; } + public autosaveFunction: Function; public setupAutosaveTimer() { const timer = async () => { if(!this.isLoaded) { @@ -1390,6 +1407,8 @@ export default class ExcalidrawView extends TextFileView { ); } }; + + this.autosaveFunction = timer; if (this.autosaveTimer) { clearTimeout(this.autosaveTimer); this.autosaveTimer = null; @@ -1446,6 +1465,7 @@ export default class ExcalidrawView extends TextFileView { return; } if (this.semaphores.saving) return; + this.lastLoadedFile = null; this.diskIcon.querySelector("svg").removeClass("excalidraw-dirty"); if (this.compatibilityMode) { this.clearDirty(); @@ -1604,6 +1624,10 @@ export default class ExcalidrawView extends TextFileView { public isLoaded: boolean = false; async setViewData(data: string, clear: boolean = false) { + //I am using last loaded file to control when the view reloads. + //It seems text file view gets the modified file event after sync before the modifyEventHandler in main.ts + //reload can only be triggered via reload() + if(this.lastLoadedFile === this.file) return; this.isLoaded = false; if(!this.file) return; if(this.plugin.settings.showNewVersionNotification) checkExcalidrawVersion(app); @@ -1611,6 +1635,7 @@ export default class ExcalidrawView extends TextFileView { this.clear(); } this.lastSaveTimestamp = this.file.stat.mtime; + this.lastLoadedFile = this.file; data = this.data = data.replaceAll("\r\n", "\n").replaceAll("\r", "\n"); app.workspace.onLayoutReady(async () => { this.compatibilityMode = this.file.extension === "excalidraw"; @@ -1642,14 +1667,41 @@ export default class ExcalidrawView extends TextFileView { } } catch (e) { errorlog({ where: "ExcalidrawView.setViewData", error: e }); - new Notice( - `Error loading drawing:\n${e.message}${ - e.message === "Cannot read property 'index' of undefined" - ? "\n'# Drawing' section is likely missing" - : "" - }\n\nTry manually fixing the file or restoring an earlier version from sync history.`, - 10000, - ); + const file = this.file; + const plugin = this.plugin; + const leaf = this.leaf; + (async () => { + let confirmation:boolean = true; + while (!imageCache.isReady() && confirmation) { + imageCache.initializationNotice = true; + const confirmationPrompt = new ConfirmationPrompt(plugin,t("CACHE_NOT_READY")); + confirmation = await confirmationPrompt.waitForClose + } + + const drawingBAK = await imageCache.getBAKFromCache(file.path); + if (!drawingBAK) { + new Notice( + `Error loading drawing:\n${e.message}${ + e.message === "Cannot read property 'index' of undefined" + ? "\n'# Drawing' section is likely missing" + : "" + }\n\nTry manually fixing the file or restoring an earlier version from sync history.`, + 10000, + ); + return; + } + const confirmationPrompt = new ConfirmationPrompt(plugin,t("BACKUP_AVAILABLE")); + confirmationPrompt.waitForClose.then(async (confirmed) => { + if (confirmed) { + await app.vault.modify(file, drawingBAK); + //@ts-ignore + plugin.excalidrawFileModes[leaf.id || file.path] = VIEW_TYPE_EXCALIDRAW; + plugin.setExcalidrawView(leaf); + } + }); + + + })(); this.setMarkdownView(); return; } @@ -1758,7 +1810,7 @@ export default class ExcalidrawView extends TextFileView { try { const deletedIds = inData.deletedElements.map(el=>el.id); - const sceneElements = this.excalidrawAPI.getSceneElements() + const sceneElements = this.excalidrawAPI.getSceneElementsIncludingDeleted() //remove deleted elements .filter((el: ExcalidrawElement)=>!deletedIds.contains(el.id)); const sceneElementIds = sceneElements.map((el:ExcalidrawElement)=>el.id); @@ -2230,7 +2282,6 @@ export default class ExcalidrawView extends TextFileView { const reactElement = React.createElement(() => { const excalidrawWrapperRef = React.useRef(null); const toolsPanelRef = React.useRef(null); - const menuLinksRef = React.useRef(null); const [dimensions, setDimensions] = React.useState({ width: undefined, @@ -2246,7 +2297,6 @@ export default class ExcalidrawView extends TextFileView { this.toolsPanelRef = toolsPanelRef; this.obsidianMenu = new ObsidianMenu(this.plugin, toolsPanelRef, this); - this.menuLinks = new MenuLinks(this.plugin, menuLinksRef); //excalidrawRef readypromise based on //https://codesandbox.io/s/eexcalidraw-resolvable-promise-d0qg3?file=/src/App.js:167-760 @@ -4062,7 +4112,11 @@ export default class ExcalidrawView extends TextFileView { elements.filter((el: ExcalidrawElement) => el.type === "text"), query, exactMatch - ); + ).concat(getFrameElementsMatchingQuery( + elements.filter((el: ExcalidrawElement) => el.type === "frame"), + query, + exactMatch + )); if (match.length === 0) { new Notice("I could not find a matching text element"); @@ -4157,6 +4211,51 @@ export default class ExcalidrawView extends TextFileView { : this.plugin.ea.getLargestElement(elements).id; } + const isFrame = elements.some(el=>el.id === elementId && el.type==="frame"); + + let buttons = []; + if(isFrame) { + switch(prefix) { + case "area=": + case "group=": + case "frame=": + buttons = [ + {caption: "Frame", action:()=>{prefix="frame="; return;}}, + {caption: "Link", action:()=>{prefix="";return}}, + ]; + break; + default: + buttons = [ + {caption: "Link", action:()=>{prefix="";return}}, + {caption: "Frame", action:()=>{prefix="frame="; return;}}, + ] + } + + } else { + switch(prefix) { + case "area=": + buttons = [ + {caption: "Area", action:()=>{prefix="area="; return;}}, + {caption: "Link", action:()=>{prefix="";return}}, + {caption: "Group", action:()=>{prefix="group="; return;}}, + ]; + break; + case "group=": + buttons = [ + {caption: "Group", action:()=>{prefix="group="; return;}}, + {caption: "Link", action:()=>{prefix="";return}}, + {caption: "Area", action:()=>{prefix="area="; return;}}, + ]; + break; + default: + buttons = [ + {caption: "Link", action:()=>{prefix="";return}}, + {caption: "Area", action:()=>{prefix="area="; return;}}, + {caption: "Group", action:()=>{prefix="group="; return;}}, + ] + } + } + const alias = await ScriptEngine.inputPrompt( this, this.plugin, @@ -4164,11 +4263,7 @@ export default class ExcalidrawView extends TextFileView { "Set link alias", "Leave empty if you do not want to set an alias", "", - [ - {caption: "Link", action:()=>{prefix="";return}}, - {caption: "Area", action:()=>{prefix="area="; return;}}, - {caption: "Group", action:()=>{prefix="group="; return;}} - ] + buttons, ); navigator.clipboard.writeText( `${prefix.length>0?"!":""}[[${this.file.path}#^${prefix}${elementId}${alias ? `|${alias}` : ``}]]`, diff --git a/src/MarkdownPostProcessor.ts b/src/MarkdownPostProcessor.ts index 19c8ba5..8e220eb 100644 --- a/src/MarkdownPostProcessor.ts +++ b/src/MarkdownPostProcessor.ts @@ -134,7 +134,7 @@ const getIMG = async ( } } - const quickPNG = !filenameParts.hasGroupref + const quickPNG = !(filenameParts.hasGroupref || filenameParts.hasFrameref) ? await getQuickImagePreview(plugin, file.path, "png") : undefined; @@ -142,7 +142,7 @@ const getIMG = async ( const png = quickPNG ?? (await createPNG( - filenameParts.hasGroupref + (filenameParts.hasGroupref || filenameParts.hasFrameref) ? filenameParts.filepath + filenameParts.linkpartReference : file.path, scale, @@ -183,7 +183,7 @@ const getIMG = async ( } const svgSnapshot = ( await createSVG( - filenameParts.hasGroupref || filenameParts.hasBlockref || filenameParts.hasSectionref + filenameParts.hasGroupref || filenameParts.hasBlockref || filenameParts.hasSectionref || filenameParts.hasFrameref ? filenameParts.filepath + filenameParts.linkpartReference : file.path, true, @@ -394,7 +394,7 @@ const isTextOnlyEmbed = (internalEmbedEl: Element):boolean => { const src = internalEmbedEl.getAttribute("src"); if(!src) return true; //technically this does not mean this is a text only embed, but still should abort further processing const fnameParts = getEmbeddedFilenameParts(src); - return !(fnameParts.hasArearef || fnameParts.hasGroupref) && + return !(fnameParts.hasArearef || fnameParts.hasGroupref || fnameParts.hasFrameref) && (fnameParts.hasBlockref || fnameParts.hasSectionref) } diff --git a/src/dialogs/Messages.ts b/src/dialogs/Messages.ts index 79cf75b..2a6871a 100644 --- a/src/dialogs/Messages.ts +++ b/src/dialogs/Messages.ts @@ -17,6 +17,37 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
`, +"1.9.7":` +## Fixed: + +- Fixed an issue where using the color picker shortcut would cause the UI to disappear in mobile view mode. +- You can now add YouTube playlists to iframes. +- Fixed a bug where the "Add any file" dropdown suggester opened in the main Obsidian workspace instead of the popout window when Excalidraw was running. ([#1179](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1191)) +- Made some improvements to the logic of opening in the adjacent pane, although it is still not perfect. +- Fixed an issue where Obsidian sync would result in the loss of the last approximately 20 seconds of work. Excalidraw's handling of sync is now fixed. ([#1189](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1189)) + +## New: + +- Introducing Image Cache: Excalidraw will now cache rendered images embedded in Markdown documents, which will enhance the markdown rendering experience. +- Backup Cache: Excalidraw now stores a backup on your device when saving, in case the application is terminated during a save operation. If you are using sync, you can find the latest backup on the device you last used to edit your drawing. +- Added ${String.fromCharCode(96)}frame=${String.fromCharCode(96)} parameter to image references. ([#1194](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1194)) For more details about this feature, check out this [YouTube video](https://youtu.be/yZQoJg2RCKI). +- When an SVG image from Draw.io is embedded in Excalidraw, clicking the image will open the file in the [Diagram plugin](https://github.com/zapthedingbat/drawio-obsidian) (if available). +- Added the [Create DrawIO file](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Create%20DrawIO%20file.md) Excalidraw Automate Script to the library, which allows you to create a new draw.io drawing and add it to the current Excalidraw canvas. + +## New in ExcalidrawAutomate + +${String.fromCharCode(96,96,96)}typescript +async getAttachmentFilepath(filename: string): PromiseCONVERT FILES now to convert all of your *.excalidraw files, or if you prefer to make a backup first, then click CANCEL.Excalidraw: Convert *.excalidraw files to *.excalidraw.md files*.excalidraw file in File Explorer and select one of the following options to convert files one by one: *.excalidraw => *.excalidraw.md*.excalidraw => *.md (Logseq compatibility). This option will retain the original *.excalidraw file next to the new Obsidian format. " +
- "Make sure you also enable Compatibility features in Settings for a full solution.Convert to new format from the Options Menu