diff --git a/ea-scripts/Convert freedraw to line.md b/ea-scripts/Convert freedraw to line.md index ac04f69..add8244 100644 --- a/ea-scripts/Convert freedraw to line.md +++ b/ea-scripts/Convert freedraw to line.md @@ -24,7 +24,8 @@ if(!settings["Point density"]) { ea.setScriptSettings(settings); } -const setSize = parseInt(settings["Point density"].value[0]); +const scale = settings["Point density"].value; +const setSize = parseInt(scale.substring(0,scale.indexOf(":"))); const elements = ea.getViewSelectedElements().filter(el=>el.type==="freedraw"); if(elements.length === 0) { @@ -59,4 +60,5 @@ elements.forEach((el)=>{ }); ea.deleteViewElements(elements); -ea.addElementsToView(false,true,true); \ No newline at end of file +await ea.addElementsToView(false,true,true); +ea.selectElementsInView(ea.getElements()); \ No newline at end of file diff --git a/ea-scripts/Set background color of unclosed line object by adding a shadow clone.md b/ea-scripts/Set background color of unclosed line object by adding a shadow clone.md index 65f3c27..773c863 100644 --- a/ea-scripts/Set background color of unclosed line object by adding a shadow clone.md +++ b/ea-scripts/Set background color of unclosed line object by adding a shadow clone.md @@ -6,7 +6,7 @@ Use this script to set the background color of unclosed line objects by creating ```javascript */ -if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.24")) { +if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.26")) { new Notice("This script requires a newer version of Excalidraw. Please install the latest version."); return; } @@ -47,6 +47,7 @@ if(elements.length === 0) { } ea.copyViewElementsToEAforEditing(elements); +elementsToMove = []; elements.forEach((el)=>{ const newEl = ea.cloneElement(el); @@ -64,6 +65,16 @@ elements.forEach((el)=>{ ]); newEl.points.push([0,0]); if(shouldGroup) ea.addToGroup([el.id,newEl.id]); + elementsToMove.push({fillId: newEl.id, shapeId: el.id}); }); -ea.addElementsToView(); \ No newline at end of file +await ea.addElementsToView(); +elementsToMove.forEach((x)=>{ + const viewElements = ea.getViewElements(); + ea.moveViewElementToZIndex( + x.fillId, + viewElements.indexOf(viewElements.filter(el=>el.id === x.shapeId)[0])-1 + ) +}); + +ea.selectElementsInView(ea.getElements()); \ No newline at end of file diff --git a/manifest.json b/manifest.json index e4d9afe..2a8e6b5 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-excalidraw-plugin", "name": "Excalidraw", - "version": "1.5.25", + "version": "1.5.26", "minAppVersion": "0.12.16", "description": "An Obsidian plugin to edit and view Excalidraw drawings", "author": "Zsolt Viczian", diff --git a/package.json b/package.json index f8a28ae..646febc 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "author": "", "license": "MIT", "dependencies": { - "@zsviczian/excalidraw": "0.10.0-obsidian-37", + "@zsviczian/excalidraw": "0.10.0-obsidian-39", "monkey-around": "^2.3.0", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts index 829c673..4059c51 100644 --- a/src/ExcalidrawAutomate.ts +++ b/src/ExcalidrawAutomate.ts @@ -233,6 +233,7 @@ export interface ExcalidrawAutomate { selectElementsInView(elements: ExcalidrawElement[]): void; //sets selection in view generateElementId(): string; //returns an 8 character long random id cloneElement(element: ExcalidrawElement): ExcalidrawElement; //Returns a clone of the element with a new id + moveViewElementToZIndex(elementId:number, newZIndex:number): void; //Moves the element to a specific position in the z-index } declare let window: any; @@ -1307,17 +1308,14 @@ export async function initExcalidrawAutomate( }, selectElementsInView(elements: ExcalidrawElement[]):void { if (!this.targetView || !this.targetView?._loaded) { - errorMessage("targetView not set", "getViewSelectedElements()"); + errorMessage("targetView not set", "selectElementsInView()"); return; } if (!elements || elements.length===0) { return; } const API = this.getExcalidrawAPI(); - let st = API.getAppState(); - st.selectedElementIds = {}; - elements.forEach(el=>st.selectedElementIds[el.id] = true); - API.updateScene({appState: st}); + API.selectElements(elements); }, generateElementId(): string { return nanoid(); @@ -1326,7 +1324,35 @@ export async function initExcalidrawAutomate( const newEl = JSON.parse(JSON.stringify(element)); newEl.id = nanoid(); return newEl; - } + }, + moveViewElementToZIndex(elementId:number, newZIndex:number): void { + if (!this.targetView || !this.targetView?._loaded) { + errorMessage("targetView not set", "moveViewElementToZIndex()"); + return; + } + const API = this.getExcalidrawAPI(); + const elements = this.getViewElements(); + const elementToMove = elements.filter((el:any)=>el.id===elementId); + if (elementToMove.length === 0) { + errorMessage(`Element (id: ${elementId}) not found`, "moveViewElementToZIndex"); + return; + } + if (newZIndex >= elements.length) { + API.bringToFront(elementToMove); + return; + } + if (newZIndex < 0) { + API.sendToBack(elementToMove); + return; + } + + const oldZIndex = elements.indexOf(elementToMove[0]); + elements.splice(newZIndex, 0, elements.splice(oldZIndex, 1)[0]); + API.updateScene({ + elements: elements, + commitToHistory: true, + }); + } }; await initFonts(); return window.ExcalidrawAutomate; diff --git a/src/SuggestorInfo.ts b/src/SuggestorInfo.ts index 52062fb..27fe7fe 100644 --- a/src/SuggestorInfo.ts +++ b/src/SuggestorInfo.ts @@ -4,8 +4,7 @@ type SuggestorInfo = { field: string, code: string, desc: string, - after: string, - alt: boolean + after: string } export const EXCALIDRAW_AUTOMATE_INFO:SuggestorInfo[] = [ @@ -14,504 +13,438 @@ export const EXCALIDRAW_AUTOMATE_INFO:SuggestorInfo[] = [ code: null, desc: "The ExcalidrawPlugin object", after: "", - alt: true, }, { field: "elementsDict", code: null, desc: "The {} dictionary object, contains the ExcalidrawElements currently edited in Automate indexed by el.id", after: '[""]', - alt: true, }, { field: "imagesDict", code: null, desc: "the images files including DataURL, indexed by fileId", after: '[""]', - alt: true, }, { field: "style.strokeColor", code: "[string]", desc: "A valid css color. See W3 School Colors for more.", after: "", - alt: true, }, { field: "style.backgroundColor", code: "[string]", desc: "A valid css color. See W3 School Colors for more.", after: "", - alt: true, }, { field: "style.angle", code: "[number]", desc: "Rotation of the object in radian", after: "", - alt: true, }, { field: "style.fillStyle", code: "[string]", desc: "'hachure' | 'cross-hatch' | 'solid'", after: "", - alt: true, }, { field: "style.strokeWidth", code: "[number]", desc: null, after: "", - alt: true, }, { field: "style.strokeStyle", code: "[string]", desc: "'solid' | 'dashed' | 'dotted'", after: "", - alt: true, }, { field: "style.roughness", code: "[number]", desc: "0:Architect\n1:Artist\n2:Cartoonist", after: "", - alt: true, }, { field: "style.opacity", code: "[number]", desc: "100: Fully opaque\n0: Fully transparent", after: "", - alt: true, }, { field: "style.strokeSharpness", code: "[string]", desc: "'round' | 'sharp'", after: "", - alt: true, }, { field: "style.fontFamily", code: "[number]", desc: "1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont", after: "", - alt: true, }, { field: "style.fontSize", code: "[number]", desc: null, after: "", - alt: true, }, { field: "style.textAlign", code: "[string]", desc: "'left' | 'right' | 'center'", after: "", - alt: true, }, { field: "style.verticalAlign", code: "[string]", desc: "For future use, has no effect currently; 'top' | 'bottom' | 'middle'", after: "", - alt: true, }, { field: "style.startArrowHead", code: "[string]", desc: "'triangle' | 'dot' | 'arrow' | 'bar' | null", after: "", - alt: true, }, { field: "style.endArrowHead", code: "[string]", desc: "'triangle' | 'dot' | 'arrow' | 'bar' | null", after: "", - alt: true, }, { field: "canvas.theme", code: "[string]", desc: "'dark' | 'light'", after: "", - alt: true, }, { field: "canvas.viewBackgroundColor", code: "[string]", desc: "A valid css color.\nSee W3 School Colors for more.", after: "", - alt: true, }, { field: "canvas.gridSize", code: "[number]", desc: null, after: "", - alt: true, }, { field: "addToGroup", code: "addToGroup(objectIds: []): string;", desc: null, after: "", - alt: true, }, { field: "toCliboard", code: "toClipboard(templatePath?: string): void;", desc: "Copies current elements using template to clipboard, ready to be pasted into an excalidraw canvas", after: "", - alt: true, }, { field: "getElements", code: "getElements(): ExcalidrawElement[];", desc: "Get all elements from ExcalidrawAutomate elementsDict", after: "", - alt: true, }, { field: "getElement", code: "getElement(id: string): ExcalidrawElement;", desc: "Get single element from ExcalidrawAutomate elementsDict", after: "", - alt: true, }, { field: "create", code: 'create(params?: {filename?: string, foldername?: string, templatePath?: string, onNewPane?: boolean, frontmatterKeys?: { "excalidraw-plugin"?: "raw" | "parsed", "excalidraw-link-prefix"?: string, "excalidraw-link-brackets"?: boolean, "excalidraw-url-prefix"?: string,},}): Promise;', desc: 'Create a drawing and save it to filename.\nIf filename is null: default filename as defined in Excalidraw settings.\nIf folder is null: default folder as defined in Excalidraw settings\n', after: "", - alt: true, }, { field: "createSVG", code: "createSVG(templatePath?: string, embedFont?: boolean, exportSettings?: ExportSettings, loader?: EmbeddedFilesLoader, theme?: string,): Promise;", desc: "Use ExcalidrawAutomate.getExportSettings(boolean,boolean) to create an ExportSettings object.\nUse ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?) to create an EmbeddedFilesLoader object.", after: "", - alt: true, }, { field: "createPNG", code: "createPNG(templatePath?: string, scale?: number, exportSettings?: ExportSettings, loader?: EmbeddedFilesLoader, theme?: string,): Promise;", desc: "Use ExcalidrawAutomate.getExportSettings(boolean,boolean) to create an ExportSettings object.\nUse ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?) to create an EmbeddedFilesLoader object.", after: "", - alt: true, }, { field: "wrapText", code: "wrapText(text: string, lineLen: number): string;", desc: null, after: "", - alt: true, }, { field: "addRect", code: "addRect(topX: number, topY: number, width: number, height: number): string;", desc: null, after: "", - alt: true, }, { field: "addDiamond", code: "addDiamond(topX: number, topY: number, width: number, height: number): string;", desc: null, after: "", - alt: true, }, { field: "addEllipse", code: "addEllipse(topX: number, topY: number, width: number, height: number): string;", desc: null, after: "", - alt: true, }, { field: "addBlob", code: "addBlob(topX: number, topY: number, width: number, height: number): string;", desc: null, after: "", - alt: true, }, { field: "addText", code: 'addText(topX: number, topY: number, text: string, formatting?: {wrapAt?: number; width?: number; height?: number; textAlign?: string; box?: boolean | "box" | "blob" | "ellipse" | "diamond"; boxPadding?: number;}, id?: string,): string;', desc: 'If box is !null, then text will be boxed\nThe function returns the id of the TextElement. If the text element is boxed i.e. it is a sticky note, then the id of the container object', after: "", - alt: true, }, { field: "addLine", code: "addLine(points: [[x: number, y: number]]): string;", desc: null, after: "", - alt: true, }, { field: "addArrow", code: "addArrow(points: [[x: number, y: number]], formatting?: { startArrowHead?: string; endArrowHead?: string; startObjectId?: string; endObjectId?: string;},): string;", desc: null, after: "", - alt: true, }, { field: "addImage", code: "addImage(topX: number, topY: number, imageFile: TFile): Promise;", desc: null, after: "", - alt: true, }, { field: "addLaTex", code: "addLaTex(topX: number, topY: number, tex: string): Promise;", desc: null, after: "", - alt: true, }, { field: "connectObjects", code: 'connectObjects(objectA: string, connectionA: ConnectionPoint, objectB: string, connectionB: ConnectionPoint, formatting?: {numberOfPoints?: number; startArrowHead?: string; endArrowHead?: string; padding?: number;},): void;', desc: 'type ConnectionPoint = "top" | "bottom" | "left" | "right" | null\nWhen null is passed as ConnectionPoint then Excalidraw will automatically decide\nnumberOfPoints is the number of points on the line. Default is 0 i.e. line will only have a start and end point.\nArrowHead: "triangle"|"dot"|"arrow"|"bar"|null', after: "", - alt: true, }, { field: "clear", code: "clear(): void;", desc: "Clears elementsDict and imagesDict only", after: "", - alt: true, }, { field: "reset", code: "reset(): void;", desc: "clear() + reset all style values to default", after: "", - alt: true, }, { field: "isExcalidrawFile", code: "isExcalidrawFile(f: TFile): boolean;", desc: "Returns true if MD file is an Excalidraw file", after: "", - alt: true, }, { field: "targetView", code: "targetView: ExcalidrawView;", desc: "The Obsidian view currently edited", after: "", - alt: true, }, { field: "setView", code: 'setView(view: ExcalidrawView | "first" | "active"): ExcalidrawView;', desc: null, after: "", - alt: true, }, { field: "getExcalidrawAPI", code: "getExcalidrawAPI(): any;", desc: "Excalidraw API", after: "", - alt: true, }, { field: "getViewElements", code: "getViewElements(): ExcalidrawElement[];", desc: "Get elements in View", after: "", - alt: true, }, { field: "deleteViewElements", code: "deleteViewElements(el: ExcalidrawElement[]): boolean;", desc: null, after: "", - alt: true, }, { field: "getViewSelectedElement", code: "getViewSelectedElement(): ExcalidrawElement;", desc: "Get the selected element in the view, if more are selected, get the first", after: "", - alt: true, }, { field: "getViewSelectedElements", code: "getViewSelectedElements(): ExcalidrawElement[];", desc: null, after: "", - alt: true, }, { field: "getViewFileForImageElement", code: "getViewFileForImageElement(el: ExcalidrawElement): TFile | null;", desc: "Returns the TFile file handle for the image element", after: "", - alt: true, }, { field: "copyViewElementsToEAforEditing", code: "copyViewElementsToEAforEditing(elements: ExcalidrawElement[]): void;", desc: "Copies elements from view to elementsDict for editing", after: "", - alt: true, }, { field: "viewToggleFullScreen", code: "viewToggleFullScreen(forceViewMode?: boolean): void;", desc: null, after: "", - alt: true, }, { field: "connectObjectWithViewSelectedElement", code: "connectObjectWithViewSelectedElement(objectA: string, connectionA: ConnectionPoint, connectionB: ConnectionPoint, formatting?: {numberOfPoints?: number; startArrowHead?: string; endArrowHead?: string; padding?: number;},): boolean;", desc: "Connect an object to the selected element in the view\nSee tooltip for connectObjects for details", after: "", - alt: true, }, { field: "addElementsToView", code: "addElementsToView(repositionToCursor?: boolean, save?: boolean, newElementsOnTop?: boolean,): Promise;", desc: "Adds elements from elementsDict to the current view\nrepositionToCursor: default is false\nsave: default is true\nnewElementsOnTop: default is false, i.e. the new elements get to the bottom of the stack\nnewElementsOnTop controls whether elements created with ExcalidrawAutomate are added at the bottom of the stack or the top of the stack of elements already in the view\nNote that elements copied to the view with copyViewElementsToEAforEditing retain their position in the stack of elements in the view even if modified using EA", after: "", - alt: true, }, { field: "onDropHook", code: 'onDropHook(data: {ea: ExcalidrawAutomate, event: React.DragEvent, draggable: any, type: "file" | "text" | "unknown", payload: {files: TFile[], text: string,}, excalidrawFile: TFile, view: ExcalidrawView, pointerPosition: { x: number, y: number},}): boolean;', desc: 'If set Excalidraw will call this function onDrop events.\nA return of true will stop the default onDrop processing in Excalidraw.\n\ndraggable is the Obsidian draggable object\nfiles is the array of dropped files\nexcalidrawFile is the file receiving the drop event\nview is the excalidraw view receiving the drop.\npointerPosition is the pointer position on canvas at the time of drop.', after: "", - alt: true, }, { field: "mostRecentMarkdownSVG", code: "mostRecentMarkdownSVG: SVGSVGElement;", desc: "Markdown renderer will drop a copy of the most recent SVG here for debugging purposes", after: "", - alt: true, }, { field: "getEmbeddedFilesLoader", code: "getEmbeddedFilesLoader(isDark?: boolean): EmbeddedFilesLoader;", desc: "Utility function to generate EmbeddedFilesLoader object", after: "", - alt: true, }, { field: "getExportSettings", code: "getExportSettings(withBackground: boolean, withTheme: boolean,): ExportSettings;", desc: "Utility function to generate ExportSettings object", after: "", - alt: true, }, { field: "getBoundingBox", code: "getBoundingBox(elements: ExcalidrawElement[]): {topX: number, topY: number, width: number, height: number,};", desc: "Gets the bounding box of elements. The bounding box is the box encapsulating all of the elements completely.", after: "", - alt: true, }, { field: "getMaximumGroups", code: "getMaximumGroups(elements: ExcalidrawElement[]): ExcalidrawElement[][];", desc: "Elements grouped by the highest level groups", after: "", - alt: true, }, { field: "getLargestElement", code: "getLargestElement(elements: ExcalidrawElement[]): ExcalidrawElement;", desc: "Gets the largest element from a group. useful when a text element is grouped with a box, and you want to connect an arrow to the box", after: "", - alt: true, }, { field: "intersectElementWithLine", code: "intersectElementWithLine(element: ExcalidrawBindableElement, a: readonly [number, number], b: readonly [number, number], gap?: number,): Point[];", desc: "If gap is given, the element is inflated by this value.\nReturns 2 or 0 intersection points between line going through `a` and `b` and the `element`, in ascending order of distance from `a`.", after: "", - alt: true, }, { field: "getLargestElement", code: "getLargestElement(elements: ExcalidrawElement[]): ExcalidrawElement;", desc: "Gets the largest element from a group. useful when a text element is grouped with a box, and you want to connect an arrow to the box", after: "", - alt: true, }, { field: "activeScript", code: "activeScript: string;", desc: "Mandatory to set before calling the get and set ScriptSettings functions. Set automatically by the ScriptEngine\nSee for more details: Script Engine Help", after: "", - alt: true, }, { field: "getScriptSettings", code: "getScriptSettings(): {};", desc: "Returns script settings. Saves settings in plugin settings, under the activeScript key. See for more details: Script Engine Help", after: "", - alt: true, }, { field: "setScriptSettings", code: "setScriptSettings(settings: any): Promise;", desc: "Sets script settings.\nSee for more details: Script Engine Help", after: "", - alt: true, }, { field: "openFileInNewOrAdjacentLeaf", code: "openFileInNewOrAdjacentLeaf(file: TFile): WorkspaceLeaf;", desc: "Open a file in a new workspaceleaf or reuse an existing adjacent leaf depending on Excalidraw Plugin Settings", after: "", - alt: true, }, { field: "measureText", code: "measureText(text: string): { width: number; height: number };", desc: "Measures text size based on current style settings", after: "", - alt: true, }, { field: "verifyMinimumPluginVersion", code: 'verifyMinimumPluginVersion(requiredVersion: string): boolean;', desc: 'Returns true if plugin version is >= than required\nrecommended use:\nif(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.20")) {new Notice("message");return;}', after: "", - alt: true, }, { field: "selectElementsInView", code: "selectElementsInView(elements: ExcalidrawElement[]):void;", desc: "Elements provided will be set as selected in the targetView.", after: "", - alt: true, }, { field: "generateElementId", code: "generateElementId(): string;", desc: "Returns an 8 character long random id", after: "", - alt: true, }, { field: "cloneElement", code: "cloneElement(element: ExcalidrawElement): ExcalidrawElement;", desc: "Returns a clone of the element with a new element id", after: "", - alt: true, + }, + { + field: "moveViewElementToZIndex", + code: "moveViewElementToZIndex(elementId:number, newZIndex:number): void;", + desc: "Moves the element to a specific position in the z-index", + after: "", }, ]; @@ -521,14 +454,12 @@ export const EXCALIDRAW_SCRIPTENGINE_INFO:SuggestorInfo[] = [ code: "inputPrompt: (header: string, placeholder?: string, value?: string);", desc: "Opens a prompt that asks for an input.\nReturns a string with the input.\nYou need to await the result of inputPrompt.", after: "", - alt: true, }, { field: "suggester", code: "suggester: (displayItems: string[], items: any[], hint?: string, instructions?:Instruction[]);", desc: "Opens a suggester. Displays the displayItems and returns the corresponding item from items[]\nYou need to await the result of suggester.\nIf the user cancels (ESC), suggester will return undefined\nHint and instructions are optional\n\ninterface Instruction {command: string;purpose: string;}", after: "", - alt: true, }, ]; @@ -538,55 +469,47 @@ export const FRONTMATTER_KEYS_INFO:SuggestorInfo[] = [ code: null, desc: "Denotes an excalidraw file. If key is not present, the file will not be recognized as an Excalidarw file. Valid values are 'parsed' and 'raw'", after: ": parsed", - alt: true, }, { field: FRONTMATTER_KEY_CUSTOM_PREFIX, code: null, desc: "Set custom prefix to denote text element containing a valid internal link. Set to empty string if you do not want to show a prefix", after: ': "📍"', - alt: true, }, { field: FRONTMATTER_KEY_CUSTOM_URL_PREFIX, code: null, desc: "Set custom prefix to denote text element containing a valid external link. Set to empty string if you do not want to show a prefix", after: ': "🌐"', - alt: true, }, { field: FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS, code: null, desc: "Set to true, if you want to display [[square brackets]] around the links in Text Elements", after: ": true", - alt: true, }, { field: FRONTMATTER_KEY_DEFAULT_MODE, code: null, desc: "Specifies how Excalidraw should open by default. Valid values are: view|zen", after: ": view", - alt: true, }, { field: FRONTMATTER_KEY_FONT, code: null, desc: "This key applies to Markdown Embeds. You can control the appearance of the embedded markdown file on a file by file bases by adding the this front matter keys to your markdown document. Valid values are: Virgil|Cascadia|font_file_name.extension", after: ": Virgil", - alt: true, }, { field: FRONTMATTER_KEY_FONTCOLOR, code: null, desc: "This key applies to Markdown Embeds. You can control the appearance of the embedded markdown file on a file by file bases by adding the this front matter keys to your markdown document. Valid values are: css-color-name|#HEXcolor|any-other-html-standard-format", after: ": SteelBlue", - alt: true, }, { field: FRONTMATTER_KEY_MD_STYLE, code: null, desc: 'This key applies to Markdown Embeds. You can control the appearance of the embedded markdown file on a file by file bases by adding the this front matter keys to your markdown document. Valid values are: "css-filename|css snippet"', after: ': ""', - alt: true, }, ]; \ No newline at end of file diff --git a/versions.json b/versions.json index 52b4367..f21cfe9 100644 --- a/versions.json +++ b/versions.json @@ -1,4 +1,4 @@ { - "1.5.25": "0.12.16", + "1.5.26": "0.12.16", "1.4.2": "0.11.13" } diff --git a/yarn.lock b/yarn.lock index 15309c3..de4071b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2122,10 +2122,10 @@ "resolved" "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" "version" "4.2.2" -"@zsviczian/excalidraw@0.10.0-obsidian-37": - "integrity" "sha512-FssxK/xkDzsltu81aMTLkWVd0Te9EMV7H74K6GINF2M688rTk1Up5DGKIwI1fX8Zl+OobpmEBErctLLsQmb5jQ==" - "resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.10.0-obsidian-37.tgz" - "version" "0.10.0-obsidian-37" +"@zsviczian/excalidraw@0.10.0-obsidian-39": + "integrity" "sha512-8uUby+Wzt1lVGqG6231IKWssd1uL+ERBSqscjjI/15bIDrqdatwJIm4taSHjNjiasOTo8p/OjG/s2Aulkr4uug==" + "resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.10.0-obsidian-39.tgz" + "version" "0.10.0-obsidian-39" dependencies: "dotenv" "10.0.0"